Class: DslBlock
- Inherits:
-
Object
- Object
- DslBlock
- Defined in:
- lib/dsl_block.rb,
lib/dsl_block/version.rb,
lib/dsl_block/executor.rb
Overview
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 (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
do
x *= 2
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
x # => NameError
rescue NameError
'No bar for us'
end
end
end
end
Defined Under Namespace
Modules: Version Classes: Executor
Constant Summary collapse
- VERSION =
2.0.0
'2.0.0'
Instance Attribute Summary collapse
-
#block ⇒ Object
Block of code that will be executed upon yield.
-
#parent ⇒ Object
Parent object providing additional commands to the block.
Class Method Summary collapse
-
.add_command_to(destination, options = {}) ⇒ Object
This is a convenience command that allows this DslBlock to inject itself as a method into another DslBlock or Object.
-
.commands(*args) ⇒ Object
With no arguments, returns an array of command names that this DslBlock makes available to blocks either directly or indirectly.
Instance Method Summary collapse
-
#_commands ⇒ Object
This is the entire list of commands that this instance makes available to the block of code to be run.
-
#initialize(*args, &block) ⇒ DslBlock
constructor
Create a new DslBlock instance.
- #method_missing(name, *args, &block) ⇒ Object
-
#respond_to_missing?(method, include_all) ⇒ Boolean
:nodoc:.
-
#yield ⇒ Object
Yield the block given.
Constructor Details
#initialize(*args, &block) ⇒ DslBlock
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.
116 117 118 119 120 121 |
# File 'lib/dsl_block.rb', line 116 def initialize(*args, &block) = args. @block = block_given? ? block : [:block] raise ArgumentError, 'block must be provided' unless @block @parent = [:parent] end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
161 162 163 164 165 166 167 |
# File 'lib/dsl_block.rb', line 161 def method_missing(name, *args, &block) if @parent && @parent.respond_to?(name) @parent.send(name, *args, &block) else super end end |
Instance Attribute Details
#block ⇒ Object
Block of code that will be executed upon yield.
75 76 77 |
# File 'lib/dsl_block.rb', line 75 def block @block end |
#parent ⇒ Object
Parent object providing additional commands to the block.
73 74 75 |
# File 'lib/dsl_block.rb', line 73 def parent @parent end |
Class Method Details
.add_command_to(destination, options = {}) ⇒ Object
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)
95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/dsl_block.rb', line 95 def self.add_command_to(destination, ={}) # Determine the name of the method to create command_name = ([: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 => [: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 |
.commands(*args) ⇒ Object
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.
79 80 81 82 83 |
# File 'lib/dsl_block.rb', line 79 def self.commands(*args) @commands ||= [] @commands = (@commands + args.map(&:to_sym)).uniq @commands end |
Instance Method Details
#_commands ⇒ Object
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.
132 133 134 135 136 137 138 139 140 141 142 |
# 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 |
#respond_to_missing?(method, include_all) ⇒ Boolean
:nodoc:
157 158 159 |
# File 'lib/dsl_block.rb', line 157 def respond_to_missing?(method, include_all) @parent && @parent.respond_to?(method, include_all) || super end |
#yield ⇒ Object
Yield the block given.
145 146 147 148 149 150 151 152 153 154 |
# 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 |