docile 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- 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'