docile 1.1.5 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +3 -1
- data/.travis.yml +21 -9
- data/Gemfile +9 -1
- data/HISTORY.md +28 -0
- data/LICENSE +1 -1
- data/README.md +152 -12
- data/Rakefile +12 -12
- data/docile.gemspec +40 -28
- data/lib/docile.rb +43 -4
- data/lib/docile/chaining_fallback_context_proxy.rb +2 -2
- data/lib/docile/execution.rb +8 -2
- data/lib/docile/fallback_context_proxy.rb +32 -2
- data/lib/docile/version.rb +1 -1
- data/on_what.rb +16 -4
- metadata +23 -28
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/spec/docile_spec.rb +0 -339
- data/spec/spec_helper.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d7dae1c209d04576cf98b4d080a44f659eaf7ed41dccf942bec2ff95c44db5e8
|
4
|
+
data.tar.gz: 3209a17a2588ba8c873fddd9b5c219ea6e7b73d3fcb3575997b347480abc622a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 714bafc9545d9ccc0037fdb5c9fa8ff016a6f5f3df3c758ca16b225e371758cb3b00f2afb85763eef87576686192b129a53f1b21e2ee4f34089bdd6ad58ffd54
|
7
|
+
data.tar.gz: bb82951b411c16cd720bdf9c359f43f4e38c9fa71815d336ee45bc240eeb7d9e2de1922dacdaad84ebfffdfdb54a25bf023302a0fe487a1507d102d9e59dfcae
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,21 +1,33 @@
|
|
1
1
|
language: ruby
|
2
|
-
|
2
|
+
|
3
|
+
# Apparently sudo is required to test on Rubinius and JRuby-head
|
4
|
+
sudo: required
|
5
|
+
|
6
|
+
# See https://docs.travis-ci.com/user/languages/ruby/#Rubinius
|
7
|
+
dist: trusty
|
8
|
+
|
3
9
|
rvm:
|
10
|
+
# MRI
|
4
11
|
- ruby-head
|
5
|
-
- 2.
|
6
|
-
- 2.
|
7
|
-
- 2.
|
8
|
-
- 2.
|
12
|
+
- 2.6
|
13
|
+
- 2.5
|
14
|
+
- 2.4
|
15
|
+
- 2.3
|
16
|
+
- 2.2
|
17
|
+
- 2.1
|
9
18
|
- 1.9.3
|
10
|
-
- 1.9.2
|
11
19
|
- 1.8.7
|
12
20
|
- ree
|
21
|
+
# JRuby
|
13
22
|
- jruby-head
|
14
|
-
- jruby-
|
15
|
-
- jruby-
|
16
|
-
|
23
|
+
- jruby-9.1
|
24
|
+
- jruby-9.0
|
25
|
+
# Rubinius
|
26
|
+
- rubinius-3
|
27
|
+
|
17
28
|
matrix:
|
18
29
|
allow_failures:
|
19
30
|
- rvm: ruby-head
|
20
31
|
- rvm: jruby-head
|
32
|
+
- rvm: rubinius-3
|
21
33
|
fast_finish: true
|
data/Gemfile
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
-
|
1
|
+
require File.expand_path("on_what", File.dirname(__FILE__))
|
2
|
+
source "https://rubygems.org"
|
3
|
+
|
4
|
+
# Travis-only dependencies go here
|
5
|
+
if on_travis? && !on_1_8? && !on_rubinius?
|
6
|
+
group :test do
|
7
|
+
gem "codecov", ">= 0.0.9", :require => false
|
8
|
+
end
|
9
|
+
end
|
2
10
|
|
3
11
|
# Specify gem's dependencies in docile.gemspec
|
4
12
|
gemspec
|
data/HISTORY.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# HISTORY
|
2
2
|
|
3
|
+
## [Unreleased changes](http://github.com/ms-ati/docile/compare/v1.3.2...master)
|
4
|
+
|
5
|
+
- ...
|
6
|
+
|
7
|
+
## [v1.3.2 (Jun 12, 2019)](http://github.com/ms-ati/docile/compare/v1.3.1...v1.3.2)
|
8
|
+
|
9
|
+
- Special thanks (again!) to Taichi Ishitani (@taichi-ishitani):
|
10
|
+
- Fix for DSL object is replaced when #dsl_eval is nested (#33, PR #34)
|
11
|
+
|
12
|
+
## [v1.3.1 (May 24, 2018)](http://github.com/ms-ati/docile/compare/v1.3.0...v1.3.1)
|
13
|
+
|
14
|
+
- Special thanks to Taichi Ishitani (@taichi-ishitani):
|
15
|
+
- Fix for when DSL object is also the block's context (#30)
|
16
|
+
|
17
|
+
## [v1.3.0 (Feb 7, 2018)](http://github.com/ms-ati/docile/compare/v1.2.0...v1.3.0)
|
18
|
+
|
19
|
+
- Allow helper methods in block's context to call DSL methods
|
20
|
+
- Add SemVer release policy explicitly
|
21
|
+
- Standardize on double-quoted string literals
|
22
|
+
- Workaround some more Travis CI shenanigans
|
23
|
+
|
24
|
+
## [v1.2.0 (Jan 11, 2018)](http://github.com/ms-ati/docile/compare/v1.1.5...v1.2.0)
|
25
|
+
|
26
|
+
- Special thanks to Christina Koller (@cmkoller)
|
27
|
+
- add DSL evaluation returning *return value of the block* (see `.dsl_eval_with_block_return`)
|
28
|
+
- add an example to README
|
29
|
+
- keep travis builds passing on old ruby versions
|
30
|
+
|
3
31
|
## [v1.1.5 (Jun 15, 2014)](http://github.com/ms-ati/docile/compare/v1.1.4...v1.1.5)
|
4
32
|
|
5
33
|
- as much as possible, loosen version restrictions on development dependencies
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
# Docile
|
2
|
-
|
3
|
-
[](https://rubygems.org/gems/docile)
|
4
|
+
[](https://rubygems.org/gems/docile)
|
5
|
+
|
6
|
+
[](https://gitter.im/ms-ati/docile?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
7
|
+
[](http://rubydoc.info/github/ms-ati/docile)
|
8
|
+
[](http://inch-ci.org/github/ms-ati/docile)
|
9
|
+
|
10
|
+
[](https://travis-ci.org/ms-ati/docile)
|
11
|
+
[](https://codecov.io/github/ms-ati/docile)
|
12
|
+
[](https://codeclimate.com/github/ms-ati/docile/maintainability)
|
9
13
|
|
10
14
|
Ruby makes it possible to create very expressive **Domain Specific
|
11
15
|
Languages**, or **DSL**'s for short. However, it requires some deep knowledge and
|
@@ -20,7 +24,7 @@ coding a bit more docile...
|
|
20
24
|
|
21
25
|
## Usage
|
22
26
|
|
23
|
-
### Basic
|
27
|
+
### Basic: Ruby [Array](http://ruby-doc.org/core-2.2.2/Array.html) as DSL
|
24
28
|
|
25
29
|
Let's say that we want to make a DSL for modifying Array objects.
|
26
30
|
Wouldn't it be great if we could just treat the methods of Array as a DSL?
|
@@ -45,7 +49,80 @@ end
|
|
45
49
|
|
46
50
|
Easy!
|
47
51
|
|
48
|
-
###
|
52
|
+
### Next step: Allow helper methods to call DSL methods
|
53
|
+
|
54
|
+
What if, in our use of the methods of Array as a DSL, we want to extract
|
55
|
+
helper methods which in turn call DSL methods?
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
def pop_sum_and_push(n)
|
59
|
+
sum = 0
|
60
|
+
n.times { sum += pop }
|
61
|
+
push sum
|
62
|
+
end
|
63
|
+
|
64
|
+
Docile.dsl_eval([]) do
|
65
|
+
push 5
|
66
|
+
push 6
|
67
|
+
pop_sum_and_push(2)
|
68
|
+
end
|
69
|
+
#=> [11]
|
70
|
+
```
|
71
|
+
|
72
|
+
Without Docile, you may find this sort of code extraction to be more
|
73
|
+
challenging.
|
74
|
+
|
75
|
+
### Wait! Can't I do that with just `instance_eval` or `instance_exec`?
|
76
|
+
|
77
|
+
Good question!
|
78
|
+
|
79
|
+
In short: **No**.
|
80
|
+
|
81
|
+
Not if you want the code in the block to be able to refer to anything
|
82
|
+
the block would normally have access to from the surrounding context.
|
83
|
+
|
84
|
+
Let's be very specific. Docile internally uses `instance_exec` (see [execution.rb#26](lib/docile/execution.rb#L26)), adding a small layer to support referencing *local variables*, *instance variables*, and *methods* from the _block's context_ **or** the target _object's context_, interchangeably. This is "**the hard part**", where most folks making a DSL in Ruby throw up their hands.
|
85
|
+
|
86
|
+
For example:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
class ContextOfBlock
|
90
|
+
def example_of_contexts
|
91
|
+
@block_instance_var = 1
|
92
|
+
block_local_var = 2
|
93
|
+
|
94
|
+
with_array do
|
95
|
+
push @block_instance_var
|
96
|
+
push block_local_var
|
97
|
+
pop
|
98
|
+
push block_sees_this_method
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def block_sees_this_method
|
103
|
+
3
|
104
|
+
end
|
105
|
+
|
106
|
+
def with_array(&block)
|
107
|
+
{
|
108
|
+
docile: Docile.dsl_eval([], &block),
|
109
|
+
instance_eval: ([].instance_eval(&block) rescue $!),
|
110
|
+
instance_exec: ([].instance_exec(&block) rescue $!)
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
ContextOfBlock.new.example_of_contexts
|
116
|
+
#=> {
|
117
|
+
:docile=>[1, 3],
|
118
|
+
:instance_eval=>#<NameError: undefined local variable or method `block_sees_this_method' for [nil]:Array>,
|
119
|
+
:instance_exec=>#<NameError: undefined local variable or method `block_sees_this_method' for [nil]:Array>
|
120
|
+
}
|
121
|
+
```
|
122
|
+
|
123
|
+
As you can see, it won't be possible to call methods or access instance variables defined in the block's context using just the raw `instance_eval` or `instance_exec` methods. And in fact, Docile goes further, making it easy to maintain this support even in multi-layered DSLs.
|
124
|
+
|
125
|
+
### Build a Pizza
|
49
126
|
|
50
127
|
Mutating (changing) an Array instance is fine, but what usually makes a good DSL is a [Builder Pattern][2].
|
51
128
|
|
@@ -83,7 +160,7 @@ PizzaBuilder.new.cheese.pepperoni.sauce(:extra).build
|
|
83
160
|
|
84
161
|
Then implement your DSL like this:
|
85
162
|
|
86
|
-
```
|
163
|
+
```ruby
|
87
164
|
def pizza(&block)
|
88
165
|
Docile.dsl_eval(PizzaBuilder.new, &block).build
|
89
166
|
end
|
@@ -93,6 +170,38 @@ It's just that easy!
|
|
93
170
|
|
94
171
|
[2]: http://stackoverflow.com/questions/328496/when-would-you-use-the-builder-pattern "Builder Pattern"
|
95
172
|
|
173
|
+
### Multi-level and Recursive DSLs
|
174
|
+
|
175
|
+
Docile is a very easy way to write a multi-level DSL in Ruby, even for
|
176
|
+
a [recursive data structure such as a tree][4]:
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
Person = Struct.new(:name, :mother, :father)
|
180
|
+
|
181
|
+
person {
|
182
|
+
name 'John Smith'
|
183
|
+
mother {
|
184
|
+
name 'Mary Smith'
|
185
|
+
}
|
186
|
+
father {
|
187
|
+
name 'Tom Smith'
|
188
|
+
mother {
|
189
|
+
name 'Jane Smith'
|
190
|
+
}
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
#=> #<struct Person name="John Smith",
|
195
|
+
# mother=#<struct Person name="Mary Smith", mother=nil, father=nil>,
|
196
|
+
# father=#<struct Person name="Tom Smith",
|
197
|
+
# mother=#<struct Person name="Jane Smith", mother=nil, father=nil>,
|
198
|
+
# father=nil>>
|
199
|
+
```
|
200
|
+
|
201
|
+
See the full [person tree example][4] for details.
|
202
|
+
|
203
|
+
[4]: https://gist.github.com/ms-ati/2bb17bdf10a430faba98
|
204
|
+
|
96
205
|
### Block parameters
|
97
206
|
|
98
207
|
Parameters can be passed to the DSL block.
|
@@ -153,7 +262,7 @@ end
|
|
153
262
|
|
154
263
|
[3]: http://www.sinatrarb.com "Sinatra"
|
155
264
|
|
156
|
-
### Functional-Style DSL Objects
|
265
|
+
### Functional-Style Immutable DSL Objects
|
157
266
|
|
158
267
|
Sometimes, you want to use an object as a DSL, but it doesn't quite fit the
|
159
268
|
[imperative](http://en.wikipedia.org/wiki/Imperative_programming) pattern shown
|
@@ -192,6 +301,33 @@ end
|
|
192
301
|
|
193
302
|
All set!
|
194
303
|
|
304
|
+
### Accessing the block's return value
|
305
|
+
|
306
|
+
Sometimes you might want to access the return value of your provided block,
|
307
|
+
as opposed to the DSL object itself. In these cases, use
|
308
|
+
`dsl_eval_with_block_return`. It behaves exactly like `dsl_eval`, but returns
|
309
|
+
the output from executing the block, rather than the DSL object.
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
arr = []
|
313
|
+
with_array(arr) do
|
314
|
+
push "a"
|
315
|
+
push "b"
|
316
|
+
push "c"
|
317
|
+
length
|
318
|
+
end
|
319
|
+
#=> 3
|
320
|
+
|
321
|
+
arr
|
322
|
+
#=> ["a", "b", "c"]
|
323
|
+
```
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
def with_array(arr=[], &block)
|
327
|
+
Docile.dsl_eval_with_block_return(arr, &block)
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
195
331
|
## Features
|
196
332
|
|
197
333
|
1. Method lookup falls back from the DSL object to the block's context
|
@@ -219,6 +355,10 @@ Works on [all ruby versions since 1.8.7](https://github.com/ms-ati/docile/blob/m
|
|
219
355
|
|
220
356
|
Used by some pretty cool gems to implement their DSLs, notably including [SimpleCov](https://github.com/colszowka/simplecov). Keep an eye out for new gems using Docile at the [Ruby Toolbox](https://www.ruby-toolbox.com/projects/docile).
|
221
357
|
|
358
|
+
## Release Policy
|
359
|
+
|
360
|
+
Docile releases follow [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
|
361
|
+
|
222
362
|
## Note on Patches/Pull Requests
|
223
363
|
|
224
364
|
* Fork the project.
|
@@ -234,7 +374,7 @@ Used by some pretty cool gems to implement their DSLs, notably including [Simple
|
|
234
374
|
|
235
375
|
## Copyright & License
|
236
376
|
|
237
|
-
Copyright (c) 2012-
|
377
|
+
Copyright (c) 2012-2019 Marc Siegel.
|
238
378
|
|
239
379
|
Licensed under the [MIT License](http://choosealicense.com/licenses/mit/), see [LICENSE](LICENSE) for details.
|
240
380
|
|
data/Rakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require File.expand_path(
|
1
|
+
require "rake/clean"
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
require File.expand_path("on_what", File.dirname(__FILE__))
|
5
5
|
|
6
6
|
# Default task for `rake` is to run rspec
|
7
7
|
task :default => [:spec]
|
@@ -10,19 +10,19 @@ task :default => [:spec]
|
|
10
10
|
RSpec::Core::RakeTask.new
|
11
11
|
|
12
12
|
# Configure `rake clobber` to delete all generated files
|
13
|
-
CLOBBER.include(
|
13
|
+
CLOBBER.include("pkg", "doc", "coverage")
|
14
14
|
|
15
15
|
# To limit needed compatibility with versions of dependencies, only configure
|
16
|
-
# yard doc generation when *not* on Travis, JRuby, or
|
17
|
-
if !on_travis? && !on_jruby? && !
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
16
|
+
# yard doc generation when *not* on Travis, JRuby, or < 2.0
|
17
|
+
if !on_travis? && !on_jruby? && !on_less_than_2_0?
|
18
|
+
require "github/markup"
|
19
|
+
require "redcarpet"
|
20
|
+
require "yard"
|
21
|
+
require "yard/rake/yardoc_task"
|
22
22
|
|
23
23
|
YARD::Rake::YardocTask.new do |t|
|
24
24
|
OTHER_PATHS = %w()
|
25
|
-
t.files = [
|
25
|
+
t.files = ["lib/**/*.rb", OTHER_PATHS]
|
26
26
|
t.options = %w(--markup-provider=redcarpet --markup=markdown --main=README.md)
|
27
27
|
end
|
28
28
|
end
|
data/docile.gemspec
CHANGED
@@ -1,43 +1,55 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require File.expand_path("on_what", File.dirname(__FILE__))
|
3
|
+
require "docile/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
|
-
s.name =
|
6
|
+
s.name = "docile"
|
7
7
|
s.version = Docile::VERSION
|
8
|
-
s.author =
|
9
|
-
s.email =
|
10
|
-
s.homepage =
|
11
|
-
s.summary =
|
12
|
-
s.description =
|
13
|
-
|
8
|
+
s.author = "Marc Siegel"
|
9
|
+
s.email = "marc@usainnov.com"
|
10
|
+
s.homepage = "https://ms-ati.github.io/docile/"
|
11
|
+
s.summary = "Docile keeps your Ruby DSLs tame and well-behaved."
|
12
|
+
s.description = "Docile treats the methods of a given ruby object as a DSL " \
|
13
|
+
"(domain specific language) within a given block. \n\n" \
|
14
|
+
"Killer feature: you can also reference methods, instance " \
|
15
|
+
"variables, and local variables from the original (non-DSL) "\
|
16
|
+
"context within the block. \n\n" \
|
17
|
+
"Docile releases follow Semantic Versioning as defined at " \
|
18
|
+
"semver.org."
|
19
|
+
s.license = "MIT"
|
14
20
|
|
15
21
|
# Files included in the gem
|
16
|
-
s.files = `git ls-files`.split("\
|
17
|
-
|
18
|
-
|
19
|
-
s.require_paths =
|
22
|
+
s.files = `git ls-files -z`.split("\x0").reject do |f|
|
23
|
+
f.match(%r{^(test|spec|features)/})
|
24
|
+
end
|
25
|
+
s.require_paths = ["lib"]
|
20
26
|
|
21
27
|
# Specify oldest supported Ruby version
|
22
|
-
s.required_ruby_version =
|
28
|
+
s.required_ruby_version = ">= 1.8.7"
|
29
|
+
|
30
|
+
# Run rspec tests from rake even on old Ruby versions
|
31
|
+
s.add_development_dependency "rake", "~> 10.5" if on_less_than_1_9_3? # Pin compatible rake on old rubies, see: https://github.com/travis-ci/travis.rb/issues/380
|
32
|
+
s.add_development_dependency "rake", "< 11.0" unless on_less_than_1_9_3? # See http://stackoverflow.com/questions/35893584/nomethoderror-undefined-method-last-comment-after-upgrading-to-rake-11
|
33
|
+
s.add_development_dependency "rspec", "~> 3.0"
|
34
|
+
s.add_development_dependency "rspec-expectations", "!= 3.8.3" # Workaround for RSpec's issue, see: https://github.com/rspec/rspec-expectations/issues/1111
|
23
35
|
|
24
|
-
# Run
|
25
|
-
|
26
|
-
|
36
|
+
# Run code coverage where possible - not on Rubinius
|
37
|
+
unless on_rubinius?
|
38
|
+
# Pin versions for Travis builds on 1.9
|
39
|
+
s.add_development_dependency "json", "< 2.0" if on_less_than_2_0?
|
27
40
|
|
28
|
-
|
29
|
-
|
41
|
+
# Pin versions for Travis builds on 1.8
|
42
|
+
s.add_development_dependency "mime-types" , "~> 1.25.1" if on_1_8?
|
43
|
+
s.add_development_dependency "rest-client", "~> 1.6.8" if on_1_8?
|
44
|
+
end
|
30
45
|
|
31
46
|
# To limit needed compatibility with versions of dependencies, only configure
|
32
|
-
# yard doc generation when *not* on Travis, JRuby, or
|
33
|
-
if !on_travis? && !on_jruby? && !
|
47
|
+
# yard doc generation when *not* on Travis, JRuby, Rubinius, or < 2.0
|
48
|
+
if !on_travis? && !on_jruby? && !on_rubinius? && !on_less_than_2_0?
|
34
49
|
# Github flavored markdown in YARD documentation
|
35
50
|
# http://blog.nikosd.com/2011/11/github-flavored-markdown-in-yard.html
|
36
|
-
s.add_development_dependency
|
37
|
-
s.add_development_dependency
|
38
|
-
s.add_development_dependency
|
51
|
+
s.add_development_dependency "yard"
|
52
|
+
s.add_development_dependency "redcarpet"
|
53
|
+
s.add_development_dependency "github-markup"
|
39
54
|
end
|
40
|
-
|
41
|
-
# Coveralls test coverage tool, basically hosted SimpleCov
|
42
|
-
s.add_development_dependency 'coveralls'
|
43
55
|
end
|
data/lib/docile.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "docile/version"
|
2
|
+
require "docile/execution"
|
3
|
+
require "docile/fallback_context_proxy"
|
4
|
+
require "docile/chaining_fallback_context_proxy"
|
5
5
|
|
6
6
|
# Docile keeps your Ruby DSLs tame and well-behaved.
|
7
7
|
module Docile
|
@@ -45,6 +45,45 @@ module Docile
|
|
45
45
|
end
|
46
46
|
module_function :dsl_eval
|
47
47
|
|
48
|
+
# Execute a block in the context of an object whose methods represent the
|
49
|
+
# commands in a DSL, and return *the block's return value*.
|
50
|
+
#
|
51
|
+
# @note Use with an *imperative* DSL (commands modify the context object)
|
52
|
+
#
|
53
|
+
# Use this method to execute an *imperative* DSL, which means that:
|
54
|
+
#
|
55
|
+
# 1. Each command mutates the state of the DSL context object
|
56
|
+
# 2. The return value of each command is ignored
|
57
|
+
# 3. The final return value is the original context object
|
58
|
+
#
|
59
|
+
# @example Use a String as a DSL
|
60
|
+
# Docile.dsl_eval_with_block_return("Hello, world!") do
|
61
|
+
# reverse!
|
62
|
+
# upcase!
|
63
|
+
# first
|
64
|
+
# end
|
65
|
+
# #=> "!"
|
66
|
+
#
|
67
|
+
# @example Use an Array as a DSL
|
68
|
+
# Docile.dsl_eval_with_block_return([]) do
|
69
|
+
# push "a"
|
70
|
+
# push "b"
|
71
|
+
# pop
|
72
|
+
# push "c"
|
73
|
+
# length
|
74
|
+
# end
|
75
|
+
# #=> 2
|
76
|
+
#
|
77
|
+
# @param dsl [Object] context object whose methods make up the DSL
|
78
|
+
# @param args [Array] arguments to be passed to the block
|
79
|
+
# @param block [Proc] the block of DSL commands to be executed against the
|
80
|
+
# `dsl` context object
|
81
|
+
# @return [Object] the return value from executing the block
|
82
|
+
def dsl_eval_with_block_return(dsl, *args, &block)
|
83
|
+
exec_in_proxy_context(dsl, FallbackContextProxy, *args, &block)
|
84
|
+
end
|
85
|
+
module_function :dsl_eval_with_block_return
|
86
|
+
|
48
87
|
# Execute a block in the context of an immutable object whose methods,
|
49
88
|
# and the methods of their return values, represent the commands in a DSL.
|
50
89
|
#
|
data/lib/docile/execution.rb
CHANGED
@@ -15,16 +15,22 @@ module Docile
|
|
15
15
|
# @param block [Proc] the block of DSL commands to be executed
|
16
16
|
# @return [Object] the return value of the block
|
17
17
|
def exec_in_proxy_context(dsl, proxy_type, *args, &block)
|
18
|
-
block_context = eval(
|
18
|
+
block_context = eval("self", block.binding)
|
19
19
|
proxy_context = proxy_type.new(dsl, block_context)
|
20
20
|
begin
|
21
21
|
block_context.instance_variables.each do |ivar|
|
22
22
|
value_from_block = block_context.instance_variable_get(ivar)
|
23
23
|
proxy_context.instance_variable_set(ivar, value_from_block)
|
24
24
|
end
|
25
|
+
|
25
26
|
proxy_context.instance_exec(*args, &block)
|
26
27
|
ensure
|
28
|
+
if block_context.respond_to?(:__docile_undo_fallback__)
|
29
|
+
block_context.send(:__docile_undo_fallback__)
|
30
|
+
end
|
31
|
+
|
27
32
|
block_context.instance_variables.each do |ivar|
|
33
|
+
next unless proxy_context.instance_variables.include?(ivar)
|
28
34
|
value_from_dsl_proxy = proxy_context.instance_variable_get(ivar)
|
29
35
|
block_context.instance_variable_set(ivar, value_from_dsl_proxy)
|
30
36
|
end
|
@@ -32,4 +38,4 @@ module Docile
|
|
32
38
|
end
|
33
39
|
module_function :exec_in_proxy_context
|
34
40
|
end
|
35
|
-
end
|
41
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "set"
|
2
2
|
|
3
3
|
module Docile
|
4
4
|
# @api private
|
@@ -17,10 +17,14 @@ module Docile
|
|
17
17
|
# The set of methods which will **not** be proxied, but instead answered
|
18
18
|
# by this object directly.
|
19
19
|
NON_PROXIED_METHODS = Set[:__send__, :object_id, :__id__, :==, :equal?,
|
20
|
-
:
|
20
|
+
:"!", :"!=", :instance_exec, :instance_variables,
|
21
21
|
:instance_variable_get, :instance_variable_set,
|
22
22
|
:remove_instance_variable]
|
23
23
|
|
24
|
+
# The set of methods which will **not** fallback from the block's context
|
25
|
+
# to the dsl object.
|
26
|
+
NON_FALLBACK_METHODS = Set[:class, :self, :respond_to?, :instance_of?]
|
27
|
+
|
24
28
|
# The set of instance variables which are local to this object and hidden.
|
25
29
|
# All other instance variables will be copied in and out of this object
|
26
30
|
# from the scope in which this proxy was created.
|
@@ -38,6 +42,32 @@ module Docile
|
|
38
42
|
def initialize(receiver, fallback)
|
39
43
|
@__receiver__ = receiver
|
40
44
|
@__fallback__ = fallback
|
45
|
+
|
46
|
+
# Enables calling DSL methods from helper methods in the block's context
|
47
|
+
unless fallback.respond_to?(:method_missing)
|
48
|
+
# NOTE: There's no {#define_singleton_method} on Ruby 1.8.x
|
49
|
+
singleton_class = (class << fallback; self; end)
|
50
|
+
|
51
|
+
# instrument {#method_missing} on the block's context to fallback to
|
52
|
+
# the DSL object. This allows helper methods in the block's context to
|
53
|
+
# contain calls to methods on the DSL object.
|
54
|
+
singleton_class.
|
55
|
+
send(:define_method, :method_missing) do |method, *args, &block|
|
56
|
+
m = method.to_sym
|
57
|
+
if !NON_FALLBACK_METHODS.include?(m) && !fallback.respond_to?(m) && receiver.respond_to?(m)
|
58
|
+
receiver.__send__(method.to_sym, *args, &block)
|
59
|
+
else
|
60
|
+
super(method, *args, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# instrument a helper method to remove the above instrumentation
|
65
|
+
singleton_class.
|
66
|
+
send(:define_method, :__docile_undo_fallback__) do
|
67
|
+
singleton_class.send(:remove_method, :method_missing)
|
68
|
+
singleton_class.send(:remove_method, :__docile_undo_fallback__)
|
69
|
+
end
|
70
|
+
end
|
41
71
|
end
|
42
72
|
|
43
73
|
# @return [Array<Symbol>] Instance variable names, excluding
|
data/lib/docile/version.rb
CHANGED
data/on_what.rb
CHANGED
@@ -2,13 +2,25 @@
|
|
2
2
|
# between Rakefile, gemspec, and spec_helper. Not for use in actual library.
|
3
3
|
|
4
4
|
def on_travis?
|
5
|
-
ENV[
|
5
|
+
ENV["CI"] == "true"
|
6
6
|
end
|
7
7
|
|
8
8
|
def on_jruby?
|
9
|
-
|
9
|
+
defined?(RUBY_ENGINE) && "jruby" == RUBY_ENGINE
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_rubinius?
|
13
|
+
defined?(RUBY_ENGINE) && "rbx" == RUBY_ENGINE
|
10
14
|
end
|
11
15
|
|
12
16
|
def on_1_8?
|
13
|
-
RUBY_VERSION.start_with?
|
14
|
-
end
|
17
|
+
RUBY_VERSION.start_with? "1.8"
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_less_than_1_9_3?
|
21
|
+
RUBY_VERSION < "1.9.3"
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_less_than_2_0?
|
25
|
+
RUBY_VERSION < "2.0.0"
|
26
|
+
end
|
metadata
CHANGED
@@ -1,59 +1,59 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: docile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc Siegel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "<"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
19
|
+
version: '11.0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "<"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
26
|
+
version: '11.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 3.0
|
33
|
+
version: '3.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 3.0
|
40
|
+
version: '3.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rspec-expectations
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "!="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 3.8.3
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "!="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 3.8.3
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: yard
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: redcarpet
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: github-markup
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
@@ -94,8 +94,10 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
description: Docile
|
98
|
-
|
97
|
+
description: "Docile treats the methods of a given ruby object as a DSL (domain specific
|
98
|
+
language) within a given block. \n\nKiller feature: you can also reference methods,
|
99
|
+
instance variables, and local variables from the original (non-DSL) context within
|
100
|
+
the block. \n\nDocile releases follow Semantic Versioning as defined at semver.org."
|
99
101
|
email: marc@usainnov.com
|
100
102
|
executables: []
|
101
103
|
extensions: []
|
@@ -103,8 +105,6 @@ extra_rdoc_files: []
|
|
103
105
|
files:
|
104
106
|
- ".gitignore"
|
105
107
|
- ".rspec"
|
106
|
-
- ".ruby-gemset"
|
107
|
-
- ".ruby-version"
|
108
108
|
- ".travis.yml"
|
109
109
|
- ".yardopts"
|
110
110
|
- Gemfile
|
@@ -119,8 +119,6 @@ files:
|
|
119
119
|
- lib/docile/fallback_context_proxy.rb
|
120
120
|
- lib/docile/version.rb
|
121
121
|
- on_what.rb
|
122
|
-
- spec/docile_spec.rb
|
123
|
-
- spec/spec_helper.rb
|
124
122
|
homepage: https://ms-ati.github.io/docile/
|
125
123
|
licenses:
|
126
124
|
- MIT
|
@@ -141,11 +139,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
139
|
version: '0'
|
142
140
|
requirements: []
|
143
141
|
rubyforge_project:
|
144
|
-
rubygems_version: 2.
|
142
|
+
rubygems_version: 2.7.9
|
145
143
|
signing_key:
|
146
144
|
specification_version: 4
|
147
|
-
summary: Docile keeps your Ruby DSLs tame and well-behaved
|
148
|
-
test_files:
|
149
|
-
- spec/docile_spec.rb
|
150
|
-
- spec/spec_helper.rb
|
151
|
-
has_rdoc:
|
145
|
+
summary: Docile keeps your Ruby DSLs tame and well-behaved.
|
146
|
+
test_files: []
|
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
docile
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ruby-2.1.0
|
data/spec/docile_spec.rb
DELETED
@@ -1,339 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'singleton'
|
3
|
-
|
4
|
-
describe Docile do
|
5
|
-
|
6
|
-
describe '.dsl_eval' do
|
7
|
-
|
8
|
-
context 'when DSL context object is an Array' do
|
9
|
-
let(:array) { [] }
|
10
|
-
let!(:result) { execute_dsl_against_array }
|
11
|
-
|
12
|
-
def execute_dsl_against_array
|
13
|
-
Docile.dsl_eval(array) do
|
14
|
-
push 1
|
15
|
-
push 2
|
16
|
-
pop
|
17
|
-
push 3
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'executes the block against the DSL context object' do
|
22
|
-
expect(array).to eq([1, 3])
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'returns the DSL object after executing block against it' do
|
26
|
-
expect(result).to eq(array)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "doesn't proxy #__id__" do
|
30
|
-
Docile.dsl_eval(array) { expect(__id__).not_to eq(array.__id__) }
|
31
|
-
end
|
32
|
-
|
33
|
-
it "raises NoMethodError if the DSL object doesn't implement the method" do
|
34
|
-
expect { Docile.dsl_eval(array) { no_such_method } }.to raise_error(NoMethodError)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
Pizza = Struct.new(:cheese, :pepperoni, :bacon, :sauce)
|
39
|
-
|
40
|
-
class PizzaBuilder
|
41
|
-
def cheese(v=true); @cheese = v; end
|
42
|
-
def pepperoni(v=true); @pepperoni = v; end
|
43
|
-
def bacon(v=true); @bacon = v; end
|
44
|
-
def sauce(v=nil); @sauce = v; end
|
45
|
-
def build
|
46
|
-
Pizza.new(!!@cheese, !!@pepperoni, !!@bacon, @sauce)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
context 'when DSL context object is a Builder pattern' do
|
51
|
-
let(:builder) { PizzaBuilder.new }
|
52
|
-
let(:result) { execute_dsl_against_builder_and_call_build }
|
53
|
-
|
54
|
-
def execute_dsl_against_builder_and_call_build
|
55
|
-
@sauce = :extra
|
56
|
-
Docile.dsl_eval(builder) do
|
57
|
-
bacon
|
58
|
-
cheese
|
59
|
-
sauce @sauce
|
60
|
-
end.build
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'returns correctly built object' do
|
64
|
-
expect(result).to eq(Pizza.new(true, false, true, :extra))
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
class InnerDSL
|
69
|
-
def initialize; @b = 'b'; end
|
70
|
-
attr_accessor :b
|
71
|
-
end
|
72
|
-
|
73
|
-
class OuterDSL
|
74
|
-
def initialize; @a = 'a'; end
|
75
|
-
attr_accessor :a
|
76
|
-
|
77
|
-
def inner(&block)
|
78
|
-
Docile.dsl_eval(InnerDSL.new, &block)
|
79
|
-
end
|
80
|
-
|
81
|
-
def inner_with_params(param, &block)
|
82
|
-
Docile.dsl_eval(InnerDSL.new, param, :foo, &block)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def outer(&block)
|
87
|
-
Docile.dsl_eval(OuterDSL.new, &block)
|
88
|
-
end
|
89
|
-
|
90
|
-
context 'when given parameters for the DSL block' do
|
91
|
-
def parameterized(*args, &block)
|
92
|
-
Docile.dsl_eval(OuterDSL.new, *args, &block)
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'passes parameters to the block' do
|
96
|
-
parameterized(1,2,3) do |x,y,z|
|
97
|
-
expect(x).to eq(1)
|
98
|
-
expect(y).to eq(2)
|
99
|
-
expect(z).to eq(3)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'finds parameters before methods' do
|
104
|
-
parameterized(1) { |a| expect(a).to eq(1) }
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'find outer dsl parameters in inner dsl scope' do
|
108
|
-
parameterized(1,2,3) do |a,b,c|
|
109
|
-
inner_with_params(c) do |d,e|
|
110
|
-
expect(a).to eq(1)
|
111
|
-
expect(b).to eq(2)
|
112
|
-
expect(c).to eq(3)
|
113
|
-
expect(d).to eq(c)
|
114
|
-
expect(e).to eq(:foo)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
class DSLWithNoMethod
|
121
|
-
def initialize(b); @b = b; end
|
122
|
-
attr_accessor :b
|
123
|
-
def push_element
|
124
|
-
@b.push 1
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
context 'when DSL have NoMethod error inside' do
|
129
|
-
it 'raise error from nil' do
|
130
|
-
Docile.dsl_eval(DSLWithNoMethod.new(nil)) do
|
131
|
-
expect { push_element }.to raise_error(NoMethodError, /undefined method `push' (for|on) nil:NilClass/)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
context 'when DSL blocks are nested' do
|
137
|
-
|
138
|
-
context 'method lookup' do
|
139
|
-
it 'finds method of outer dsl in outer dsl scope' do
|
140
|
-
outer { expect(a).to eq('a') }
|
141
|
-
end
|
142
|
-
|
143
|
-
it 'finds method of inner dsl in inner dsl scope' do
|
144
|
-
outer { inner { expect(b).to eq('b') } }
|
145
|
-
end
|
146
|
-
|
147
|
-
it 'finds method of outer dsl in inner dsl scope' do
|
148
|
-
outer { inner { expect(a).to eq('a') } }
|
149
|
-
end
|
150
|
-
|
151
|
-
it "finds method of block's context in outer dsl scope" do
|
152
|
-
def c; 'c'; end
|
153
|
-
outer { expect(c).to eq('c') }
|
154
|
-
end
|
155
|
-
|
156
|
-
it "finds method of block's context in inner dsl scope" do
|
157
|
-
def c; 'c'; end
|
158
|
-
outer { inner { expect(c).to eq('c') } }
|
159
|
-
end
|
160
|
-
|
161
|
-
it 'finds method of outer dsl in preference to block context' do
|
162
|
-
def a; 'not a'; end
|
163
|
-
outer { expect(a).to eq('a') }
|
164
|
-
outer { inner { expect(a).to eq('a') } }
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
context 'local variable lookup' do
|
169
|
-
it 'finds local variable from block context in outer dsl scope' do
|
170
|
-
foo = 'foo'
|
171
|
-
outer { expect(foo).to eq('foo') }
|
172
|
-
end
|
173
|
-
|
174
|
-
it 'finds local variable from block definition in inner dsl scope' do
|
175
|
-
bar = 'bar'
|
176
|
-
outer { inner { expect(bar).to eq('bar') } }
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
context 'instance variable lookup' do
|
181
|
-
it 'finds instance variable from block definition in outer dsl scope' do
|
182
|
-
@iv1 = 'iv1'; outer { expect(@iv1).to eq('iv1') }
|
183
|
-
end
|
184
|
-
|
185
|
-
it "proxies instance variable assignments in block in outer dsl scope back into block's context" do
|
186
|
-
@iv1 = 'foo'; outer { @iv1 = 'bar' }; expect(@iv1).to eq('bar')
|
187
|
-
end
|
188
|
-
|
189
|
-
it 'finds instance variable from block definition in inner dsl scope' do
|
190
|
-
@iv2 = 'iv2'; outer { inner { expect(@iv2).to eq('iv2') } }
|
191
|
-
end
|
192
|
-
|
193
|
-
it "proxies instance variable assignments in block in inner dsl scope back into block's context" do
|
194
|
-
@iv2 = 'foo'; outer { inner { @iv2 = 'bar' } }; expect(@iv2).to eq('bar')
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
end
|
199
|
-
|
200
|
-
context 'when DSL context object is a Dispatch pattern' do
|
201
|
-
class DispatchScope
|
202
|
-
def params
|
203
|
-
{ :a => 1, :b => 2, :c => 3 }
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
class MessageDispatch
|
208
|
-
include Singleton
|
209
|
-
|
210
|
-
def initialize
|
211
|
-
@responders = {}
|
212
|
-
end
|
213
|
-
|
214
|
-
def add_responder path, &block
|
215
|
-
@responders[path] = block
|
216
|
-
end
|
217
|
-
|
218
|
-
def dispatch path, request
|
219
|
-
Docile.dsl_eval(DispatchScope.new, request, &@responders[path])
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
def respond(path, &block)
|
224
|
-
MessageDispatch.instance.add_responder(path, &block)
|
225
|
-
end
|
226
|
-
|
227
|
-
def send_request(path, request)
|
228
|
-
MessageDispatch.instance.dispatch(path, request)
|
229
|
-
end
|
230
|
-
|
231
|
-
it 'dispatches correctly' do
|
232
|
-
@first = @second = nil
|
233
|
-
|
234
|
-
respond '/path' do |request|
|
235
|
-
@first = request
|
236
|
-
end
|
237
|
-
|
238
|
-
respond '/new_bike' do |bike|
|
239
|
-
@second = "Got a new #{bike}"
|
240
|
-
end
|
241
|
-
|
242
|
-
def x(y) ; "Got a #{y}"; end
|
243
|
-
respond '/third' do |third|
|
244
|
-
expect(x(third)).to eq('Got a third thing')
|
245
|
-
end
|
246
|
-
|
247
|
-
fourth = nil
|
248
|
-
respond '/params' do |arg|
|
249
|
-
fourth = params[arg]
|
250
|
-
end
|
251
|
-
|
252
|
-
send_request '/path', 1
|
253
|
-
send_request '/new_bike', 'ten speed'
|
254
|
-
send_request '/third', 'third thing'
|
255
|
-
send_request '/params', :b
|
256
|
-
|
257
|
-
expect(@first).to eq(1)
|
258
|
-
expect(@second).to eq('Got a new ten speed')
|
259
|
-
expect(fourth).to eq(2)
|
260
|
-
end
|
261
|
-
|
262
|
-
end
|
263
|
-
|
264
|
-
end
|
265
|
-
|
266
|
-
describe '.dsl_eval_immutable' do
|
267
|
-
|
268
|
-
context 'when DSL context object is a frozen String' do
|
269
|
-
let(:original) { "I'm immutable!".freeze }
|
270
|
-
let!(:result) { execute_non_mutating_dsl_against_string }
|
271
|
-
|
272
|
-
def execute_non_mutating_dsl_against_string
|
273
|
-
Docile.dsl_eval_immutable(original) do
|
274
|
-
reverse
|
275
|
-
upcase
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
it "doesn't modify the original string" do
|
280
|
-
expect(original).to eq("I'm immutable!")
|
281
|
-
end
|
282
|
-
|
283
|
-
it 'chains the commands in the block against the DSL context object' do
|
284
|
-
expect(result).to eq("!ELBATUMMI M'I")
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
context 'when DSL context object is a number' do
|
289
|
-
let(:original) { 84.5 }
|
290
|
-
let!(:result) { execute_non_mutating_dsl_against_number }
|
291
|
-
|
292
|
-
def execute_non_mutating_dsl_against_number
|
293
|
-
Docile.dsl_eval_immutable(original) do
|
294
|
-
fdiv(2)
|
295
|
-
floor
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
it 'chains the commands in the block against the DSL context object' do
|
300
|
-
expect(result).to eq(42)
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
end
|
306
|
-
|
307
|
-
describe Docile::FallbackContextProxy do
|
308
|
-
|
309
|
-
describe '#instance_variables' do
|
310
|
-
subject { create_fcp_and_set_one_instance_variable.instance_variables }
|
311
|
-
let(:expected_type_of_names) { type_of_ivar_names_on_this_ruby }
|
312
|
-
let(:actual_type_of_names) { subject.first.class }
|
313
|
-
let(:excluded) { Docile::FallbackContextProxy::NON_PROXIED_INSTANCE_VARIABLES }
|
314
|
-
|
315
|
-
def create_fcp_and_set_one_instance_variable
|
316
|
-
fcp = Docile::FallbackContextProxy.new(nil, nil)
|
317
|
-
fcp.instance_variable_set(:@foo, 'foo')
|
318
|
-
fcp
|
319
|
-
end
|
320
|
-
|
321
|
-
def type_of_ivar_names_on_this_ruby
|
322
|
-
@a = 1
|
323
|
-
instance_variables.first.class
|
324
|
-
end
|
325
|
-
|
326
|
-
it 'returns proxied instance variables' do
|
327
|
-
expect(subject.map(&:to_sym)).to include(:@foo)
|
328
|
-
end
|
329
|
-
|
330
|
-
it "doesn't return non-proxied instance variables" do
|
331
|
-
expect(subject.map(&:to_sym)).not_to include(*excluded)
|
332
|
-
end
|
333
|
-
|
334
|
-
it 'preserves the type (String or Symbol) of names on this ruby version' do
|
335
|
-
expect(actual_type_of_names).to eq(expected_type_of_names)
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
require File.expand_path('on_what', File.dirname(File.dirname(__FILE__)))
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'simplecov'
|
5
|
-
require 'coveralls'
|
6
|
-
|
7
|
-
# On Ruby 1.9+ use SimpleCov and publish to Coveralls.io
|
8
|
-
if !on_1_8?
|
9
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
10
|
-
SimpleCov::Formatter::HTMLFormatter,
|
11
|
-
Coveralls::SimpleCov::Formatter
|
12
|
-
]
|
13
|
-
SimpleCov.start do
|
14
|
-
add_filter '/spec/' # exclude test code
|
15
|
-
add_filter '/vendor/' # exclude gems which are vendored on Travis CI
|
16
|
-
end
|
17
|
-
|
18
|
-
# Remove Docile, which was required by SimpleCov, to require again later
|
19
|
-
Object.send(:remove_const, :Docile)
|
20
|
-
$LOADED_FEATURES.reject! { |f| f =~ /\/docile\// }
|
21
|
-
end
|
22
|
-
rescue LoadError
|
23
|
-
warn 'warning: simplecov/coveralls gems not found; skipping coverage'
|
24
|
-
end
|
25
|
-
|
26
|
-
lib_dir = File.join(File.dirname(File.dirname(__FILE__)), 'lib')
|
27
|
-
$LOAD_PATH.unshift lib_dir unless $LOAD_PATH.include? lib_dir
|
28
|
-
|
29
|
-
# Require Docile again, now with coverage enabled on 1.9+
|
30
|
-
require 'docile'
|