mirror of
https://github.com/ms-ati/docile
synced 2023-03-27 23:21:52 -04:00
Merge pull request #2 from dslh/master
Passing parameters to DSL blocks.
This commit is contained in:
commit
92c669ece0
5 changed files with 159 additions and 6 deletions
58
README.md
58
README.md
|
@ -81,6 +81,64 @@ It's just that easy!
|
|||
|
||||
[2]: http://stackoverflow.com/questions/328496/when-would-you-use-the-builder-pattern "Builder Pattern"
|
||||
|
||||
## Block parameters
|
||||
|
||||
Parameters can be passed to the DSL block.
|
||||
|
||||
Supposing you want to make some sort of cheap [Sinatra][3] knockoff:
|
||||
```ruby
|
||||
@last_request = nil
|
||||
respond '/path' do |request|
|
||||
puts "Request received: #{request}"
|
||||
@last_request = request
|
||||
end
|
||||
|
||||
def ride bike
|
||||
# Play with your new bike
|
||||
end
|
||||
|
||||
respond '/new_bike' do |bike|
|
||||
ride(bike)
|
||||
end
|
||||
```
|
||||
|
||||
You'd put together a dispatcher something like this:
|
||||
```ruby
|
||||
require 'singleton'
|
||||
|
||||
class DispatchScope
|
||||
def a_method_you_can_call_from_inside_the_block
|
||||
:useful_huh?
|
||||
end
|
||||
end
|
||||
|
||||
class MessageDispatch
|
||||
include Singleton
|
||||
|
||||
def initialize
|
||||
@responders = {}
|
||||
end
|
||||
|
||||
def add_responder path, &block
|
||||
@responders[path] = block
|
||||
end
|
||||
|
||||
def dispatch path, request
|
||||
Docile.dsl_eval(DispatchScope.new, request, &@responders[path])
|
||||
end
|
||||
end
|
||||
|
||||
def respond path, &handler
|
||||
MessageDispatch.instance.add_responder path, handler
|
||||
end
|
||||
|
||||
def send_request path, request
|
||||
MessageDispatch.instance.dispatch path, request
|
||||
end
|
||||
```
|
||||
|
||||
[3]: http://www.sinatrarb.com "Sinatra"
|
||||
|
||||
## Features
|
||||
|
||||
1. method lookup falls back from the DSL object to the block's context
|
||||
|
|
|
@ -15,14 +15,15 @@ module Docile
|
|||
# #=> [1, 3]
|
||||
#
|
||||
# @param dsl [Object] an object whose methods represent a DSL
|
||||
# @param args [Array] arguments to be passed to the block
|
||||
# @param block [Proc] a block to execute in the DSL context
|
||||
# @return [Object] the dsl object, after execution of the block
|
||||
def dsl_eval(dsl, &block)
|
||||
def dsl_eval(dsl, *args, &block)
|
||||
block_context = eval("self", block.binding)
|
||||
proxy_context = FallbackContextProxy.new(dsl, block_context)
|
||||
begin
|
||||
block_context.instance_variables.each { |ivar| proxy_context.instance_variable_set(ivar, block_context.instance_variable_get(ivar)) }
|
||||
proxy_context.instance_eval(&block)
|
||||
proxy_context.instance_exec(*args,&block)
|
||||
ensure
|
||||
block_context.instance_variables.each { |ivar| block_context.instance_variable_set(ivar, proxy_context.instance_variable_get(ivar)) }
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ require 'set'
|
|||
|
||||
module Docile
|
||||
class FallbackContextProxy
|
||||
NON_PROXIED_METHODS = Set[:object_id, :__send__, :__id__, :==, :equal?, :"!", :"!=", :instance_eval,
|
||||
NON_PROXIED_METHODS = Set[:object_id, :__send__, :__id__, :==, :equal?, :"!", :"!=", :instance_exec,
|
||||
:instance_variables, :instance_variable_get, :instance_variable_set,
|
||||
:remove_instance_variable]
|
||||
|
||||
|
@ -50,4 +50,4 @@ module Docile
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,12 +45,46 @@ describe Docile do
|
|||
def inner(&block)
|
||||
Docile.dsl_eval(InnerDSL.new, &block)
|
||||
end
|
||||
|
||||
def inner_with_params(param,&block)
|
||||
Docile.dsl_eval(InnerDSL.new, param, :foo, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def outer(&block)
|
||||
Docile.dsl_eval(OuterDSL.new, &block)
|
||||
end
|
||||
|
||||
def parameterized(*args,&block)
|
||||
Docile.dsl_eval(OuterDSL.new, *args, &block)
|
||||
end
|
||||
|
||||
context "parameters" do
|
||||
it "should pass parameters to the block" do
|
||||
parameterized(1,2,3) do |x,y,z|
|
||||
x.should == 1
|
||||
y.should == 2
|
||||
z.should == 3
|
||||
end
|
||||
end
|
||||
|
||||
it "should find parameters before methods" do
|
||||
parameterized(1) { |a| a.should == 1 }
|
||||
end
|
||||
|
||||
it "should find outer parameters in inner dsl scope" do
|
||||
parameterized(1,2,3) do |a,b,c|
|
||||
inner_with_params(c) do |d,e|
|
||||
a.should == 1
|
||||
b.should == 2
|
||||
c.should == 3
|
||||
d.should == c
|
||||
e.should == :foo
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "methods" do
|
||||
it "should find method of outer dsl in outer dsl scope" do
|
||||
outer { a.should == 'a' }
|
||||
|
@ -111,6 +145,65 @@ describe Docile do
|
|||
end
|
||||
end
|
||||
|
||||
class DispatchScope
|
||||
def params
|
||||
{ :a => 1, :b => 2, :c => 3 }
|
||||
end
|
||||
end
|
||||
|
||||
class MessageDispatch
|
||||
include Singleton
|
||||
|
||||
def initialize
|
||||
@responders = {}
|
||||
end
|
||||
|
||||
def add_responder path, &block
|
||||
@responders[path] = block
|
||||
end
|
||||
|
||||
def dispatch path, request
|
||||
Docile.dsl_eval(DispatchScope.new, request, &@responders[path])
|
||||
end
|
||||
end
|
||||
|
||||
def respond path, &block
|
||||
MessageDispatch.instance.add_responder path, &block
|
||||
end
|
||||
|
||||
def send_request path, request
|
||||
MessageDispatch.instance.dispatch path, request
|
||||
end
|
||||
|
||||
it "should handle the dispatch pattern" do
|
||||
@first = @second = nil
|
||||
respond '/path' do |request|
|
||||
@first = request
|
||||
end
|
||||
|
||||
respond '/new_bike' do |bike|
|
||||
@second = "Got a new #{bike}"
|
||||
end
|
||||
|
||||
def x(y) ; "Got a #{y}"; end
|
||||
respond '/third' do |third|
|
||||
x(third).should == 'Got a third thing'
|
||||
end
|
||||
|
||||
fourth = nil
|
||||
respond '/params' do |arg|
|
||||
fourth = params[arg]
|
||||
end
|
||||
|
||||
send_request '/path', 1
|
||||
send_request '/new_bike', 'ten speed'
|
||||
send_request '/third', 'third thing'
|
||||
send_request '/params', :b
|
||||
|
||||
@first.should == 1
|
||||
@second.should == 'Got a new ten speed'
|
||||
fourth.should == 2
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require 'rubygems'
|
||||
require 'rspec'
|
||||
require 'singleton'
|
||||
|
||||
test_dir = File.dirname(__FILE__)
|
||||
$LOAD_PATH.unshift test_dir unless $LOAD_PATH.include?(test_dir)
|
||||
|
@ -7,4 +8,4 @@ $LOAD_PATH.unshift test_dir unless $LOAD_PATH.include?(test_dir)
|
|||
lib_dir = File.join(File.dirname(test_dir), 'lib')
|
||||
$LOAD_PATH.unshift lib_dir unless $LOAD_PATH.include?(lib_dir)
|
||||
|
||||
require 'docile'
|
||||
require 'docile'
|
||||
|
|
Loading…
Add table
Reference in a new issue