docile 1.2.0 → 1.3.0
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/.travis.yml +2 -5
- data/Gemfile +4 -4
- data/HISTORY.md +10 -1
- data/README.md +36 -4
- data/Rakefile +10 -10
- data/docile.gemspec +38 -29
- data/lib/docile.rb +4 -4
- data/lib/docile/chaining_fallback_context_proxy.rb +2 -2
- data/lib/docile/execution.rb +7 -2
- data/lib/docile/fallback_context_proxy.rb +27 -2
- data/lib/docile/version.rb +1 -1
- data/on_what.rb +9 -5
- metadata +11 -13
- data/spec/docile_spec.rb +0 -362
- data/spec/spec_helper.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ba0cac2ae518dbcd219f26dfb0d8e8d556471a5043d69d9b4dc1e7300461a5a4
|
4
|
+
data.tar.gz: 2d588448f977a59f9f28e92ff98249fb31e5c7fee4e8485b4aaa32a31b11d022
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5bffdb49a9de2072292d8f78454e029f7d8545e9d36aac86ce8ed6360e4081be196a2dbb4b23c18764986b76b71446c9c6a3203c695b87c7b3aab980b4e22ec7
|
7
|
+
data.tar.gz: 7c2b89a311f27559e735bc7c7d8c6a008240f2b86b2b527074a788e0ed085c6abe6f75d54dd9b45a5d89f8f0f3f084c12292f22fcb9cbe33abcf904b47c228b6
|
data/.travis.yml
CHANGED
@@ -9,18 +9,17 @@ dist: trusty
|
|
9
9
|
rvm:
|
10
10
|
# MRI
|
11
11
|
- ruby-head
|
12
|
+
- 2.5
|
12
13
|
- 2.4
|
13
14
|
- 2.3
|
14
15
|
- 2.2
|
15
16
|
- 2.1
|
16
|
-
- 2.0
|
17
|
-
- 1.9.2 # allowed to fail until Travis binary for this is fixed
|
18
17
|
- 1.9.3
|
19
18
|
- 1.8.7
|
20
19
|
- ree
|
21
20
|
# JRuby
|
22
21
|
- jruby-head
|
23
|
-
- jruby-9.1 #
|
22
|
+
- jruby-9.1.15.0 # Specific version to work around https://github.com/travis-ci/travis-ci/issues/9049
|
24
23
|
- jruby-9.0
|
25
24
|
- jruby-19mode
|
26
25
|
- jruby-18mode
|
@@ -30,8 +29,6 @@ rvm:
|
|
30
29
|
matrix:
|
31
30
|
allow_failures:
|
32
31
|
- rvm: ruby-head
|
33
|
-
- rvm: 1.9.2 # See build error https://travis-ci.org/tcrayford/Values/jobs/202728857
|
34
32
|
- rvm: jruby-head
|
35
|
-
- rvm: jruby-9.1 # See build error https://travis-ci.org/ms-ati/docile/jobs/327830706
|
36
33
|
- rvm: rubinius-3
|
37
34
|
fast_finish: true
|
data/Gemfile
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require File.expand_path(
|
2
|
-
source
|
1
|
+
require File.expand_path("on_what", File.dirname(__FILE__))
|
2
|
+
source "https://rubygems.org"
|
3
3
|
|
4
4
|
# Travis-only dependencies go here
|
5
|
-
if on_travis? && !on_1_8?
|
5
|
+
if on_travis? && !on_1_8? && !on_rubinius?
|
6
6
|
group :test do
|
7
|
-
gem
|
7
|
+
gem "codecov", ">= 0.0.9", :require => false
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
data/HISTORY.md
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
# HISTORY
|
2
2
|
|
3
|
-
## [Unreleased changes](http://github.com/ms-ati/docile/compare/v1.
|
3
|
+
## [Unreleased changes](http://github.com/ms-ati/docile/compare/v1.3.0...master)
|
4
|
+
|
5
|
+
- ...
|
6
|
+
|
7
|
+
## [v1.3.0 (Feb 7, 2018)](http://github.com/ms-ati/docile/compare/v1.2.0...v1.3.0)
|
8
|
+
|
9
|
+
- Allow helper methods in block's context to call DSL methods
|
10
|
+
- Add SemVer release policy explicitly
|
11
|
+
- Standardize on double-quoted string literals
|
12
|
+
- Workaround some more Travis CI shenanigans
|
4
13
|
|
5
14
|
## [v1.2.0 (Jan 11, 2018)](http://github.com/ms-ati/docile/compare/v1.1.5...v1.2.0)
|
6
15
|
|
data/README.md
CHANGED
@@ -8,9 +8,9 @@
|
|
8
8
|
[](http://inch-ci.org/github/ms-ati/docile)
|
9
9
|
|
10
10
|
[](https://travis-ci.org/ms-ati/docile)
|
11
|
-
[](https://gemnasium.com/ms-ati/docile)
|
12
|
-
[](https://codeclimate.com/github/ms-ati/docile)
|
13
11
|
[](https://codecov.io/github/ms-ati/docile)
|
12
|
+
[](https://gemnasium.com/ms-ati/docile)
|
13
|
+
[](https://codeclimate.com/github/ms-ati/docile/maintainability)
|
14
14
|
|
15
15
|
Ruby makes it possible to create very expressive **Domain Specific
|
16
16
|
Languages**, or **DSL**'s for short. However, it requires some deep knowledge and
|
@@ -48,13 +48,41 @@ def with_array(arr=[], &block)
|
|
48
48
|
end
|
49
49
|
```
|
50
50
|
|
51
|
-
|
51
|
+
Easy!
|
52
|
+
|
53
|
+
### Next step: Allow helper methods to call DSL methods
|
54
|
+
|
55
|
+
What if, in our use of the methods of Array as a DSL, we want to extract
|
56
|
+
helper methods which in turn call DSL methods?
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
def pop_sum_and_push(n)
|
60
|
+
sum = 0
|
61
|
+
n.times { sum += pop }
|
62
|
+
push sum
|
63
|
+
end
|
64
|
+
|
65
|
+
Docile.dsl_eval([]) do
|
66
|
+
push 5
|
67
|
+
push 6
|
68
|
+
pop_sum_and_push(2)
|
69
|
+
end
|
70
|
+
#=> [11]
|
71
|
+
```
|
72
|
+
|
73
|
+
Without Docile, you may find this sort of code extraction to be more
|
74
|
+
challenging.
|
52
75
|
|
53
76
|
### Wait! Can't I do that with just `instance_eval` or `instance_exec`?
|
54
77
|
|
55
78
|
Good question!
|
56
79
|
|
57
|
-
|
80
|
+
In short: **No**.
|
81
|
+
|
82
|
+
Not if you want the code in the block to be able to refer to anything
|
83
|
+
the block would normally have access to from the surrounding context.
|
84
|
+
|
85
|
+
Let's be very specific. Docile internally uses `instance_exec` (see [execution.rb#25](lib/docile/execution.rb#L25)), 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.
|
58
86
|
|
59
87
|
For example:
|
60
88
|
|
@@ -328,6 +356,10 @@ Works on [all ruby versions since 1.8.7](https://github.com/ms-ati/docile/blob/m
|
|
328
356
|
|
329
357
|
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).
|
330
358
|
|
359
|
+
## Release Policy
|
360
|
+
|
361
|
+
Docile releases follow [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
|
362
|
+
|
331
363
|
## Note on Patches/Pull Requests
|
332
364
|
|
333
365
|
* Fork the project.
|
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
16
|
# yard doc generation when *not* on Travis, JRuby, or < 2.0
|
17
17
|
if !on_travis? && !on_jruby? && !on_less_than_2_0?
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
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,45 +1,54 @@
|
|
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"
|
23
29
|
|
24
|
-
# Run rspec tests from rake
|
25
|
-
s.add_development_dependency
|
26
|
-
s.add_development_dependency
|
27
|
-
s.add_development_dependency
|
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"
|
28
34
|
|
29
|
-
#
|
30
|
-
|
35
|
+
# Run code coverage where possible - not on Rubinius
|
36
|
+
unless on_rubinius?
|
37
|
+
# Pin versions for Travis builds on 1.9
|
38
|
+
s.add_development_dependency "json", "< 2.0" if on_less_than_2_0?
|
31
39
|
|
32
|
-
|
33
|
-
|
34
|
-
|
40
|
+
# Pin versions for Travis builds on 1.8
|
41
|
+
s.add_development_dependency "mime-types" , "~> 1.25.1" if on_1_8?
|
42
|
+
s.add_development_dependency "rest-client", "~> 1.6.8" if on_1_8?
|
43
|
+
end
|
35
44
|
|
36
45
|
# To limit needed compatibility with versions of dependencies, only configure
|
37
|
-
# yard doc generation when *not* on Travis, JRuby, or < 2.0
|
38
|
-
if !on_travis? && !on_jruby? && !on_less_than_2_0?
|
46
|
+
# yard doc generation when *not* on Travis, JRuby, Rubinius, or < 2.0
|
47
|
+
if !on_travis? && !on_jruby? && !on_rubinius? && !on_less_than_2_0?
|
39
48
|
# Github flavored markdown in YARD documentation
|
40
49
|
# http://blog.nikosd.com/2011/11/github-flavored-markdown-in-yard.html
|
41
|
-
s.add_development_dependency
|
42
|
-
s.add_development_dependency
|
43
|
-
s.add_development_dependency
|
50
|
+
s.add_development_dependency "yard"
|
51
|
+
s.add_development_dependency "redcarpet"
|
52
|
+
s.add_development_dependency "github-markup"
|
44
53
|
end
|
45
54
|
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
|
data/lib/docile/execution.rb
CHANGED
@@ -15,15 +15,20 @@ 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|
|
28
33
|
value_from_dsl_proxy = proxy_context.instance_variable_get(ivar)
|
29
34
|
block_context.instance_variable_set(ivar, value_from_dsl_proxy)
|
@@ -32,4 +37,4 @@ module Docile
|
|
32
37
|
end
|
33
38
|
module_function :exec_in_proxy_context
|
34
39
|
end
|
35
|
-
end
|
40
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "set"
|
2
2
|
|
3
3
|
module Docile
|
4
4
|
# @api private
|
@@ -17,7 +17,7 @@ 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
|
|
@@ -38,6 +38,31 @@ module Docile
|
|
38
38
|
def initialize(receiver, fallback)
|
39
39
|
@__receiver__ = receiver
|
40
40
|
@__fallback__ = fallback
|
41
|
+
|
42
|
+
# Enables calling DSL methods from helper methods in the block's context
|
43
|
+
unless fallback.respond_to?(:method_missing)
|
44
|
+
# NOTE: There's no {#define_singleton_method} on Ruby 1.8.x
|
45
|
+
singleton_class = (class << fallback; self; end)
|
46
|
+
|
47
|
+
# instrument {#method_missing} on the block's context to fallback to
|
48
|
+
# the DSL object. This allows helper methods in the block's context to
|
49
|
+
# contain calls to methods on the DSL object.
|
50
|
+
singleton_class.
|
51
|
+
send(:define_method, :method_missing) do |method, *args, &block|
|
52
|
+
if receiver.respond_to?(method.to_sym)
|
53
|
+
receiver.__send__(method.to_sym, *args, &block)
|
54
|
+
else
|
55
|
+
super(method, *args, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# instrument a helper method to remove the above instrumentation
|
60
|
+
singleton_class.
|
61
|
+
send(:define_method, :__docile_undo_fallback__) do
|
62
|
+
singleton_class.send(:remove_method, :method_missing)
|
63
|
+
singleton_class.send(:remove_method, :__docile_undo_fallback__)
|
64
|
+
end
|
65
|
+
end
|
41
66
|
end
|
42
67
|
|
43
68
|
# @return [Array<Symbol>] Instance variable names, excluding
|
data/lib/docile/version.rb
CHANGED
data/on_what.rb
CHANGED
@@ -2,21 +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
|
-
defined?(RUBY_ENGINE) &&
|
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?
|
17
|
+
RUBY_VERSION.start_with? "1.8"
|
14
18
|
end
|
15
19
|
|
16
20
|
def on_less_than_1_9_3?
|
17
|
-
RUBY_VERSION <
|
21
|
+
RUBY_VERSION < "1.9.3"
|
18
22
|
end
|
19
23
|
|
20
24
|
def on_less_than_2_0?
|
21
|
-
RUBY_VERSION <
|
25
|
+
RUBY_VERSION < "2.0.0"
|
22
26
|
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.
|
4
|
+
version: 1.3.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: 2018-
|
11
|
+
date: 2018-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -30,14 +30,14 @@ dependencies:
|
|
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
42
|
name: yard
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,8 +80,10 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description: Docile
|
84
|
-
|
83
|
+
description: "Docile treats the methods of a given ruby object as a DSL (domain specific
|
84
|
+
language) within a given block. \n\nKiller feature: you can also reference methods,
|
85
|
+
instance variables, and local variables from the original (non-DSL) context within
|
86
|
+
the block. \n\nDocile releases follow Semantic Versioning as defined at semver.org."
|
85
87
|
email: marc@usainnov.com
|
86
88
|
executables: []
|
87
89
|
extensions: []
|
@@ -103,8 +105,6 @@ files:
|
|
103
105
|
- lib/docile/fallback_context_proxy.rb
|
104
106
|
- lib/docile/version.rb
|
105
107
|
- on_what.rb
|
106
|
-
- spec/docile_spec.rb
|
107
|
-
- spec/spec_helper.rb
|
108
108
|
homepage: https://ms-ati.github.io/docile/
|
109
109
|
licenses:
|
110
110
|
- MIT
|
@@ -125,10 +125,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
125
|
version: '0'
|
126
126
|
requirements: []
|
127
127
|
rubyforge_project:
|
128
|
-
rubygems_version: 2.
|
128
|
+
rubygems_version: 2.7.4
|
129
129
|
signing_key:
|
130
130
|
specification_version: 4
|
131
|
-
summary: Docile keeps your Ruby DSLs tame and well-behaved
|
132
|
-
test_files:
|
133
|
-
- spec/docile_spec.rb
|
134
|
-
- spec/spec_helper.rb
|
131
|
+
summary: Docile keeps your Ruby DSLs tame and well-behaved.
|
132
|
+
test_files: []
|
data/spec/docile_spec.rb
DELETED
@@ -1,362 +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_with_block_return' do
|
267
|
-
let(:array) { [] }
|
268
|
-
let!(:result) { execute_dsl_against_array }
|
269
|
-
|
270
|
-
def execute_dsl_against_array
|
271
|
-
Docile.dsl_eval_with_block_return(array) do
|
272
|
-
push 1
|
273
|
-
push 2
|
274
|
-
pop
|
275
|
-
push 3
|
276
|
-
'Return me!'
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
it 'executes the block against the DSL context object' do
|
281
|
-
expect(array).to eq([1, 3])
|
282
|
-
end
|
283
|
-
|
284
|
-
it "returns the block's return value" do
|
285
|
-
expect(result).to eq('Return me!')
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
describe '.dsl_eval_immutable' do
|
290
|
-
|
291
|
-
context 'when DSL context object is a frozen String' do
|
292
|
-
let(:original) { "I'm immutable!".freeze }
|
293
|
-
let!(:result) { execute_non_mutating_dsl_against_string }
|
294
|
-
|
295
|
-
def execute_non_mutating_dsl_against_string
|
296
|
-
Docile.dsl_eval_immutable(original) do
|
297
|
-
reverse
|
298
|
-
upcase
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
it "doesn't modify the original string" do
|
303
|
-
expect(original).to eq("I'm immutable!")
|
304
|
-
end
|
305
|
-
|
306
|
-
it 'chains the commands in the block against the DSL context object' do
|
307
|
-
expect(result).to eq("!ELBATUMMI M'I")
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
context 'when DSL context object is a number' do
|
312
|
-
let(:original) { 84.5 }
|
313
|
-
let!(:result) { execute_non_mutating_dsl_against_number }
|
314
|
-
|
315
|
-
def execute_non_mutating_dsl_against_number
|
316
|
-
Docile.dsl_eval_immutable(original) do
|
317
|
-
fdiv(2)
|
318
|
-
floor
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
it 'chains the commands in the block against the DSL context object' do
|
323
|
-
expect(result).to eq(42)
|
324
|
-
end
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
end
|
329
|
-
|
330
|
-
describe Docile::FallbackContextProxy do
|
331
|
-
|
332
|
-
describe '#instance_variables' do
|
333
|
-
subject { create_fcp_and_set_one_instance_variable.instance_variables }
|
334
|
-
let(:expected_type_of_names) { type_of_ivar_names_on_this_ruby }
|
335
|
-
let(:actual_type_of_names) { subject.first.class }
|
336
|
-
let(:excluded) { Docile::FallbackContextProxy::NON_PROXIED_INSTANCE_VARIABLES }
|
337
|
-
|
338
|
-
def create_fcp_and_set_one_instance_variable
|
339
|
-
fcp = Docile::FallbackContextProxy.new(nil, nil)
|
340
|
-
fcp.instance_variable_set(:@foo, 'foo')
|
341
|
-
fcp
|
342
|
-
end
|
343
|
-
|
344
|
-
def type_of_ivar_names_on_this_ruby
|
345
|
-
@a = 1
|
346
|
-
instance_variables.first.class
|
347
|
-
end
|
348
|
-
|
349
|
-
it 'returns proxied instance variables' do
|
350
|
-
expect(subject.map(&:to_sym)).to include(:@foo)
|
351
|
-
end
|
352
|
-
|
353
|
-
it "doesn't return non-proxied instance variables" do
|
354
|
-
expect(subject.map(&:to_sym)).not_to include(*excluded)
|
355
|
-
end
|
356
|
-
|
357
|
-
it 'preserves the type (String or Symbol) of names on this ruby version' do
|
358
|
-
expect(actual_type_of_names).to eq(expected_type_of_names)
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require File.expand_path('on_what', File.dirname(File.dirname(__FILE__)))
|
2
|
-
|
3
|
-
# Code coverage (via SimpleCov) on Ruby 1.9+
|
4
|
-
unless on_1_8?
|
5
|
-
begin
|
6
|
-
require 'simplecov'
|
7
|
-
SimpleCov.start do
|
8
|
-
add_filter '/spec/' # exclude test code
|
9
|
-
add_filter '/vendor/' # exclude gems which are vendored on Travis CI
|
10
|
-
add_filter '/on_what.rb' # exclude help used only in gemspec
|
11
|
-
end
|
12
|
-
|
13
|
-
# On CI we publish simplecov results to codecov.io
|
14
|
-
if on_travis?
|
15
|
-
require 'codecov'
|
16
|
-
SimpleCov.formatter = SimpleCov::Formatter::Codecov
|
17
|
-
end
|
18
|
-
|
19
|
-
# Due to circular dependency (simplecov depends on docile), remove docile and require again below
|
20
|
-
Object.send(:remove_const, :Docile)
|
21
|
-
$LOADED_FEATURES.reject! { |f| f =~ /\/docile\// }
|
22
|
-
rescue LoadError
|
23
|
-
warn 'warning: simplecov or codecov gems not found; skipping coverage'
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
lib_dir = File.join(File.dirname(File.dirname(__FILE__)), 'lib')
|
28
|
-
$LOAD_PATH.unshift lib_dir unless $LOAD_PATH.include? lib_dir
|
29
|
-
|
30
|
-
# Require Docile again, now with coverage enabled on 1.9+
|
31
|
-
require 'docile'
|