Forum: Ruby using dynamic method calling with attr_writer methods

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
1151dc5752afb29c75025406f0ffd7de?d=identicon&s=25 Markus Hohenhaus (daisagah)
on 2007-05-09 21:23
I'm a total Ruby newbie looking for help.

I'm writing an application where I need to dynamical assign object
variables via attr_writer object methods.

My code is something like this:

   class Test < ParentClass
       attr_reader :one, :two, :three
       attr_writer :one, :two, :three

       def initialize
          super
       end

   end
( I know I could use attr_accessor but for testing I'll stick to
attr_writer and _reader)

Now I try to call the 'Test' methods dynamical witch 'object.send' from
within my main application:

   [...]

   @methods = ['one', 'two', 'three']

   test = Test.new

   @methods.each { |a| test.send(a, 'foo') }

   [...]

This throws the error: ArgumentError: wrong number of Arguments (1 for
0)

As far as I understand, this is the correct behaviour. Because without
the second argument, the method call returns the value of the class
variable. So I believe, that this should work if i'll write all the
setter and getter methods myself (which I don't want to, because I'm
using a lot of variables).
 Are there any other methods I can use to dynamically call attr_writer
methods to assign values to the class variables or did I get something
wrong and made a mistake?
D150490744fbd0a10e9e62fabb068b90?d=identicon&s=25 Nicholas Clare (Guest)
on 2007-05-09 21:35
(Received via mailing list)
On 5/9/07, Markus Hohenhaus <markush@hni.upb.de> wrote:
>        attr_writer :one, :two, :three
> within my main application:
>
> wrong and made a mistake?
>
> --
> Posted via http://www.ruby-forum.com/.
>
>
I'm pretty new to ruby too, but from what I understand, the writer
methods
will be called one=, two=, etc

So changing that @methods array to include the equals should fix your
problem

Hope this helps,
Nick
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 Gary Wright (Guest)
on 2007-05-09 21:38
(Received via mailing list)
On May 9, 2007, at 3:23 PM, Markus Hohenhaus wrote:

> I'm a total Ruby newbie looking for help.
> [...]
>    @methods.each { |a| test.send(a, 'foo') }

> This throws the error: ArgumentError: wrong number of Arguments (1 for
> 0)

The problem is you are calling the 'getters' with an argument ('foo').
Getters don't take an argument, thus the error message.  You want:

    @methods.each { |a| puts test.send(a) }

I added the puts, otherwise you won't get any output.

If you want to call the setters, you need to send a different method
name:

    @methods.each { |a| test.send("#{a}=", 'foo') }

And of course you need to send an argument also.

Gary Wright
1151dc5752afb29c75025406f0ffd7de?d=identicon&s=25 Markus Hohenhaus (daisagah)
on 2007-05-09 22:10
> The problem is you are calling the 'getters' with an argument ('foo').
> Getters don't take an argument, thus the error message.  You want:
>
>     @methods.each { |a| puts test.send(a) }
>
> I added the puts, otherwise you won't get any output.

Yep, this works perfectly. I already tested this.

>
> If you want to call the setters, you need to send a different method
> name:
>
>     @methods.each { |a| test.send("#{a}=", 'foo') }
>
> And of course you need to send an argument also.

That's it!! Thanks. Now everything works as expected. Allthough I like
mine better, seems more
logical to me ;).
852a62a28f1de229dc861ce903b07a60?d=identicon&s=25 Gavin Kistner (phrogz)
on 2007-05-10 00:41
(Received via mailing list)
On May 9, 2:10 pm, Markus Hohenhaus <mark...@hni.upb.de> wrote:
> That's it!! Thanks. Now everything works as expected. Allthough I like
> mine better, seems more
> logical to me ;).

Well, you can do that too, if you like:

class Module
  def attr_readwrite( *methods )
    class_eval{
      methods.each{ |method_name|
        define_method( method_name ){ |*val|
          val = val.first
          unless val.nil?
            instance_variable_set( "@#{method_name}", val )
          else
            instance_variable_get( "@#{method_name}" )
          end
        }
      }
    }
  end
end

class Foo
  attr_readwrite :foo, :bar
end

f = Foo.new

f.foo( 42 )
p f.foo
#=> 42
f.foo( 12 )
p f.foo
#=> 12


(I think there's a more elegant way to define the method and detect if
it received a value, but I was too lazy to figure it out.)

I'm pretty sure Ara T. Howard's 'attributes' gem also gives you the
ability to do what you want, where calling the method without a
parameter acts as a getter, and calling it with a parameter acts as a
setter. And I bet his way is clean. :)
This topic is locked and can not be replied to.