David Sletten
4/21/2006 5:50:00 AM
J wrote:
> Hello,
>
> I was trying to overload the find method of an activerecord model when I
> came across some behavior that I didn't understand.
>
> The model name is 'permission'
>
> When I do this, it works...
>
> def self.find(*args)
> super *args
> end
>
> When I do this, it doesn't work
>
> def self.find(*args)
> super args
> end
>
> I get the following error
> ActiveRecord::RecordNotFound: Couldn't find Permission with ID=all
>
> I always tested my method using
> Permission.find(:all)
>
> I don't understand what is going on, since the following statements are
> showing me that args and *args are equal??
>
> puts *args
> puts args
> puts *args.class.to_s
> puts args.class.to_s
> puts args.first.class.to_s
> puts args.first.class.to_s
>
> - A confused yannick
>
>
You are exploring the mechanism that Ruby provides to allow you to write
methods that deal with an arbitrary number of arguments. Basic method
definitions create methods that only accept a fixed number of args:
def f(x)
x + 2
end
Now we can call f with 1 arg:
irb(main):711:0> f(8)
=> 10
But not 2:
irb(main):712:0> f(8, 9)
ArgumentError: wrong number of arguments (2 for 1)
from (irb):712:in `f'
from (irb):712
from :0
If instead we want a method that can handle an unspecified number of
arguments we use the *args mechanism:
def all_even?(*numbers)
numbers.all? {|n| n % 2 == 0}
end
Ruby packages all of the arguments we give the method into the array
'numbers', which we can manipulate like any other array.
Now we can call this method with any number of args:
irb(main):716:0> all_even?(2)
=> true
irb(main):717:0> all_even?(2, 4, 6, 8)
=> true
irb(main):718:0> all_even?(2, 4, 6, 8, 7)
=> false
irb(main):719:0> all_even?()
=> true
As you can see, Ruby bundles multiple arguments into a single object.
Sometimes we need to work in the other direction too. We want to unpack
a collection into multiple values. We use a similar syntax to do this:
irb(main):724:0> all_even?([2, 4, 6, 8])
NoMethodError: undefined method `%' for [2, 4, 6, 8]:Array
from (irb):714:in `all_even?'
from (irb):714:in `all?'
from (irb):714:in `each'
from (irb):714:in `all?'
from (irb):714:in `all_even?'
from (irb):724
from :0
irb(main):725:0> all_even?(*[2, 4, 6, 8])
=> true
Here the '*' unpacks the array before invoking all_even?.
In your case, your 'find' method gets its args packaged into an array,
but the superclass method is not expecting a single array. So you have
to unpack them again. Here you are using both aspects of Ruby's '*' syntax.
Aloha,
David Sletten