docile 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75054a395059cd55ba3132c00dc49ae15cc6b954
4
- data.tar.gz: 71f6114e6ac5d727240bd50c8e967127af688a53
3
+ metadata.gz: 57d90aa5d94c06fab36bcf2ad9c058e8b3c7ec3b
4
+ data.tar.gz: c73f0d3af5a1d87fcc3ae562e2c90bdd7e06b724
5
5
  SHA512:
6
- metadata.gz: 57476b49adfb0e49f909c391420f6c7b1fdacd2fcf5aaced53443bd3c138ca6ca70ba047b261c9c20dc899a1a35e70950d3582f0dc267d33a2d5748dfd53aee1
7
- data.tar.gz: 46b4708db7b9cb68b3f1415edb0f07d5dcc49ba6d86831a51b96e1774abcea346aa3f7412e106572d32893f2473ff7ccddae0596470efad85a1c08a1d737b91c
6
+ metadata.gz: 2ed37d882ad062a8ae1dc02963582c57ed51383b7916176964a55ad4833ee34e93a98743382d527113554f8cef5774ef96e8e681396de00b11bfdcb4bda80bef
7
+ data.tar.gz: a000461e5ffe9649801875b2d0228d1a5d61510813713a90fe5cfb4aa594abaaa52e7f2554ec57113eb0b679712f39be469664ccbe82f0ed9afbb9c1f1324d8b
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/HISTORY.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # HISTORY
2
2
 
3
+ ## [v1.1.0 (Jul 29, 2013)](http://github.com/ms-ati/docile/compare/v1.0.5...v1.1.0)
4
+
5
+ - add functional-style DSL objects via `Docile#dsl_eval_immutable`
6
+
3
7
  ## [v1.0.5 (Jul 28, 2013)](http://github.com/ms-ati/docile/compare/v1.0.4...v1.0.5)
4
8
 
5
9
  - achieve 100% yard docs coverage
data/README.md CHANGED
@@ -1,19 +1,21 @@
1
1
  # Docile
