Wednesday’s post on acts_as_locateable didn’t do much to explain what the patch to the plugin’s methods was doing to allow us to pass extra arguments to ActiveRecord#find. The secret is in the *, or array argument.
A normal method will have a fixed number of arguments:
def simple_method(first, second, third)
puts "#{first} : #{second} : #{third}"
end
simple_method('one', 'two', 'three')
>> one : two : three
and sometimes we can develop that by allowing default values for those arguments:
def simple_method(first, second, third = 'four')
puts "#{first} : #{second} : #{third}"
end
simple_method('one', 'two')
>> one : two : four
but sometimes we want to allow an arbitrary number of arguments, and that’s where * (the Array Argument) comes in.
def simple_method(first, *others)
puts "#{first} : " + others.join(' : ')
end
simple_method('one', 'two', 'three')
>> one : two : three
If you pass in keyed parameters then you will get a hash within the others array:
def simple_method(*others)
puts others.inspect
end
simple_method('one', :two => 'three')
>> ["one", {:two=>"three"}]
So in the case of acts_as_locateable, the find_within_radius can take an arbitrary number of parameters to allow for different types of input:
def find_within_radius(radius_in_miles, *args)
All we needed to do to allow in more parameters was to extract our extra arguments which I knew would be grouped together in a Hash, so that the rest of the internals would still get the input they’re expecting:
# standard_args will be the args input minus any arguments of type Hash
# (our addition to the input)
standard_args = args.reject { |arg| arg.kind_of?(Hash) }
# query_args will then be the first Hash found in the arguments
query_args = (args - standard_args).first
and then we can pass our query_args hash to the find call later on:
find(:all, query_args)