views:

201

answers:

4

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.

+2  A: 

I suppose you could do....

def initialize *e
  @a, @b, @c = e
end
DigitalRoss
I would of course prefer maintaining argument length knowledge but yes this is certainly more concise... Hmm.
roja
A: 

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
+1 for cleverness, -1 for cleverness. ;)
Jordan
changed 'better' for 'more concise' damn my generalities!
roja
A: 

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
Shame, i would say exactly what i desire. I am going to go and hunt down said proposal and try and find out Matz's rejection reasons.
roja
Which is different to not remembering _why's opinion. Sorry, bad pun.
Andrew Grimm
+3  A: 

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
Fantastic intro to block assignment semantics. Seriously interesting :) I am going to have to go and read yet more about this stuff now!!
roja