2
-
3
- Definition: *Ready to accept control or instruction; submissive* [[1]]
4
-
5
- Tired of overly complex DSL libraries and hairy meta-programming?
6
-
7
- Let's make our Ruby DSLs more docile...
8
-
9
- [1]: http://www.google.com/search?q=docile+definition "Google"
10
-
11
2
  [![Gem Version](https://badge.fury.io/rb/docile.png)](http://badge.fury.io/rb/docile)
12
3
  [![Build Status](https://travis-ci.org/ms-ati/docile.png)](https://travis-ci.org/ms-ati/docile)
13
4
  [![Dependency Status](https://gemnasium.com/ms-ati/docile.png)](https://gemnasium.com/ms-ati/docile)
14
5
  [![Code Climate](https://codeclimate.com/github/ms-ati/docile.png)](https://codeclimate.com/github/ms-ati/docile)
15
6
  [![Coverage Status](https://coveralls.io/repos/ms-ati/docile/badge.png)](https://coveralls.io/r/ms-ati/docile)
16
7
 
8
+ Ruby makes it possible to create very expressive **Domain Specific
9
+ Languages**, or **DSL**'s for short. However, it requires some deep knowledge and
10
+ somewhat hairy meta-programming to get the interface just right.
11
+
12
+ "Docile" means *Ready to accept control or instruction; submissive* [[1]]
13
+
14
+ Instead of each Ruby project reinventing this wheel, let's make our Ruby DSL
15
+ coding a bit more docile...
16
+
17
+ [1]: http://www.google.com/search?q=docile+definition "Google"
18
+
17
19
  ## Basic Usage
18
20
 
19
21
  Let's say that we want to make a DSL for modifying Array objects.
@@ -26,12 +28,12 @@ with_array([]) do
26
28
  pop
27
29
  push 3
28
30
  end
29
- # => [1, 3]
31
+ #=> [1, 3]
30
32
  ```
31
33
 
32
34
  No problem, just define the method `with_array` like this:
33
35
 
34
- ``` ruby
36
+ ```ruby
35
37
  def with_array(arr=[], &block)
36
38
  Docile.dsl_eval(arr, &block)
37
39
  end
@@ -53,7 +55,7 @@ pizza do
53
55
  pepperoni
54
56
  sauce @sauce_level
55
57
  end
56
- # => #<Pizza:0x00001009dc398 @cheese=true, @pepperoni=true, @bacon=false, @sauce=:extra>
58
+ #=> #<Pizza:0x00001009dc398 @cheese=true, @pepperoni=true, @bacon=false, @sauce=:extra>
57
59
  ```
58
60
 
59
61
  And let's say we have a PizzaBuilder, which builds a Pizza like this:
@@ -83,7 +85,7 @@ def pizza(&block)
83
85
  end
84
86
  ```
85
87
 
86
- It's just that easy!
88
+ It's just that easy!
87
89
 
88
90
  [2]: http://stackoverflow.com/questions/328496/when-would-you-use-the-builder-pattern "Builder Pattern"
89
91
 
@@ -147,12 +149,49 @@ end
147
149
 
148
150
  [3]: http://www.sinatrarb.com "Sinatra"
149
151
 
152
+ ## Functional-Style DSL Objects
153
+
154
+ Sometimes, you want to use an object as a DSL, but it doesn't quite fit the
155
+ [imperative](http://en.wikipedia.org/wiki/Imperative_programming) pattern shown
156
+ above.
157
+
158
+ Instead of methods like
159
+ [Array#push](http://www.ruby-doc.org/core-2.0/Array.html#method-i-push), which
160
+ modifies the object at hand, it has methods like
161
+ [String#reverse](http://www.ruby-doc.org/core-2.0/String.html#method-i-reverse),
162
+ which returns a new object without touching the original. Perhaps it's even
163
+ [frozen](http://www.ruby-doc.org/core-2.0/Object.html#method-i-freeze) in
164
+ order to enforce [immutability](http://en.wikipedia.org/wiki/Immutable_object).
165
+
166
+ Wouldn't it be great if we could just treat these methods as a DSL as well?
167
+
168
+ ```ruby
169
+ with_immutable_string("I'm immutable!".freeze) do
170
+ reverse
171
+ upcase
172
+ end
173
+ #=> "!ELBATUMMI M'I"
174
+ ```
175
+
176
+ No problem, just define the method `with_immutable_string` like this:
177
+
178
+ ```ruby
179
+ def with_immutable_string(str="", &block)
180
+ Docile.dsl_eval_immutable(str, &block)
181
+ end
182
+ ```
183
+
184
+ All set!
185
+
150
186
  ## Features
151
187
 
152
- 1. method lookup falls back from the DSL object to the block's context
153
- 2. local variable lookup falls back from the DSL object to the block's context
154
- 3. instance variables are from the block's context only
155
- 4. nested dsl evaluation
188
+ 1. Method lookup falls back from the DSL object to the block's context
189
+ 2. Local variable lookup falls back from the DSL object to the block's
190
+ context
191
+ 3. Instance variables are from the block's context only
192
+ 4. Nested DSL evaluation, correctly chaining method and variable handling
193
+ from the inner to the outer DSL scopes
194
+ 5. Alternatives for both imperative and functional styles of DSL objects
156
195
 
157
196
  ## Installation
158
197
 
@@ -173,12 +212,14 @@ Version 1.0.x works on [all ruby versions since 1.8.7](https://github.com/ms-ati
173
212
  ## Note on Patches/Pull Requests
174
213
 
175
214
  * Fork the project.
176
- * Setup your development environment with: gem install bundler; bundle install
215
+ * Setup your development environment with:
216
+ `gem install bundler; bundle install`
177
217
  * Make your feature addition or bug fix.
178
- * Add tests for it. This is important so I don't break it in a
179
- future version unintentionally.
218
+ * Add tests for it. This is important so I don't break it in a future version
219
+ unintentionally.
180
220
  * Commit, do not mess with rakefile, version, or history.
181
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
221
+ (if you want to have your own version, that is fine but bump version in a
222
+ commit by itself I can ignore when I pull)
182
223
  * Send me a pull request. Bonus points for topic branches.
183
224
 
184
225
  ## Copyright
@@ -1,9 +1,12 @@
1
1
  require "docile/version"
2
+ require "docile/execution"
2
3
  require "docile/fallback_context_proxy"
4
+ require "docile/chaining_fallback_context_proxy"
3
5
 
4
- # Docile keeps your Ruby DSLs tame and well-behaved
5
- # @see http://ms-ati.github.com/docile/
6
+ # Docile keeps your Ruby DSLs tame and well-behaved.
6
7
  module Docile
8
+ extend Execution
9
+
7
10
  # Execute a block in the context of an object whose methods represent the
8
11
  # commands in a DSL.
9
12
  #
@@ -11,8 +14,9 @@ module Docile
11
14
  #
12
15
  # Use this method to execute an *imperative* DSL, which means that:
13
16
  #
14
- # 1. each command mutates the state of the DSL context object
15
- # 2. the return values of the commands are ignored
17
+ # 1. Each command mutates the state of the DSL context object
18
+ # 2. The return value of each command is ignored
19
+ # 3. The final return value is the original context object
16
20
  #
17
21
  # @example Use a String as a DSL
18
22
  # Docile.dsl_eval("Hello, world!") do
@@ -36,21 +40,45 @@ module Docile
36
40
  # `dsl` context object
37
41
  # @return [Object] the `dsl` context object after executing the block
38
42
  def dsl_eval(dsl, *args, &block)
39
- block_context = eval("self", block.binding)
40
- proxy_context = FallbackContextProxy.new(dsl, block_context)
41
- begin
42
- block_context.instance_variables.each do |ivar|
43
- value_from_block = block_context.instance_variable_get(ivar)
44
- proxy_context.instance_variable_set(ivar, value_from_block)
45
- end
46
- proxy_context.instance_exec(*args, &block)
47
- ensure
48
- block_context.instance_variables.each do |ivar|
49
- value_from_dsl_proxy = proxy_context.instance_variable_get(ivar)
50
- block_context.instance_variable_set(ivar, value_from_dsl_proxy)
51
- end
52
- end
43
+ exec_in_proxy_context(dsl, FallbackContextProxy, *args, &block)
53
44
  dsl
54
45
  end
55
46
  module_function :dsl_eval
47
+
48
+ # Execute a block in the context of an immutable object whose methods,
49
+ # and the methods of their return values, represent the commands in a DSL.
50
+ #
51
+ # @note Use with a *functional* DSL (commands return successor
52
+ # context objects)
53
+ #
54
+ # Use this method to execute a *functional* DSL, which means that:
55
+ #
56
+ # 1. The original DSL context object is never mutated
57
+ # 2. Each command returns the next DSL context object
58
+ # 3. The final return value is the value returned by the last command
59
+ #
60
+ # @example Use a frozen String as a DSL
61
+ # Docile.dsl_eval_immutable("I'm immutable!".freeze) do
62
+ # reverse
63
+ # upcase
64
+ # end
65
+ # #=> "!ELBATUMMI M'I"
66
+ #
67
+ # @example Use a Float as a DSL
68
+ # Docile.dsl_eval_immutable(84.5) do
69
+ # fdiv(2)
70
+ # floor
71
+ # end
72
+ # #=> 42
73
+ #
74
+ # @param dsl [Object] immutable context object whose methods make up the
75
+ # initial DSL
76
+ # @param args [Array] arguments to be passed to the block
77
+ # @yield the block of DSL commands to be executed against the
78
+ # `dsl` context object and successor return values
79
+ # @return [Object] the return value of the final command in the block
80
+ def dsl_eval_immutable(dsl, *args, &block)
81
+ exec_in_proxy_context(dsl, ChainingFallbackContextProxy, *args, &block)
82
+ end
83
+ module_function :dsl_eval_immutable
56
84
  end
@@ -0,0 +1,20 @@
1
+ require "docile/fallback_context_proxy"
2
+
3
+ module Docile
4
+ # @api private
5
+ #
6
+ # Operates in the same manner as {FallbackContextProxy}, but replacing
7
+ # the primary `receiver` object with the result of each proxied method.
8
+ #
9
+ # This is useful for implementing DSL evaluation for immutable context
10
+ # objects.
11
+ #
12
+ # @see Docile#dsl_eval_immutable
13
+ class ChainingFallbackContextProxy < FallbackContextProxy
14
+ # Proxy methods as in {FallbackContextProxy#method_missing}, replacing
15
+ # `receiver` with the returned value.
16
+ def method_missing(method, *args, &block)
17
+ @__receiver__ = super(method, *args, &block)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ module Docile
2
+ # @api private
3
+ #
4
+ # A namespace for functions relating to the execution of a block agsinst a
5
+ # proxy object.
6
+ module Execution
7
+ # Execute a block in the context of an object whose methods represent the
8
+ # commands in a DSL, using a specific proxy class.
9
+ #
10
+ # @param dsl [Object] context object whose methods make up the
11
+ # (initial) DSL
12
+ # @param proxy_type [FallbackContextProxy,
13
+ # ChainingFallbackContextProxy]
14
+ # class to instantiate as the proxy context
15
+ # @param args [Array] arguments to be passed to the block
16
+ # @yield the block of DSL commands to be executed
17
+ # @return [Object] the return value of the block
18
+ def exec_in_proxy_context(dsl, proxy_type, *args, &block)
19
+ block_context = eval("self", block.binding)
20
+ proxy_context = proxy_type.new(dsl, proxy_type.new(dsl, block_context))
21
+ begin
22
+ block_context.instance_variables.each do |ivar|
23
+ value_from_block = block_context.instance_variable_get(ivar)
24
+ proxy_context.instance_variable_set(ivar, value_from_block)
25
+ end
26
+ proxy_context.instance_exec(*args, &block)
27
+ ensure
28
+ block_context.instance_variables.each do |ivar|
29
+ value_from_dsl_proxy = proxy_context.instance_variable_get(ivar)
30
+ block_context.instance_variable_set(ivar, value_from_dsl_proxy)
31
+ end
32
+ end
33
+ end
34
+ module_function :exec_in_proxy_context
35
+ end
36
+ end
@@ -1,12 +1,18 @@
1
1
  require 'set'
2
2
 
3
3
  module Docile
4
+ # @api private
5
+ #
4
6
  # A proxy object with a primary receiver as well as a secondary
5
7
  # fallback receiver.
6
8
  #
7
9
  # Will attempt to forward all method calls first to the primary receiver,
8
10
  # and then to the fallback receiver if the primary does not handle that
9
11
  # method.
12
+ #
13
+ # This is useful for implementing DSL evaluation in the context of an object.
14
+ #
15
+ # @see Docile#dsl_eval
10
16
  class FallbackContextProxy
11
17
  # The set of methods which will **not** be proxied, but instead answered
12
18
  # by this object directly.
@@ -1,4 +1,4 @@
1
1
  module Docile
2
2
  # The current version of this library
3
- VERSION = "1.0.5"
3
+ VERSION = "1.1.0"
4
4
  end
@@ -2,24 +2,36 @@ require "spec_helper"
2
2
 
3
3
  describe Docile do
4
4
 
5
- context :dsl_eval do
5
+ describe "#dsl_eval" do
6
+
7
+ context "when DSL context object is an Array" do
8
+ let(:array) { [] }
9
+ let!(:result) { execute_dsl_against_array }
10
+
11
+ def execute_dsl_against_array
12
+ Docile.dsl_eval(array) do
13
+ push 1
14
+ push 2
15
+ pop
16
+ push 3
17
+ end
18
+ end
6
19
 
7
- it "should return the DSL object" do
8
- Docile.dsl_eval([]) do
9
- push 1
10
- push 2
11
- pop
12
- push 3
13
- end.should == [1, 3]
14
- end
20
+ it "executes the block against the DSL context object" do
21
+ array.should == [1, 3]
22
+ end
15
23
 
16
- it "should use the __id__ method of the proxy object" do
17
- arr = []
18
- Docile.dsl_eval(arr) { __id__.should_not == arr.__id__ }
19
- end
24
+ it "returns the DSL object after executing block against it" do
25
+ result.should == array
26
+ end
27
+
28
+ it "doesn't proxy #__id__" do
29
+ Docile.dsl_eval(array) { __id__.should_not == array.__id__ }
30
+ end
20
31
 
21
- it "should raise NoMethodError if the DSL object doesn't implement the method" do
22
- expect { Docile.dsl_eval([]) { no_such_method } }.to raise_error(NoMethodError)
32
+ it "raises NoMethodError if the DSL object doesn't implement the method" do
33
+ expect { Docile.dsl_eval(array) { no_such_method } }.to raise_error(NoMethodError)
34
+ end
23
35
  end
24
36
 
25
37
  Pizza = Struct.new(:cheese, :pepperoni, :bacon, :sauce)
@@ -34,13 +46,22 @@ describe Docile do
34
46
  end
35
47
  end
36
48
 
37
- it "should handle a Builder pattern" do
38
- @sauce = :extra
39
- Docile.dsl_eval(PizzaBuilder.new) do
40
- bacon
41
- cheese
42
- sauce @sauce
43
- end.build.should == Pizza.new(true, false, true, :extra)
49
+ context "when DSL context object is a Builder pattern" do
50
+ let(:builder) { PizzaBuilder.new }
51
+ let(:result) { execute_dsl_against_builder_and_call_build }
52
+
53
+ def execute_dsl_against_builder_and_call_build
54
+ @sauce = :extra
55
+ Docile.dsl_eval(builder) do
56
+ bacon
57
+ cheese
58
+ sauce @sauce
59
+ end.build
60
+ end
61
+
62
+ it "returns correctly built object" do
63
+ result.should == Pizza.new(true, false, true, :extra)
64
+ end
44
65
  end
45
66
 
46
67
  class InnerDSL
@@ -65,12 +86,12 @@ describe Docile do
65
86
  Docile.dsl_eval(OuterDSL.new, &block)
66
87
  end
67
88
 
68
- def parameterized(*args, &block)
69
- Docile.dsl_eval(OuterDSL.new, *args, &block)
70
- end
89
+ context "when given parameters for the DSL block" do
90
+ def parameterized(*args, &block)
91
+ Docile.dsl_eval(OuterDSL.new, *args, &block)
92
+ end
71
93
 
72
- context "parameters" do
73
- it "should pass parameters to the block" do
94
+ it "passes parameters to the block" do
74
95
  parameterized(1,2,3) do |x,y,z|
75
96
  x.should == 1
76
97
  y.should == 2
@@ -78,11 +99,11 @@ describe Docile do
78
99
  end
79
100
  end
80
101
 
81
- it "should find parameters before methods" do
102
+ it "finds parameters before methods" do
82
103
  parameterized(1) { |a| a.should == 1 }
83
104
  end
84
105
 
85
- it "should find outer parameters in inner dsl scope" do
106
+ it "find outer dsl parameters in inner dsl scope" do
86
107
  parameterized(1,2,3) do |a,b,c|
87
108
  inner_with_params(c) do |d,e|
88
109
  a.should == 1
@@ -95,140 +116,207 @@ describe Docile do
95
116
  end
96
117
  end
97
118
 
98
- context "methods" do
99
- it "should find method of outer dsl in outer dsl scope" do
100
- outer { a.should == 'a' }
101
- end
119
+ context "when DSL blocks are nested" do
102
120
 
103
- it "should find method of inner dsl in inner dsl scope" do
104
- outer { inner { b.should == 'b' } }
105
- end
121
+ context "method lookup" do
122
+ it "finds method of outer dsl in outer dsl scope" do
123
+ outer { a.should == 'a' }
124
+ end
106
125
 
107
- it "should find method of outer dsl in inner dsl scope" do
108
- outer { inner { a.should == 'a' } }
109
- end
126
+ it "finds method of inner dsl in inner dsl scope" do
127
+ outer { inner { b.should == 'b' } }
128
+ end
110
129
 
111
- it "should find method of block's context in outer dsl scope" do
112
- def c; 'c'; end
113
- outer { c.should == 'c' }
114
- end
130
+ it "finds method of outer dsl in inner dsl scope" do
131
+ outer { inner { a.should == 'a' } }
132
+ end
115
133
 
116
- it "should find method of block's context in inner dsl scope" do
117
- def c; 'c'; end
118
- outer { inner { c.should == 'c' } }
119
- end
134
+ it "finds method of block's context in outer dsl scope" do
135
+ def c; 'c'; end
136
+ outer { c.should == 'c' }
137
+ end
138
+
139
+ it "finds method of block's context in inner dsl scope" do
140
+ def c; 'c'; end
141
+ outer { inner { c.should == 'c' } }
142
+ end
120
143
 
121
- it "should find method of outer dsl in preference to block context" do
122
- def a; 'not a'; end
123
- outer { a.should == 'a' }
124
- outer { inner { a.should == 'a' } }
144
+ it "finds method of outer dsl in preference to block context" do
145
+ def a; 'not a'; end
146
+ outer { a.should == 'a' }
147
+ outer { inner { a.should == 'a' } }
148
+ end
125
149
  end
126
- end
127
150
 
128
- context "local variables" do
129
- it "should find local variable from block context in outer dsl scope" do
130
- foo = 'foo'
131
- outer { foo.should == 'foo' }
151
+ context "local variable lookup" do
152
+ it "finds local variable from block context in outer dsl scope" do
153
+ foo = 'foo'
154
+ outer { foo.should == 'foo' }
155
+ end
156
+
157
+ it "finds local variable from block definition in inner dsl scope" do
158
+ bar = 'bar'
159
+ outer { inner { bar.should == 'bar' } }
160
+ end
132
161
  end
133
162
 
134
- it "should find local variable from block definition in inner dsl scope" do
135
- bar = 'bar'
136
- outer { inner { bar.should == 'bar' } }
163
+ context "instance variable lookup" do
164
+ it "finds instance variable from block definition in outer dsl scope" do
165
+ @iv1 = 'iv1'; outer { @iv1.should == 'iv1' }
166
+ end
167
+
168
+ it "proxies instance variable assignments in block in outer dsl scope back into block's context" do
169
+ @iv1 = 'foo'; outer { @iv1 = 'bar' }; @iv1.should == 'bar'
170
+ end
171
+
172
+ it "finds instance variable from block definition in inner dsl scope" do
173
+ @iv2 = 'iv2'; outer { inner { @iv2.should == 'iv2' } }
174
+ end
175
+
176
+ it "proxies instance variable assignments in block in inner dsl scope back into block's context" do
177
+ @iv2 = 'foo'; outer { inner { @iv2 = 'bar' } }; @iv2.should == 'bar'
178
+ end
137
179
  end
180
+
138
181
  end
139
182
 
140
- context "instance variables" do
141
- it "should find instance variable from block definition in outer dsl scope" do
142
- @iv1 = 'iv1'; outer { @iv1.should == 'iv1' }
183
+ context "when DSL context object is a Dispatch pattern" do
184
+ class DispatchScope
185
+ def params
186
+ { :a => 1, :b => 2, :c => 3 }
187
+ end
143
188
  end
144
189
 
145
- it "should proxy instance variable assignments in block in outer dsl scope back into block's context" do
146
- @iv1 = 'foo'; outer { @iv1 = 'bar' }; @iv1.should == 'bar'
147
- end
190
+ class MessageDispatch
191
+ include Singleton
192
+
193
+ def initialize
194
+ @responders = {}
195
+ end
196
+
197
+ def add_responder path, &block
198
+ @responders[path] = block
199
+ end
148
200
 
149
- it "should find instance variable from block definition in inner dsl scope" do
150
- @iv2 = 'iv2'; outer { inner { @iv2.should == 'iv2' } }
201
+ def dispatch path, request
202
+ Docile.dsl_eval(DispatchScope.new, request, &@responders[path])
203
+ end
151
204
  end
152
205
 
153
- it "should proxy instance variable assignments in block in inner dsl scope back into block's context" do
154
- @iv2 = 'foo'; outer { inner { @iv2 = 'bar' } }; @iv2.should == 'bar'
206
+ def respond(path, &block)
207
+ MessageDispatch.instance.add_responder(path, &block)
155
208
  end
156
- end
157
209
 
158
- class DispatchScope
159
- def params
160
- { :a => 1, :b => 2, :c => 3 }
210
+ def send_request(path, request)
211
+ MessageDispatch.instance.dispatch(path, request)
161
212
  end
162
- end
163
213
 
164
- class MessageDispatch
165
- include Singleton
214
+ it "dispatches correctly" do
215
+ @first = @second = nil
166
216
 
167
- def initialize
168
- @responders = {}
169
- end
217
+ respond '/path' do |request|
218
+ @first = request
219
+ end
170
220
 
171
- def add_responder path, &block
172
- @responders[path] = block
173
- end
221
+ respond '/new_bike' do |bike|
222
+ @second = "Got a new #{bike}"
223
+ end
224
+
225
+ def x(y) ; "Got a #{y}"; end
226
+ respond '/third' do |third|
227
+ x(third).should == 'Got a third thing'
228
+ end
229
+
230
+ fourth = nil
231
+ respond '/params' do |arg|
232
+ fourth = params[arg]
233
+ end
234
+
235
+ send_request '/path', 1
236
+ send_request '/new_bike', 'ten speed'
237
+ send_request '/third', 'third thing'
238
+ send_request '/params', :b
174
239
 
175
- def dispatch path, request
176
- Docile.dsl_eval(DispatchScope.new, request, &@responders[path])
240
+ @first.should == 1
241
+ @second.should == 'Got a new ten speed'
242
+ fourth.should == 2
177
243
  end
178
- end
179
244
 
180
- def respond path, &block
181
- MessageDispatch.instance.add_responder path, &block
182
245
  end
183
246
 
184
- def send_request path, request
185
- MessageDispatch.instance.dispatch path, request
186
- end
247
+ end
187
248
 
188
- it "should handle the dispatch pattern" do
189
- @first = @second = nil
190
- respond '/path' do |request|
191
- @first = request
192
- end
249
+ describe "#dsl_eval_immutable" do
250
+
251
+ context "when DSL context object is a frozen String" do
252
+ let(:original) { "I'm immutable!".freeze }
253
+ let!(:result) { execute_non_mutating_dsl_against_string }
193
254
 
194
- respond '/new_bike' do |bike|
195
- @second = "Got a new #{bike}"
255
+ def execute_non_mutating_dsl_against_string
256
+ Docile.dsl_eval_immutable(original) do
257
+ reverse
258
+ upcase
259
+ end
196
260
  end
197
261
 
198
- def x(y) ; "Got a #{y}"; end
199
- respond '/third' do |third|
200
- x(third).should == 'Got a third thing'
262
+ it "doesn't modify the original string" do
263
+ original.should == "I'm immutable!"
201
264
  end
202
265
 
203
- fourth = nil
204
- respond '/params' do |arg|
205
- fourth = params[arg]
266
+ it "chains the commands in the block against the DSL context object" do
267
+ result.should == "!ELBATUMMI M'I"
206
268
  end
269
+ end
207
270
 
208
- send_request '/path', 1
209
- send_request '/new_bike', 'ten speed'
210
- send_request '/third', 'third thing'
211
- send_request '/params', :b
271
+ context "when DSL context object is a number" do
272
+ let(:original) { 84.5 }
273
+ let!(:result) { execute_non_mutating_dsl_against_number }
212
274
 
213
- @first.should == 1
214
- @second.should == 'Got a new ten speed'
215
- fourth.should == 2
216
- end
275
+ def execute_non_mutating_dsl_against_number
276
+ Docile.dsl_eval_immutable(original) do
277
+ fdiv(2)
278
+ floor
279
+ end
280
+ end
217
281
 
282
+ it "chains the commands in the block against the DSL context object" do
283
+ result.should == 42
284
+ end
285
+ end
218
286
  end
219
287
 
220
- context "FallbackContextProxy#instance_variables" do
288
+ end
221
289
 
222
- it "should preserve the class (String or Symbol) normally returned by current ruby version" do
223
- @a = 1
224
- expected_type = instance_variables.first.class
290
+ describe Docile::FallbackContextProxy do
291
+
292
+ describe "#instance_variables" do
293
+ subject { create_fcp_and_set_one_instance_variable.instance_variables }
294
+ let(:expected_type_of_names) { type_of_ivar_names_on_this_ruby }
295
+ let(:actual_type_of_names) { subject.first.class }
296
+ let(:excluded) { Docile::FallbackContextProxy::NON_PROXIED_INSTANCE_VARIABLES }
225
297
 
298
+ def create_fcp_and_set_one_instance_variable
226
299
  fcp = Docile::FallbackContextProxy.new(nil, nil)
227
300
  fcp.instance_variable_set(:@foo, "foo")
301
+ fcp
302
+ end
228
303
 
229
- fcp.instance_variables.first.class.should == expected_type
304
+ def type_of_ivar_names_on_this_ruby
305
+ @a = 1
306
+ instance_variables.first.class
230
307
  end
231
308
 
309
+ it "returns proxied instance variables" do
310
+ subject.map(&:to_sym).should include(:@foo)
311
+ end
312
+
313
+ it "doesn't return non-proxied instance variables" do
314
+ subject.map(&:to_sym).should_not include(*excluded)
315
+ end
316
+
317
+ it "preserves the type (String or Symbol) of names on this ruby version" do
318
+ actual_type_of_names.should == expected_type_of_names
319
+ end
232
320
  end
233
321
 
234
322
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docile
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Siegel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-28 00:00:00.000000000 Z
11
+ date: 2013-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -104,6 +104,7 @@ extra_rdoc_files: []
104
104
  files:
105
105
  - .coveralls.yml
106
106
  - .gitignore
107
+ - .rspec
107
108
  - .ruby-gemset
108
109
  - .ruby-version
109
110
  - .travis.yml
@@ -115,6 +116,8 @@ files:
115
116
  - Rakefile
116
117
  - docile.gemspec
117
118
  - lib/docile.rb
119
+ - lib/docile/chaining_fallback_context_proxy.rb
120
+ - lib/docile/execution.rb
118
121
  - lib/docile/fallback_context_proxy.rb
119
122
  - lib/docile/version.rb
120
123
  - spec/docile_spec.rb