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)