support local variable and method fallback contexts

This commit is contained in:
Marc Siegel 2011-12-06 15:05:31 -05:00
parent 2a6452b432
commit c04587a03f
3 changed files with 84 additions and 32 deletions

View File

@ -1,9 +1,11 @@
require "docile/version" require "docile/version"
require "docile/fallback_context_proxy"
module Docile module Docile
# Executes a block in the context of an object whose interface represents a DSL. # Executes a block in the context of an object whose interface represents a DSL.
# #
# Docile.dsl_eval([]) do # Example of using an Array as a DSL:
# Docile.dsl_eval [] do
# push 1 # push 1
# push 2 # push 2
# pop # pop
@ -16,7 +18,7 @@ module Docile
# @return [Object] the given DSL object # @return [Object] the given DSL object
def dsl_eval(dsl, &block) def dsl_eval(dsl, &block)
block_context = eval("self", block.binding) block_context = eval("self", block.binding)
dsl.instance_eval(&block) FallbackContextProxy.new(dsl, block_context).instance_eval(&block)
dsl dsl
end end
module_function :dsl_eval module_function :dsl_eval

View File

@ -0,0 +1,43 @@
require 'set'
module Docile
class FallbackContextProxy
BASIC_METHODS = Set[:==, :equal?, :"!", :"!=", :instance_eval, :object_id, :__send__, :__id__]
instance_methods.each do |method|
unless BASIC_METHODS.include?(method.to_sym)
undef_method(method)
end
end
def initialize(receiver, fallback)
@__receiver__ = receiver
@__fallback__ = fallback
end
def id
@__receiver__.__send__(:id)
end
# Special case due to `Kernel#sub`'s existence
def sub(*args, &block)
__proxy_method__(:sub, *args, &block)
end
def method_missing(method, *args, &block)
__proxy_method__(method, *args, &block)
end
def __proxy_method__(method, *args, &block)
begin
@__receiver__.__send__(method.to_sym, *args, &block)
rescue ::NoMethodError => e
begin
@__fallback__.__send__(method.to_sym, *args, &block)
rescue ::NoMethodError
raise(e)
end
end
end
end
end

View File

@ -30,8 +30,7 @@ describe Docile do
end.should == [1, 3] end.should == [1, 3]
end end
context "methods" context "methods" do
it "should find method of outer dsl in outer dsl scope" do it "should find method of outer dsl in outer dsl scope" do
outer { a.should == 'a' } outer { a.should == 'a' }
end end
@ -40,36 +39,44 @@ describe Docile do
outer { inner { b.should == 'b' } } outer { inner { b.should == 'b' } }
end end
#it "should find method of outer dsl in inner dsl scope" do it "should find method of outer dsl in inner dsl scope" do
# outer { inner { a.should == 'a' } } outer { inner { a.should == 'a' } }
#end
#
#it "should find method of block's context in outer dsl scope" do
# def c; 'c'; end
# outer { c.should == 'c' }
#end
#
#it "should find method of block's context in inner dsl scope" do
# def c; 'c'; end
# outer { inner { c.should == 'c' } }
#end
end end
# it "should find local variable from block context in outer dsl scope" do it "should find method of block's context in outer dsl scope" do
# foo = 'foo' def c; 'c'; end
# outer { foo.should == 'foo' } outer { c.should == 'c' }
# end end
#
# it "should find method of block's context in inner dsl scope" do
# def c; 'c'; end
# it "should find local variable from block definition in inner dsl scope" do outer { inner { c.should == 'c' } }
# bar = 'bar' end
# outer { inner { bar.should == 'bar' } }
# end it "should find method of outer dsl in preference to block context" do
# def a; 'not a'; end
outer { a.should == 'a' }
end
end
context "local variables" do
it "should find local variable from block context in outer dsl scope" do
foo = 'foo'
outer { foo.should == 'foo' }
end
it "should find local variable from block definition in inner dsl scope" do
bar = 'bar'
outer { inner { bar.should == 'bar' } }
end
end
context "instance variables" do
#it "should find instance variable from block definition in inner dsl scope" do #it "should find instance variable from block definition in inner dsl scope" do
# @iv1 = 'iv1'; outer { inner { @iv1.should == 'iv1' } } # @iv1 = 'iv1'; outer { inner { @iv1.should == 'iv1' } }
#end #end
#end end
end
end end