Search code examples
rubymiddleman

Ruby Limit / Take / First - limit sometimes or ignore


I have the following method:

Now sometimes I want the limit attribute to be ignored

Therefore the .take method would be ignored, at the moment I do not know how to do this gracefully. As setting to nil errors the code.

Any help appreciated - new to ruby.

def articlesByCategory( category, extensions = [ "md" ], limit = 3 )

    # Check category is an array
    if !category.kind_of?(Array)
        category = [ category ]
    end

    # Create Return Array
    ret = []

    # Get the resources that are
    sitemap.resources.select { |r| ( category & Array( r.data.category ) ).present? }.take( limit ).each do |a|

        ret << a

    end

    # Return
    ret

end

Solution

  • First, your code can be significantly refactored to do the same thing it’s doing now. There’s no need to do an each to build an exact copy of the array, and we can apply Kernel#Array to category the same way your did to r.data.category. Finally, any? reads a bit better (IMO) than present?, especially since the value cannot be nil (only caveat is if nil or false is a valid category).

    def articles_by_category category, limit = 3
      category = Array(category)
      sitemap.resources.select do |resource|
        (Array(resource.data.category) & category).any?
      end.take(limit)
    end
    

    We can easily pull the take out into a conditional to get what you want:

    def articles_by_category category, limit = 3
      category = Array(category)
      articles = sitemap.resources.select do |resource|
        (Array(resource.data.category) & category).any?
      end
      limit ? articles.take(limit) : articles
    end
    

    It might, however, make sense to just get rid of the limit entirely within the method and impose it externally. This is much more functional and keeps your method from doing a lot of things (what does a limit have to do with getting articles by category? (this method doesn’t even get articles by category, it gets whatever resources is (presumably articles…) for a given category)).

    def articles_by_category category
      category = Array(category)
      sitemap.resources.select do |resource|
        (Array(resource.data.category) & category).any?
      end
    end
    
    articles_by_category('My Category').take(3)
    

    Note that if category will never be an array (which seems likely given its singular name) than you can further simplify your method to:

    def articles_by_category category
      sitemap.resources.select do |resource|
        resource.data.category == category
      end
    end
    

    (And of course add the limit feature back in if so desired.)