class DslBlock
DslBlock
is a base class for defining a Domain Specific Language. Subclasses of DslBlock
define the desired dsl. These methods become available to ruby code running in the context of the subclass. The block execution is automatically isolated to prevent the called block from accessing instance methods unless specifically designated as callable. DslBlocks can be nested and parent blocks can allow their methods to be exposed to child block.
Example¶ ↑
# Define three DslBlocks each with at least one command in each block class Foo < DslBlock commands :show_foo def show_foo(x) "Mr. T says you are a foo times #{x.to_i}" end end class Bar < DslBlock commands :show_bar def show_bar(x) "Ordering #{x.to_i} Shirley Temples from the bar" end end class Baz < DslBlock commands :show_baz def show_baz(x) "Baz spaz #{x.inspect}" end end # Connect the blocks to each other so they can be easily nested Baz.add_command_to(Bar) Bar.add_command_to(Foo, :propagate => true) # Let Bar blocks also respond to foo methods Foo.add_command_to(self) # Use the new DSL foo do self.inspect # => #<Foo:0x007fdbd52b54e0 @block=#<Proc:0x007fdbd52b5530@/home/fhall/wonderland/alice.rb:29>, @parent=nil> x = 10/10 show_foo x # => Mr. T says you are a foo times 1 bar do x *= 2 show_bar x # => Ordering 2 Shirley Temples from the bar x += 1 show_foo x # => Mr. T says you are a foo times 3 baz do x *= 4 x /= 3 show_baz x # => Baz spaz 4 begin x += 1 show_bar x # => NameError rescue NameError 'No bar for us' end end end end
Constants
- VERSION
2.0.0
Attributes
Block of code that will be executed upon yield.
Parent object providing additional commands to the block.
Public Class Methods
This is a convenience command that allows this DslBlock
to inject itself as a method into another DslBlock
or Object. If the parent is also a DslBlock
, the new method will automatically be added to the available commands.
Params:
destination
-
The object to receive the new method
options
-
A hash of options configuring the command
Options:
:propagate
-
Allow methods in the destination to be called by the block. (default: false)
:command_name
-
The name of the method to be created or nil to use the default which is based off of the class name. (default: nil)
# File lib/dsl_block.rb, line 95 def self.add_command_to(destination, options={}) # Determine the name of the method to create command_name = (options[:command_name] || name).to_s.underscore.to_sym # Save a reference to our self so we will have something to call in a bit when self will refer to someone else. this_class = self # Define the command in the destination. destination.send(:define_method, command_name) do |&block| # Create a new instance of our self with the callers 'self' passed in as an optional parent. # Immediately after initialization, yield the block. this_class.new(:parent => options[:propagate] ? self : nil, &block).yield end # Add the new command to the parent if it is a DslBlock. destination.commands << command_name if destination.is_a?(Class) && destination < DslBlock end
With no arguments, returns an array of command names that this DslBlock
makes available to blocks either directly or indirectly. With arguments, adds new names to the array of command names, then returns the new array.
# File lib/dsl_block.rb, line 79 def self.commands(*args) @commands ||= [] @commands = (@commands + args.map(&:to_sym)).uniq @commands end
Create a new DslBlock
instance.
block
-
Required block of code that will be executed when yield is called on the new
DslBlock
instance.
Options:
:parent
-
Optional parent
DslBlock
or Object that is providing additional commands to the block. (default: nil) :block
-
Optional method of passing in a block.
# File lib/dsl_block.rb, line 116 def initialize(*args, &block) options = args.extract_options! @block = block_given? ? block : options[:block] raise ArgumentError, 'block must be provided' unless @block @parent = options[:parent] end
Public Instance Methods
This is the entire list of commands that this instance makes available to the block of code to be run. It is a combination of three distinct sources.
-
The class’s declared commands
-
If there is a parent of this DslBock instance…
-
The parents declared commands if it is a
DslBlock
-
The parents public_methods if it is any other type of object
-
-
Kernel.methods
This method is prefixed with an underscore in an attempt to avoid collisions with commands in the given block.
# File lib/dsl_block.rb, line 132 def _commands cmds = self.class.commands.dup if @parent if @parent.is_a?(DslBlock) cmds += @parent._commands else cmds += @parent.public_methods end end (cmds + Kernel.methods).uniq end
Yield the block given.
# File lib/dsl_block.rb, line 145 def yield begin # Evaluate the block in an executor to provide isolation # and prevent accidental interference with ourselves. Executor.new(self).instance_eval(&@block) rescue Exception => e e.set_backtrace(caller.select { |x| !x.include?(__FILE__)}) raise e end end