arraysrubyrefactoringsplat

How to pass array as individual arguments in ruby?


I am trying to think of a more elegant way to pass elements of an array as individual arguments to a method.

So far, I have the following code:

def fetch_data(keywords)
  Product.where("name ILIKE ? AND type ILIKE ?", *construct_keywords(keywords))
end

def construct_keywords(keywords)
  keywords.map{ |keyword| "%#{keyword}%" }
end

This works just fine, and I know that the splat operator is doing a good job but I want to see if instead of using that ugly asterisk, it is possible to reshape the return value of construct_keywords in a way so that the fetch_data method will recognize the array elements as individual arguments. So ultimately I want it to be like:

def fetch_data(keywords)
  Product.where("name ILIKE ? AND type ILIKE ?", construct_keywords(keywords))
end

def construct_keywords(keywords)
  keywords.map{ |keyword| "%#{keyword}%" }
  # reshape above array in a way so that fetch_data will recognize each elements as arguments
end

Thank you in advance.


Solution

  • Although that might be convenient, there's no way to directly inform how your return value will be used and have it "pre-splatted". That's something the method being called has to implement.

    You might want to explore using keyword-based placeholders, like name ILIKE :name where you can then create a hash that matches.

    Right now you pass in an arbitrary number of keywords to a method that returns an arbitrary-sized result array, which then binds to a query with precisely two places. That seems like it's prone to failure.

    Using [ :name, :type ].zip(keywords.map { ... }).to_h gives you something that can slot into that, of course, presuming you have two keyword args.

    Although it might take some refactoring, a form that's really hard to foul up looks like this:

    def fetch_data(name:, type:)
      Product.where("name ILIKE :name AND type ILIKE :type", name: name, type: type)
    end
    

    Where those keyword arguments must be specified, and there can only be two.