class Blockenspiel::Builder

Dynamically construct a target

These methods are available in a block passed to Blockenspiel#invoke and can be used to dynamically define what methods are available from a block. See Blockenspiel#invoke for more information.

Public Instance Methods

add_method(name_, opts_={}, &block_) click to toggle source

Declare a DSL method.

This call creates a method that can be called from the DSL block. Provide a name for the method, a block defining the method's implementation, and an optional hash of options.

By default, a method of the same name is also made available to parameterless blocks. To change the name of the parameterless method, provide its name as the value of the :dsl_method option. To disable this method for parameterless blocks, set the :dsl_method option to false.

The :mixin option is a deprecated alias for :dsl_method.

Warning about the return keyword

Because you are implementing your method using a block, remember the distinction between Proc.new and lambda. Invoking return from the former does not return from the block, but returns from the surrounding method scope. Since normal blocks passed to methods are of the former type, be very careful about using the return keyword:

add_method(:foo) do |param|
  puts "foo called with parameter "+param.inspect
  return "a return value"   # DOESN'T WORK LIKE YOU EXPECT!
end

To return a value from the method you are creating, set the evaluation value at the end of the block:

add_method(:foo) do |param|
  puts "foo called with parameter "+param.inspect
  "a return value"    # Returns from method foo
end

If you must use the return keyword, create your block as a lambda as in this example:

code = lambda do |param|
  puts "foo called with parameter "+param.inspect
  return "a return value"   # Returns from method foo
end
add_method(:foo, &code)

Accepting a block argument

If you want your method to take a block, you have several options depending on your Ruby version. If you are running the standard Matz Ruby interpreter (MRI) version 1.8.7 or later (including 1.9.x), or a compatible interpreter such as JRuby 1.5 or later, you can use the standard “&” block argument notation to receive the block. Note that you must call the passed block using the call method since Ruby doesn't support invoking such a block with yield. For example, to create a method named “foo” that takes one parameter and a block, do this:

add_method(:foo) do |param, &block|
  puts "foo called with parameter "+param.inspect
  puts "the block returned "+block.call.inspect
end

In your DSL, you can then call:

foo("hello"){ "a value" }

If you are using MRI 1.8.6, or another Ruby interpreter that doesn't fully support this syntax (such as JRuby versions older than 1.5), Blockenspiel provides an alternative in the form of the :block option. This option causes blocks provided by the caller to be included in the normal parameter list to your method, instead of as a block parameter. It can be set to :first or :last to prepend or append, respectively, the block (as a Proc object) to the parameter list. If the caller does not include a block when calling your DSL method, nil is prepended/appended. For example:

add_method(:foo, :block => :last) do |param, block|
  puts "foo called with parameter "+param.inspect
  if block
    puts "the block returned "+block.call.inspect
  else
    puts "no block passed"
  end
end

The :receive_block option is a deprecated alternative. Setting :receive_block => true is currently equivalent to setting :block => :last.

# File lib/blockenspiel/builder.rb, line 187
def add_method(name_, opts_={}, &block_)
  receive_block_ = opts_[:receive_block] ? :last : opts_[:block]
  receive_block_ = :first if receive_block_ && receive_block_ != :last
  @target_class._add_methodinfo(name_, block_, receive_block_)
  dsl_method_name_ = opts_[:dsl_method] || opts_[:mixin]
  if dsl_method_name_ != false
    dsl_method_name_ = name_ if dsl_method_name_.nil? || dsl_method_name_ == true
    @target_class.dsl_method(dsl_method_name_, name_)
  end
end