docile 1.2.0 → 1.3.4

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: 3ecef57f8977694d10721b6f18ece9744d03288b21e67d8fd8a4d3bca2837a57
4
+ data.tar.gz: e10ccdb31a0ffeb6fca27bb1f54250fd0c0a881afa70d2eac58145b05616a820
5
5
  SHA512:
6
- metadata.gz: 410b86373972c31e31811dad9dc257501be169d43484023f98a54f63ff1a9c61cc5ac5974aaea384e4373a93eeab763f1f5d7db80803f2d605581963f19c9f4c
7
- data.tar.gz: 84dd464b50e7d368c198570370233e8ea1c2fef17e1d114a0b1798a302c0bf7126ff38bfe1a53d6a2e1609f524e689ae6b9233af7c9b5bca6978f0f68fd817e0
6
+ metadata.gz: 0f8331a837ca968790f87bb9cdcc213d35da6c5973f4d9dcb56a604849c476c8d99d20dcdc921388389aed85f3b7eb2894eabcb90abfd42a1f1214817a631264
7
+ data.tar.gz: cffe4f42680621637b3103607a96c5511712f0d298c30401e04bfce08997a702fdcc5f965b8737e8531f12d330612ada1887818f93e109facfaaac215eb9243c
@@ -1,37 +1,43 @@
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
3
+ branches:
4
+ only:
5
+ - master
8
6
 
9
7
  rvm:
10
8
  # MRI
11
9
  - ruby-head
10
+ - 2.7
11
+ - 2.6
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
- - 1.8.7
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
24
- - jruby-9.0
25
- - jruby-19mode
26
- - jruby-18mode
27
- # Rubinius
28
- - rubinius-3
22
+ - jruby-9.2
23
+ # Truffleruby
24
+ - truffleruby-head
25
+ - truffleruby-20.3.0
29
26
 
30
- matrix:
27
+ jobs:
31
28
  allow_failures:
32
29
  - rvm: ruby-head
33
- - rvm: 1.9.2 # See build error https://travis-ci.org/tcrayford/Values/jobs/202728857
34
30
  - rvm: jruby-head
