From 62de806d24f4dee104d2b89babecb6d2c2b2e029 Mon Sep 17 00:00:00 2001 From: Marc Siegel Date: Sun, 28 Jul 2013 23:57:25 -0400 Subject: [PATCH] Add documentation of #dsl_eval_immutable --- lib/docile.rb | 49 +++++++++++++++++-- lib/docile/chaining_fallback_context_proxy.rb | 13 +++-- lib/docile/fallback_context_proxy.rb | 4 ++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/lib/docile.rb b/lib/docile.rb index bf610b8..5dafd0c 100644 --- a/lib/docile.rb +++ b/lib/docile.rb @@ -3,7 +3,6 @@ require "docile/fallback_context_proxy" require "docile/chaining_fallback_context_proxy" # Docile keeps your Ruby DSLs tame and well-behaved -# @see http://ms-ati.github.com/docile/ module Docile # Execute a block in the context of an object whose methods represent the # commands in a DSL. @@ -12,8 +11,9 @@ module Docile # # Use this method to execute an *imperative* DSL, which means that: # - # 1. each command mutates the state of the DSL context object - # 2. the return values of the commands are ignored + # 1. Each command mutates the state of the DSL context object + # 2. The return value of each command is ignored + # 3. The final return value is the original context object # # @example Use a String as a DSL # Docile.dsl_eval("Hello, world!") do @@ -42,6 +42,38 @@ module Docile end module_function :dsl_eval + # Execute a block in the context of an immutable object whose methods, + # and the methods of their return values, represent the commands in a DSL. + # + # @note Use with a *functional* DSL (commands return successor + # context objects) + # + # Use this method to execute a *functional* DSL, which means that: + # + # 1. The original DSL context object is never mutated + # 2. Each command returns the next DSL context object + # 3. The final return value is the value returned by the last command + # + # @example Use a String as a DSL + # Docile.dsl_eval_immutable("Hello, world!") do + # reverse + # upcase + # end + # #=> "!DLROW ,OLLEH" + # + # @example Use a Float as a DSL + # Docile.dsl_eval_immutable(84.5) do + # fdiv(2) + # floor + # end + # #=> 42 + # + # @param dsl [Object] immutable context object whose methods make up the + # initial DSL + # @param args [Array] arguments to be passed to the block + # @yield the block of DSL commands to be executed against the + # `dsl` context object and successor return values + # @return [Object] the return value of the final command in the block def dsl_eval_immutable(dsl, *args, &block) exec_in_proxy_context(dsl, ChainingFallbackContextProxy, *args, &block) end @@ -49,6 +81,17 @@ module Docile private + # @api private + # + # Execute a block in the context of an object whose methods represent the + # commands in a DSL, using a specific proxy class. + # + # @param dsl [Object] context object whose methods make up the + # (initial) DSL + # @param proxy_type [Class] class to instantiate as the proxy context + # @param args [Array] arguments to be passed to the block + # @yield the block of DSL commands to be executed + # @return [Object] the return value of the block def exec_in_proxy_context(dsl, proxy_type, *args, &block) block_context = eval("self", block.binding) proxy_context = proxy_type.new(dsl, proxy_type.new(dsl, block_context)) diff --git a/lib/docile/chaining_fallback_context_proxy.rb b/lib/docile/chaining_fallback_context_proxy.rb index 3a4537e..6f13056 100644 --- a/lib/docile/chaining_fallback_context_proxy.rb +++ b/lib/docile/chaining_fallback_context_proxy.rb @@ -1,13 +1,18 @@ require "docile/fallback_context_proxy" module Docile - + # Operates in the same manner as {FallbackContextProxy}, but replacing + # the primary `receiver` object with the result of each proxied method. + # + # This is useful for implementing DSL evaluation for immutable context + # objects. + # + # @see Docile#dsl_eval_immutable class ChainingFallbackContextProxy < FallbackContextProxy - + # Proxy methods as in {FallbackContextProxy#method_missing}, replacing + # `receiver` with the returned value. def method_missing(method, *args, &block) @__receiver__ = super(method, *args, &block) end - end - end \ No newline at end of file diff --git a/lib/docile/fallback_context_proxy.rb b/lib/docile/fallback_context_proxy.rb index 236b815..a84fd40 100644 --- a/lib/docile/fallback_context_proxy.rb +++ b/lib/docile/fallback_context_proxy.rb @@ -7,6 +7,10 @@ module Docile # Will attempt to forward all method calls first to the primary receiver, # and then to the fallback receiver if the primary does not handle that # method. + # + # This is useful for implementing DSL evaluation in the context of an object. + # + # @see Docile#dsl_eval class FallbackContextProxy # The set of methods which will **not** be proxied, but instead answered # by this object directly.