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, 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 =
1.0.0
'1.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, propigate_local_commands = false, command_name = nil) ⇒ 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(parent = nil, &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(parent = nil, &block) ⇒ DslBlock
Create a new DslBlock instance.
parent-
Optional parent DslBlock or Object that is providing additional commands to the block. (default: nil)
block-
Required block of code that will be executed when yield is called on the new DslBlock instance.
108 109 110 111 112 |
# File 'lib/dsl_block.rb', line 108 def initialize(parent = nil, &block) raise ArgumentError, 'block must be provided' unless block_given? @block = block @parent = parent end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
152 153 154 155 156 157 158 |
# File 'lib/dsl_block.rb', line 152 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.
73 74 75 |
# File 'lib/dsl_block.rb', line 73 def block @block end |
#parent ⇒ Object
Parent object providing additional commands to the block.
71 72 73 |
# File 'lib/dsl_block.rb', line 71 def parent @parent end |
Class Method Details
.add_command_to(destination, propigate_local_commands = false, command_name = nil) ⇒ 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
propigate_local_commands-
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)
90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/dsl_block.rb', line 90 def self.add_command_to(destination, propigate_local_commands=false, command_name=nil) # 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(propigate_local_commands ? 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.
77 78 79 80 81 |
# File 'lib/dsl_block.rb', line 77 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.
123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/dsl_block.rb', line 123 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:
148 149 150 |
# File 'lib/dsl_block.rb', line 148 def respond_to_missing?(method, include_all) @parent && @parent.respond_to?(method, include_all) || super end |
#yield ⇒ Object
Yield the block given.
136 137 138 139 140 141 142 143 144 145 |
# File 'lib/dsl_block.rb', line 136 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 |