35
- - rvm: jruby-9.1 # See build error https://travis-ci.org/ms-ati/docile/jobs/327830706
36
- - rvm: rubinius-3
31
+ - rvm: truffleruby-head
32
+ # NOTE (2020-12-18): Failing access to Rubygems with following error:
33
+ # Unable to download data from https://rubygems.org/ - hostname was not
34
+ # match with the server certificate (https://rubygems.org/specs.4.8.gz)
35
+ #
36
+ # See Travis CI topic here:
37
+ # https://travis-ci.community/t/ruby-1-8-7-and-ree-builds-broken-by-ssl-certificate-failure/10866
38
+ #
39
+ # TODO (2020-12-18): Either find a fix or remove testing of 1.8 versions
40
+ - rvm: 1.8.7
41
+ - rvm: ree
42
+
37
43
  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_less_than_2_3? && !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,36 @@
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.4...master)
4
+
5
+ - ...
6
+
7
+ ## [v1.3.4 (Dec 22, 2020)](http://github.com/ms-ati/docile/compare/v1.3.3...v1.3.4)
8
+
9
+ - Special thanks to Benoit Daloze (@eregon):
10
+ - Fix delegation on Ruby 2.7 (issues #45 and #44, PR #52)
11
+
12
+ ## [v1.3.3 (Dec 18, 2020)](http://github.com/ms-ati/docile/compare/v1.3.2...v1.3.3)
13
+
14
+ - Special thanks (again!) to Taichi Ishitani (@taichi-ishitani):
15
+ - Fix keyword arg warnings on Ruby 2.7 (issue #44, PR #45)
16
+ - Filter Docile's source files from backtrace (issue #35, PR #36)
17
+
18
+ ## [v1.3.2 (Jun 12, 2019)](http://github.com/ms-ati/docile/compare/v1.3.1...v1.3.2)
19
+
20
+ - Special thanks (again!) to Taichi Ishitani (@taichi-ishitani):
21
+ - Fix for DSL object is replaced when #dsl_eval is nested (#33, PR #34)
22
+
23
+ ## [v1.3.1 (May 24, 2018)](http://github.com/ms-ati/docile/compare/v1.3.0...v1.3.1)
24
+
25
+ - Special thanks to Taichi Ishitani (@taichi-ishitani):
26
+ - Fix for when DSL object is also the block's context (#30)
27
+
28
+ ## [v1.3.0 (Feb 7, 2018)](http://github.com/ms-ati/docile/compare/v1.2.0...v1.3.0)
29
+
30
+ - Allow helper methods in block's context to call DSL methods
31
+ - Add SemVer release policy explicitly
32
+ - Standardize on double-quoted string literals
33
+ - Workaround some more Travis CI shenanigans
4
34
 
5
35
  ## [v1.2.0 (Jan 11, 2018)](http://github.com/ms-ati/docile/compare/v1.1.5...v1.2.0)
6
36
 
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2012-2018 Marc Siegel
3
+ Copyright (c) 2012-2021 Marc Siegel
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -8,9 +8,8 @@
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
+ [![Maintainability](https://api.codeclimate.com/v1/badges/79ca631bc123f7b83b34/maintainability)](https://codeclimate.com/github/ms-ati/docile/maintainability)
14
13
 
15
14
  Ruby makes it possible to create very expressive **Domain Specific
16
15
  Languages**, or **DSL**'s for short. However, it requires some deep knowledge and
@@ -48,13 +47,41 @@ def with_array(arr=[], &block)
48
47
  end
49
48
  ```
50
49
 
51
- **Easy!**
50
+ Easy!
51
+
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.
52
74
 
53
75
  ### Wait! Can't I do that with just `instance_eval` or `instance_exec`?
54
76
 
55
77
  Good question!
56
78
 
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.
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.
58
85
 
59
86
  For example:
60
87
 
@@ -328,6 +355,10 @@ Works on [all ruby versions since 1.8.7](https://github.com/ms-ati/docile/blob/m
328
355
 
329
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).
330
357
 
358
+ ## Release Policy
359
+
360
+ Docile releases follow [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
361
+
331
362
  ## Note on Patches/Pull Requests
332
363
 
333
364
  * Fork the project.
@@ -343,7 +374,7 @@ Used by some pretty cool gems to implement their DSLs, notably including [Simple
343
374
 
344
375
  ## Copyright & License
345
376
 
346
- Copyright (c) 2012-2018 Marc Siegel.
377
+ Copyright (c) 2012-2021 Marc Siegel.
347
378
 
348
379
  Licensed under the [MIT License](http://choosealicense.com/licenses/mit/), see [LICENSE](LICENSE) for details.
349
380
 
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,55 @@
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"
34
+ s.add_development_dependency "rspec-expectations", "!= 3.8.3" # Workaround for RSpec's issue, see: https://github.com/rspec/rspec-expectations/issues/1111
28
35
 
29
- # Pin versions for Travis builds on 1.9
30
- s.add_development_dependency 'json', '< 2.0' if on_less_than_2_0?
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?
31
40
 
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?
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
35
45
 
36
46
  # 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?
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?
39
49
  # Github flavored markdown in YARD documentation
40
50
  # 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'
51
+ s.add_development_dependency "yard"
52
+ s.add_development_dependency "redcarpet"
53
+ s.add_development_dependency "github-markup"
44
54
  end
45
55
  end
@@ -1,7 +1,8 @@
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
+ require "docile/backtrace_filter"
5
6
 
6
7
  # Docile keeps your Ruby DSLs tame and well-behaved.
7
8
  module Docile
@@ -0,0 +1,22 @@
1
+ module Docile
2
+ # @api private
3
+ #
4
+ # This is used to remove entries pointing to Docile's source files
5
+ # from {Exception#backtrace} and {Exception#backtrace_locations}.
6
+ #
7
+ # If {NoMethodError} is caught then the exception object will be extended
8
+ # by this module to add filter functionalities.
9
+ module BacktraceFilter
10
+ FILTER_PATTERN = /lib\/docile/
11
+
12
+ def backtrace
13
+ super.select { |trace| trace !~ FILTER_PATTERN }
14
+ end
15
+
16
+ if ::Exception.public_method_defined?(:backtrace_locations)
17
+ def backtrace_locations
18
+ super.select { |location| location.absolute_path !~ FILTER_PATTERN }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -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,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('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|
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 'set'
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
- :'!', :'!=', :instance_exec, :instance_variables,
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
@@ -56,8 +86,14 @@ module Docile
56
86
  if @__receiver__.respond_to?(method.to_sym)
57
87
  @__receiver__.__send__(method.to_sym, *args, &block)
58
88
  else
59
- @__fallback__.__send__(method.to_sym, *args, &block)
89
+ begin
90
+ @__fallback__.__send__(method.to_sym, *args, &block)
91
+ rescue NoMethodError => e
92
+ e.extend(BacktraceFilter)
93
+ raise e
94
+ end
60
95
  end
61
96
  end
97
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
62
98
  end
63
99
  end
@@ -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.4"
4
4
  end
data/on_what.rb CHANGED
@@ -2,21 +2,29 @@
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"
26
+ end
27
+
28
+ def on_less_than_2_3?
29
+ RUBY_VERSION < "2.3.0"
22
30
  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.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Siegel
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-11 00:00:00.000000000 Z
11
+ date: 2020-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -30,14 +30,28 @@ 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
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-expectations
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "!="
46
+ - !ruby/object:Gem::Version
47
+ version: 3.8.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "!="
53
+ - !ruby/object:Gem::Version
54
+ version: 3.8.3
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: yard
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -80,8 +94,10 @@ dependencies:
80
94
  - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
83
- description: Docile turns any Ruby object into a DSL. Especially useful with the Builder
84
- pattern.
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."
85
101
  email: marc@usainnov.com
86
102
  executables: []
87
103
  extensions: []
@@ -98,18 +114,17 @@ files:
98
114
  - Rakefile
99
115
  - docile.gemspec
100
116
  - lib/docile.rb
117
+ - lib/docile/backtrace_filter.rb
101
118
  - lib/docile/chaining_fallback_context_proxy.rb
102
119
  - lib/docile/execution.rb
103
120
  - lib/docile/fallback_context_proxy.rb
104
121
  - lib/docile/version.rb
105
122
  - on_what.rb
106
- - spec/docile_spec.rb
107
- - spec/spec_helper.rb
108
123
  homepage: https://ms-ati.github.io/docile/
109
124
  licenses:
110
125
  - MIT
111
126
  metadata: {}
112
- post_install_message:
127
+ post_install_message:
113
128
  rdoc_options: []
114
129
  require_paths:
115
130
  - lib
@@ -124,11 +139,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
139
  - !ruby/object:Gem::Version
125
140
  version: '0'
126
141
  requirements: []
127
- rubyforge_project:
128
- rubygems_version: 2.6.13
129
- signing_key:
142
+ rubygems_version: 3.1.4
143
+ signing_key:
130
144
  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
145
+ summary: Docile keeps your Ruby DSLs tame and well-behaved.
146
+ 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'