2

Possible Duplicate:
Idiomatic object creation in ruby

Sometimes it's useful to assign numerous of a constructed arguments to instance variables on construction. Other than the obvious method:

def initialize(arg1, arg2, arg3)
  @arg1, @arg2, @arg3 = arg1, arg2, arg3
end

Is there a more concise idiom for achieving the same result? Something like that found in scala for instance:

class FancyGreeter(greeting: String) {
  def greet() = println(greeting)
}

Where in this case the object FancyGreeter has a default constructor that provides assignment for it's passed arguments.

Community
  • 1
  • 1
Roja Buck
  • 2,354
  • 16
  • 26
  • Possible duplicate: http://stackoverflow.com/questions/1778638/idiomatic-object-creation-in-ruby – Andrew Grimm Feb 15 '10 at 22:08
  • It would certainly seem to be a duplicate now you have pointed it out (I had searched but my search terms were miles off that questions title.) I think the titles are distinct enough to allow different people searching for an answer to the same question to happily locate one or the other and thus i am a little loth to delete this question... What do people think? – Roja Buck Feb 16 '10 at 18:19
  • 1
    Duplicate questions are generally closed (no new answers can be added) rather than deleted (sent to /dev/null). – Andrew Grimm Feb 16 '10 at 22:07

4 Answers4

4

In Ruby 1.8, block arguments and method arguments have different semantics: method arguments have binding semantics, block arguments have assignment semantics.

What that means is that when you call a method, the method arguments get bound to the values that you pass in. When you call a block, the values get assigned to the arguments.

So, you can create some pretty crazy looking blocks that way, that seemingly don't do anything:

lambda {|@a|}.call(42)

The block body is empty, but because of the argument assignment semantics, the instance variable @a will be assigned the value 42. It works even crazier:

lambda {|foo.bar|}.call(42)

Yes, attr_writer methods work too. Or what about

foo = {}
lambda {|foo[:bar]|}.call(42)
p foo # => {:bar => 42}

Yup, those too.

And since you can define methods using blocks, you can do this:

class FancyGreeter
  define_method(:initialize) {|@greeting|}
  def greet; puts @greeting end
end

or even

class FancyGreeter
  attr_accessor :greeting
  define_method(:initialize) {|self.greeting|}
  def greet; puts greeting end
end

However, I wouldn't recommend this for two reasons:

  • Not many Rubyists know this, be kind to the people who have to maintain the code after you.
  • In Ruby 1.9 and onwards, block argument semantics are gone, blocks also use method argument semantics, therefore this does no longer work.
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • Fantastic intro to block assignment semantics. Seriously interesting :) I am going to have to go and read yet more about this stuff now!! – Roja Buck Feb 16 '10 at 18:22
2

I suppose you could do....

def initialize *e
  @a, @b, @c = e
end
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • I would of course prefer maintaining argument length knowledge but yes this is certainly more concise... Hmm. – Roja Buck Feb 15 '10 at 19:11
0

I don't know about "better" but there are varying levels of 'clever':

def initialize args={}
  args.each do |key, value|
    instance_variable_set "@#{key}", value
  end
end

But "clever" is usually dangerous when you program :-)


Edit: Given the edited question, I'll add this:

Class PickMe
  def initialize say="what?"
    @say = say
  end
end

Just because I don't know if you're aware of default options. Otherwise, think of the value of self-documenting code. A cleanly-written 'initialize' method is priceless.

Trevoke
  • 4,115
  • 1
  • 27
  • 48
0

It was either Andy Hunt or Dave Thomas who proposed that Ruby should be able to handle this syntax for initializing member variables from constructor arguments:

  def initialize(@a, @b, @c)
     ...
  end

Matz did not accept their proposal; I don't remember why.

Wayne Conrad
  • 103,207
  • 26
  • 155
  • 191