[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Help me understand *args

Steve L

4/21/2006 4:53:00 AM

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


4 Answers

rickhg12hs

4/21/2006 5:29:00 AM

0

Rather than "puts whatever", have a look at "p whatever". I think
you'll see that args and *args are different.

David Sletten

4/21/2006 5:50:00 AM

0

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

Robert Klemme

4/21/2006 7:26:00 AM

0

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

In this case the simplest solution is to use "super" without any args.

def self.find(*args)
super
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

puts is not good to see what's going on because it does some magic with
array arguments. See this:

>> def t(*a) p a end
=> nil
>> t 1
[1]
=> nil
>> t 1,2
[1, 2]
=> nil
>> t [1,2]
[[1, 2]]
=> nil
>> t *[1,2]
[1, 2]
=> nil

The star kind of flattens the array. If you omit the star the method
sees only one argument (last but one example) although that is an array.

Kind regards

robert

Steve L

4/21/2006 8:11:00 AM

0

That was an awesome explanation! Thanks so much!
- Janak

"David Sletten" <david@slytobias.com> wrote in message
news:0o_1g.9078$3W1.4118@tornado.socal.rr.com...
>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