In Ruby, is there a way to accomplish what `with`

2019-05-10 01:54发布

问题:

In Ruby, I would like to select a default object for a block.

An example in Actionscript is:

with (board) { 
    length = 66; 
    width = 19; 
    fin_system = 'lockbox'; 
}

Which is equivalent to:

board.length = 66;
board.width = 19;
board.fin_system = 'lockbox';

Here is the documentation for this statement in Actionscript: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/statements.html#with

How can I accomplish this in Ruby?

回答1:

Hash.new.tap do |h|
  h[:name] = "Mike"
  h[:language] = "Ruby"
end
#=> {:name=>"Mike", :language=>"Ruby"} 

You could try Object#tap with Ruby 1.9.

So in your case:

board.tap do |b|
  b.length = 66;
  b.width = 19;
  b.fin_system = "lockbox"
end


回答2:

One way to implement it is with instance_eval, like that:

def with(obj, &blk)
  obj.instance_eval(&blk)
end

a = "abc"
with a do
  self << 'b'
  gsub!('b', 'd')
  upcase!
end
puts a #=> ADCD

with board do 
  self.length = 66
  self.width = 19
  self.fin_system = 'lockbox'
end

But in some cases you have to use self (with operators and setting methods).



回答3:

You can't accomplish that exactly in Ruby because foo = bar will always set a foo local variable; it will never call a foo= method. You can use tap as suggested.

One solution to the larger design question would be to use a fluent interface:

board.length(66).width(20)

class Board
  def length(amt)
    @length = amt
    self
  end

  def width(amt)
    @width = amt
    self
  end
end

It's up to you to decide if this pattern suits your use case.