开发者

Mass assignment on construction from within ruby [duplicate]

This question already has answers here: Closed 12 years ago. 开发者_开发百科

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.


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.


I suppose you could do....

def initialize *e
  @a, @b, @c = e
end


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