简体   繁体   中英

How to provide class/object methods in a block in Ruby?

Sometimes you can see:

do_this do
   available_method1 "arg1"
   available_method2 "arg1"
end

When I use the block from do_this method then I get some methods I could use inside that block.

I wonder how this is accomplished? How does the code look like behind the scenes?

I want to be able to provide some methods through a block.

It's called a Domain-Specific Language (DSL). Here's (Last archived version) some great info on various forms of Ruby DSL blocks.

There are really two ways to go about doing this, with different syntaxes:

do_thing do |thing| # with a block parameter
  thing.foo :bar
  thing.baz :wibble
end

# versus

do_thing do # with block-specific methods
  foo :bar
  baz :wibble
end

The first is more explicit and less likely to fail, while the second is more concise.

The first can be implemented like so, by simply passing a new instance as the block parameter with yield :

class MyThing
  def self.create
    yield new
  end

  def foo(stuff)
    puts "doing foo with #{stuff}"
  end
end

MyThing.create do |thing|
  thing.foo :bar
end

And the second, which runs the block in the context of the new object, giving it access to self , instance variables, and methods:

class MyThing
  def self.create(&block)
    new.instance_eval &block
  end

  def foo(stuff)
    puts "doing foo with #{stuff}"
  end
end

MyThing.create do
  foo :bar
end

And if you really want to do it without calling MyThing.create , just:

def create_thing(&block)
  MyThing.create &block
end

This is usually done using instance_eval to change the value of self inside the block to be some different object, which then handles those method calls.

As a quick example:

class ExampleReceiver
  def available_method1 arg ; p [:available_method1, arg] ; end
  def available_method2 arg ; p [:available_method2, arg] ; end
end
def do_this(&blk) ; ExampleReceiver.new.instance_eval(&blk) ; end

do_this do
  available_method1 "arg1" #=> [:available_method1, "arg1"]
  available_method2 "arg1" #=> [:available_method2, "arg1"]
end

Though this is a powerful language feature, and has been used before to great effect, there is still some debate on whether it's a good idea or not. If you don't know what's going on, you might be surprised that the value of @some_instance_variable changes inside and outside the block, since it's relative to the current value of self .

See Daniel Azuma's excellent article for more discussion and details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM