Add entry point that returns block output

This commit is contained in:
Christina Koller 2018-01-11 14:27:50 -05:00 committed by Marc Siegel
parent 4ff2030975
commit 5d59cf33cf
3 changed files with 89 additions and 0 deletions

View File

@ -274,6 +274,33 @@ end
All set!
### Accessing the block's return value
Sometimes you might want to access the return value of your provided block,
as opposed to the DSL object itself. In these cases, use
`dsl_eval_with_block_return`. It behaves exactly like `dsl_eval`, but returns
the output from executing the block, rather than the DSL object.
```ruby
arr = []
with_array(arr) do
push "a"
push "b"
push "c"
length
end
#=> 3
arr
#=> ["a", "b", "c"]
```
```ruby
def with_array(arr=[], &block)
Docile.dsl_eval_with_block_return(arr, &block)
end
```
## Features
1. Method lookup falls back from the DSL object to the block's context

View File

@ -45,6 +45,45 @@ module Docile
end
module_function :dsl_eval
# Execute a block in the context of an object whose methods represent the
# commands in a DSL, and return the block's return value.
#
# @note Use with an *imperative* DSL (commands modify the context object)
#
# 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 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
# reverse!
# upcase!
# first
# end
# #=> "!"
#
# @example Use an Array as a DSL
# Docile.dsl_eval([]) do
# push "a"
# push "b"
# pop
# push "c"
# length
# end
# #=> 2
#
# @param dsl [Object] context object whose methods make up the DSL
# @param args [Array] arguments to be passed to the block
# @param block [Proc] the block of DSL commands to be executed against the
# `dsl` context object
# @return [Object] the return value from executing the block
def dsl_eval_with_block_return(dsl, *args, &block)
exec_in_proxy_context(dsl, FallbackContextProxy, *args, &block)
end
module_function :dsl_eval_with_block_return
# 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.
#

View File

@ -263,6 +263,29 @@ describe Docile do
end
describe '.dsl_eval_with_block_return' do
let(:array) { [] }
let!(:result) { execute_dsl_against_array }
def execute_dsl_against_array
Docile.dsl_eval_with_block_return(array) do
push 1
push 2
pop
push 3
'Return me!'
end
end
it 'executes the block against the DSL context object' do
expect(array).to eq([1, 3])
end
it "returns the block's return value" do
expect(result).to eq('Return me!')
end
end
describe '.dsl_eval_immutable' do
context 'when DSL context object is a frozen String' do