1
0
Fork 0
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:
Marc Siegel 2013-04-01 14:12:39 -07:00
commit 92c669ece0
5 changed files with 159 additions and 6 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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'