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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: efc3f0539f1c253fdc8487bcc23f671e8c74fab5
4
- data.tar.gz: 4a6ff57d636604f406e6d19ca27b6fce30110d9f
2
+ SHA256:
3
+ metadata.gz: ba0cac2ae518dbcd219f26dfb0d8e8d556471a5043d69d9b4dc1e7300461a5a4
4
+ data.tar.gz: 2d588448f977a59f9f28e92ff98249fb31e5c7fee4e8485b4aaa32a31b11d022
5
5
  SHA512:
6
- metadata.gz: 410b86373972c31e31811dad9dc257501be169d43484023f98a54f63ff1a9c61cc5ac5974aaea384e4373a93eeab763f1f5d7db80803f2d605581963f19c9f4c
7
- data.tar.gz: 84dd464b50e7d368c198570370233e8ea1c2fef17e1d114a0b1798a302c0bf7126ff38bfe1a53d6a2e1609f524e689ae6b9233af7c9b5bca6978f0f68fd817e0
6
+ metadata.gz: 5bffdb49a9de2072292d8f78454e029f7d8545e9d36aac86ce8ed6360e4081be196a2dbb4b23c18764986b76b71446c9c6a3203c695b87c7b3aab980b4e22ec7
7
+ data.tar.gz: 7c2b89a311f27559e735bc7c7d8c6a008240f2b86b2b527074a788e0ed085c6abe6f75d54dd9b45a5d89f8f0f3f084c12292f22fcb9cbe33abcf904b47c228b6
@@ -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 # allowed to fail due to https://github.com/travis-ci/travis-ci/issues/9049
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('on_what', File.dirname(__FILE__))
2
- source 'https://rubygems.org'
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 'codecov', '>= 0.0.9', :require => false
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.2.0...master)
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
  [![Docs Coverage](http://inch-ci.org/github/ms-ati/docile.png)](http://inch-ci.org/github/ms-ati/docile)
9
9
 
10
10
  [![Build Status](https://img.shields.io/travis/ms-ati/docile/master.svg)](https://travis-ci.org/ms-ati/docile)
11
- [![Dependency Status](https://gemnasium.com/ms-ati/docile.svg)](https://gemnasium.com/ms-ati/docile)
12
- [![Code Climate](https://img.shields.io/codeclimate/github/ms-ati/docile.svg)](https://codeclimate.com/github/ms-ati/docile)
13
11
  [![Code Coverage](https://img.shields.io/codecov/c/github/ms-ati/docile.svg)](https://codecov.io/github/ms-ati/docile)
12
+ [![Dependency Status](https://gemnasium.com/ms-ati/docile.svg)](https://gemnasium.com/ms-ati/docile)
13
+ [![Maintainability](https://api.codeclimate.com/v1/badges/79ca631bc123f7b83b34/maintainability)](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
- **Easy!**
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
- 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.
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 'rake/clean'
2
- require 'bundler/gem_tasks'
3
- require 'rspec/core/rake_task'
4
- require File.expand_path('on_what', File.dirname(__FILE__))
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('pkg', 'doc', 'coverage')
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 'github/markup'
19
- require 'redcarpet'
20
- require 'yard'
21
- require 'yard/rake/yardoc_task'
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 = ['lib/**/*.rb', OTHER_PATHS]
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
@@ -1,45 +1,54 @@
1
- require File.expand_path('on_what', File.dirname(__FILE__))
2
- $:.push File.expand_path('../lib', __FILE__)
3
- require 'docile/version'
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 = 'docile'
6
+ s.name = "docile"
7
7
  s.version = Docile::VERSION
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 turns any Ruby object into a DSL. Especially useful with the Builder pattern.'
13
- s.license = 'MIT'
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("\n")
17
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
- s.require_paths = %w(lib)
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 = '>= 1.8.7'
28
+ s.required_ruby_version = ">= 1.8.7"
23
29
 
24
- # Run rspec tests from rake
25
- s.add_development_dependency 'rake', '~> 10.5.0' if on_less_than_1_9_3? # Pin compatible rake on old rubies, see: https://github.com/travis-ci/travis.rb/issues/380
26
- 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
27
- s.add_development_dependency 'rspec', '~> 3.0.0'
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
- # Pin versions for Travis builds on 1.9
30
- s.add_development_dependency 'json', '< 2.0' if on_less_than_2_0?
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
- # Pin versions for Travis builds on 1.8
33
- s.add_development_dependency 'mime-types' , '~> 1.25.1' if on_1_8?
34
- s.add_development_dependency 'rest-client', '~> 1.6.8' if on_1_8?
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 'yard'
42
- s.add_development_dependency 'redcarpet'
43
- s.add_development_dependency 'github-markup'
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
@@ -1,7 +1,7 @@
1
- require 'docile/version'
2
- require 'docile/execution'
3
- require 'docile/fallback_context_proxy'
4
- require 'docile/chaining_fallback_context_proxy'
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
@@ -1,4 +1,4 @@
1
- require 'docile/fallback_context_proxy'
1
+ require "docile/fallback_context_proxy"
2
2
 
3
3
  module Docile
4
4
  # @api private
@@ -17,4 +17,4 @@ module Docile
17
17
  @__receiver__ = super(method, *args, &block)
18
18
  end
19
19
  end
20
- end
20
+ end
@@ -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('self', block.binding)
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 'set'
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
- :'!', :'!=', :instance_exec, :instance_variables,
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
@@ -1,4 +1,4 @@
1
1
  module Docile
2
2
  # The current version of this library
3
- VERSION = '1.2.0'
3
+ VERSION = "1.3.0"
4
4
  end
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['CI'] == 'true'
5
+ ENV["CI"] == "true"
6
6
  end
7
7
 
8
8
  def on_jruby?
9
- defined?(RUBY_ENGINE) && 'jruby' == 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? '1.8'
17
+ RUBY_VERSION.start_with? "1.8"
14
18
  end
15
19
 
16
20
  def on_less_than_1_9_3?
17
- RUBY_VERSION < '1.9.3'
21
+ RUBY_VERSION < "1.9.3"
18
22
  end
19
23
 
20
24
  def on_less_than_2_0?
21
- RUBY_VERSION < '2.0.0'
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.2.0
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-01-11 00:00:00.000000000 Z
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.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.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 turns any Ruby object into a DSL. Especially useful with the Builder
84
- pattern.
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.6.13
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: []
@@ -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
@@ -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'