Hello community, here is the log from the commit of package rubygem-docile for openSUSE:Factory checked in at 2018-02-10 17:59:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-docile (Old) and /work/SRC/openSUSE:Factory/.rubygem-docile.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-docile" Sat Feb 10 17:59:57 2018 rev:7 rq:574929 version:1.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-docile/rubygem-docile.changes 2014-10-18 09:09:54.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-docile.new/rubygem-docile.changes 2018-02-10 17:59:59.720161466 +0100 @@ -1,0 +2,24 @@ +Thu Feb 8 05:59:41 UTC 2018 - co...@suse.com + +- updated to version 1.3.0 + see installed HISTORY.md + + ## [Unreleased changes](http://github.com/ms-ati/docile/compare/v1.3.0...master) + + - ... + + ## [v1.3.0 (Feb 7, 2018)](http://github.com/ms-ati/docile/compare/v1.2.0...v1.3.0) + + - Allow helper methods in block's context to call DSL methods + - Add SemVer release policy explicitly + - Standardize on double-quoted string literals + - Workaround some more Travis CI shenanigans + + ## [v1.2.0 (Jan 11, 2018)](http://github.com/ms-ati/docile/compare/v1.1.5...v1.2.0) + + - Special thanks to Christina Koller (@cmkoller) + - add DSL evaluation returning *return value of the block* (see `.dsl_eval_with_block_return`) + - add an example to README + - keep travis builds passing on old ruby versions + +------------------------------------------------------------------- Old: ---- docile-1.1.5.gem New: ---- docile-1.3.0.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-docile.spec ++++++ --- /var/tmp/diff_new_pack.MsuNjV/_old 2018-02-10 18:00:00.340139023 +0100 +++ /var/tmp/diff_new_pack.MsuNjV/_new 2018-02-10 18:00:00.344138878 +0100 @@ -1,7 +1,7 @@ # # spec file for package rubygem-docile # -# Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -24,7 +24,7 @@ # Name: rubygem-docile -Version: 1.1.5 +Version: 1.3.0 Release: 0 %define mod_name docile %define mod_full_name %{mod_name}-%{version} @@ -33,15 +33,18 @@ BuildRequires: %{rubygem gem2rpm} BuildRequires: ruby-macros >= 5 Url: https://ms-ati.github.io/docile/ -Source: http://rubygems.org/gems/%{mod_full_name}.gem +Source: https://rubygems.org/gems/%{mod_full_name}.gem Source1: gem2rpm.yml Summary: Docile keeps your Ruby DSLs tame and well-behaved License: MIT Group: Development/Languages/Ruby %description -Docile turns any Ruby object into a DSL. Especially useful with the Builder -pattern. +Docile treats the methods of a given ruby object as a DSL (domain specific +language) within a given block. +Killer feature: you can also reference methods, instance variables, and local +variables from the original (non-DSL) context within the block. +Docile releases follow Semantic Versioning as defined at semver.org. %prep ++++++ docile-1.1.5.gem -> docile-1.3.0.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 2014-06-15 21:51:29.000000000 +0200 +++ new/.gitignore 2018-02-07 23:25:18.000000000 +0100 @@ -6,4 +6,6 @@ doc .yardoc coverage -vendor \ No newline at end of file +vendor +.ruby-gemset +.ruby-version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.ruby-gemset new/.ruby-gemset --- old/.ruby-gemset 2014-06-15 21:51:29.000000000 +0200 +++ new/.ruby-gemset 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -docile diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.ruby-version new/.ruby-version --- old/.ruby-version 2014-06-15 21:51:29.000000000 +0200 +++ new/.ruby-version 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -ruby-2.1.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.travis.yml new/.travis.yml --- old/.travis.yml 2014-06-15 21:51:29.000000000 +0200 +++ new/.travis.yml 2018-02-07 23:25:18.000000000 +0100 @@ -1,21 +1,34 @@ language: ruby -cache: bundler + +# Apparently sudo is required to test on Rubinius and JRuby-head +sudo: required + +# See https://docs.travis-ci.com/user/languages/ruby/#Rubinius +dist: trusty + rvm: + # MRI - ruby-head - - 2.1.2 - - 2.1.1 - - 2.1.0 - - 2.0.0 + - 2.5 + - 2.4 + - 2.3 + - 2.2 + - 2.1 - 1.9.3 - - 1.9.2 - 1.8.7 - ree + # JRuby - jruby-head + - jruby-9.1.15.0 # Specific version to work around https://github.com/travis-ci/travis-ci/issues/9049 + - jruby-9.0 - jruby-19mode - jruby-18mode - - rbx-2 + # Rubinius + - rubinius-3 + matrix: allow_failures: - rvm: ruby-head - rvm: jruby-head + - rvm: rubinius-3 fast_finish: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Gemfile new/Gemfile --- old/Gemfile 2014-06-15 21:51:29.000000000 +0200 +++ new/Gemfile 2018-02-07 23:25:18.000000000 +0100 @@ -1,4 +1,12 @@ -source 'https://rubygems.org' +require File.expand_path("on_what", File.dirname(__FILE__)) +source "https://rubygems.org" + +# Travis-only dependencies go here +if on_travis? && !on_1_8? && !on_rubinius? + group :test do + gem "codecov", ">= 0.0.9", :require => false + end +end # Specify gem's dependencies in docile.gemspec gemspec diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/HISTORY.md new/HISTORY.md --- old/HISTORY.md 2014-06-15 21:51:29.000000000 +0200 +++ new/HISTORY.md 2018-02-07 23:25:18.000000000 +0100 @@ -1,5 +1,23 @@ # HISTORY +## [Unreleased changes](http://github.com/ms-ati/docile/compare/v1.3.0...master) + + - ... + +## [v1.3.0 (Feb 7, 2018)](http://github.com/ms-ati/docile/compare/v1.2.0...v1.3.0) + + - Allow helper methods in block's context to call DSL methods + - Add SemVer release policy explicitly + - Standardize on double-quoted string literals + - Workaround some more Travis CI shenanigans + +## [v1.2.0 (Jan 11, 2018)](http://github.com/ms-ati/docile/compare/v1.1.5...v1.2.0) + + - Special thanks to Christina Koller (@cmkoller) + - add DSL evaluation returning *return value of the block* (see `.dsl_eval_with_block_return`) + - add an example to README + - keep travis builds passing on old ruby versions + ## [v1.1.5 (Jun 15, 2014)](http://github.com/ms-ati/docile/compare/v1.1.4...v1.1.5) - as much as possible, loosen version restrictions on development dependencies diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/LICENSE new/LICENSE --- old/LICENSE 2014-06-15 21:51:29.000000000 +0200 +++ new/LICENSE 2018-02-07 23:25:18.000000000 +0100 @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2012-2014 Marc Siegel +Copyright (c) 2012-2018 Marc Siegel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2014-06-15 21:51:29.000000000 +0200 +++ new/README.md 2018-02-07 23:25:18.000000000 +0100 @@ -1,11 +1,16 @@ # Docile -[![Gem Version](https://badge.fury.io/rb/docile.png)](http://badge.fury.io/rb/docile) -[![Build Status](https://travis-ci.org/ms-ati/docile.png)](https://travis-ci.org/ms-ati/docile) -[![Dependency Status](https://gemnasium.com/ms-ati/docile.png)](https://gemnasium.com/ms-ati/docile) -[![Code Climate](https://codeclimate.com/github/ms-ati/docile.png)](https://codeclimate.com/github/ms-ati/docile) -[![Coverage Status](https://coveralls.io/repos/ms-ati/docile/badge.png)](https://coveralls.io/r/ms-ati/docile) -[![Inline docs](http://inch-ci.org/github/ms-ati/docile.png)](http://inch-ci.org/github/ms-ati/docile) -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/ms-ati/docile/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + +[![Gem Version](https://img.shields.io/gem/v/docile.svg)](https://rubygems.org/gems/docile) +[![Gem Downloads](https://img.shields.io/gem/dt/docile.svg)](https://rubygems.org/gems/docile) + +[![Join the chat at https://gitter.im/ms-ati/docile](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ms-ati/docile?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/ms-ati/docile) +[![Docs Coverage](http://inch-ci.org/github/ms-ati/docile.png)](http://inch-ci.org/github/ms-ati/docile) + +[![Build Status](https://img.shields.io/travis/ms-ati/docile/master.svg)](https://travis-ci.org/ms-ati/docile) +[![Code Coverage](https://img.shields.io/codecov/c/github/ms-ati/docile.svg)](https://codecov.io/github/ms-ati/docile) +[![Dependency Status](https://gemnasium.com/ms-ati/docile.svg)](https://gemnasium.com/ms-ati/docile) +[![Maintainability](https://api.codeclimate.com/v1/badges/79ca631bc123f7b83b34/maintainability)](https://codeclimate.com/github/ms-ati/docile/maintainability) Ruby makes it possible to create very expressive **Domain Specific Languages**, or **DSL**'s for short. However, it requires some deep knowledge and @@ -20,7 +25,7 @@ ## Usage -### Basic +### Basic: Ruby [Array](http://ruby-doc.org/core-2.2.2/Array.html) as DSL Let's say that we want to make a DSL for modifying Array objects. Wouldn't it be great if we could just treat the methods of Array as a DSL? @@ -45,7 +50,80 @@ Easy! -### Advanced +### Next step: Allow helper methods to call DSL methods + +What if, in our use of the methods of Array as a DSL, we want to extract +helper methods which in turn call DSL methods? + +```ruby +def pop_sum_and_push(n) + sum = 0 + n.times { sum += pop } + push sum +end + +Docile.dsl_eval([]) do + push 5 + push 6 + pop_sum_and_push(2) +end +#=> [11] +``` + +Without Docile, you may find this sort of code extraction to be more +challenging. + +### Wait! Can't I do that with just `instance_eval` or `instance_exec`? + +Good question! + +In short: **No**. + +Not if you want the code in the block to be able to refer to anything +the block would normally have access to from the surrounding context. + +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. + +For example: + +```ruby +class ContextOfBlock + def example_of_contexts + @block_instance_var = 1 + block_local_var = 2 + + with_array do + push @block_instance_var + push block_local_var + pop + push block_sees_this_method + end + end + + def block_sees_this_method + 3 + end + + def with_array(&block) + { + docile: Docile.dsl_eval([], &block), + instance_eval: ([].instance_eval(&block) rescue $!), + instance_exec: ([].instance_exec(&block) rescue $!) + } + end +end + +ContextOfBlock.new.example_of_contexts +#=> { + :docile=>[1, 3], + :instance_eval=>#<NameError: undefined local variable or method `block_sees_this_method' for [nil]:Array>, + :instance_exec=>#<NameError: undefined local variable or method `block_sees_this_method' for [nil]:Array> + } +``` + +As you can see, it won't be possible to call methods or access instance variables defined in the block's context using just the raw `instance_eval` or `instance_exec` methods. And in fact, Docile goes further, making it easy to maintain this support even in multi-layered DSLs. + +### Build a Pizza Mutating (changing) an Array instance is fine, but what usually makes a good DSL is a [Builder Pattern][2]. @@ -83,7 +161,7 @@ Then implement your DSL like this: -``` ruby +```ruby def pizza(&block) Docile.dsl_eval(PizzaBuilder.new, &block).build end @@ -93,6 +171,38 @@ [2]: http://stackoverflow.com/questions/328496/when-would-you-use-the-builder-pattern "Builder Pattern" +### Multi-level and Recursive DSLs + +Docile is a very easy way to write a multi-level DSL in Ruby, even for +a [recursive data structure such as a tree][4]: + +```ruby +Person = Struct.new(:name, :mother, :father) + +person { + name 'John Smith' + mother { + name 'Mary Smith' + } + father { + name 'Tom Smith' + mother { + name 'Jane Smith' + } + } +} + +#=> #<struct Person name="John Smith", +# mother=#<struct Person name="Mary Smith", mother=nil, father=nil>, +# father=#<struct Person name="Tom Smith", +# mother=#<struct Person name="Jane Smith", mother=nil, father=nil>, +# father=nil>> +``` + +See the full [person tree example][4] for details. + +[4]: https://gist.github.com/ms-ati/2bb17bdf10a430faba98 + ### Block parameters Parameters can be passed to the DSL block. @@ -153,7 +263,7 @@ [3]: http://www.sinatrarb.com "Sinatra" -### Functional-Style DSL Objects +### Functional-Style Immutable DSL Objects Sometimes, you want to use an object as a DSL, but it doesn't quite fit the [imperative](http://en.wikipedia.org/wiki/Imperative_programming) pattern shown @@ -192,6 +302,33 @@ All set! +### Accessing the block's return value + +Sometimes you might want to access the return value of your provided block, +as opposed to the DSL object itself. In these cases, use +`dsl_eval_with_block_return`. It behaves exactly like `dsl_eval`, but returns +the output from executing the block, rather than the DSL object. + +```ruby +arr = [] +with_array(arr) do + push "a" + push "b" + push "c" + length +end +#=> 3 + +arr +#=> ["a", "b", "c"] +``` + +```ruby +def with_array(arr=[], &block) + Docile.dsl_eval_with_block_return(arr, &block) +end +``` + ## Features 1. Method lookup falls back from the DSL object to the block's context @@ -219,6 +356,10 @@ 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). +## Release Policy + +Docile releases follow [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). + ## Note on Patches/Pull Requests * Fork the project. @@ -234,7 +375,7 @@ ## Copyright & License -Copyright (c) 2012-2014 Marc Siegel. +Copyright (c) 2012-2018 Marc Siegel. Licensed under the [MIT License](http://choosealicense.com/licenses/mit/), see [LICENSE](LICENSE) for details. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Rakefile new/Rakefile --- old/Rakefile 2014-06-15 21:51:29.000000000 +0200 +++ new/Rakefile 2018-02-07 23:25:18.000000000 +0100 @@ -1,7 +1,7 @@ -require 'rake/clean' -require 'bundler/gem_tasks' -require 'rspec/core/rake_task' -require File.expand_path('on_what', File.dirname(__FILE__)) +require "rake/clean" +require "bundler/gem_tasks" +require "rspec/core/rake_task" +require File.expand_path("on_what", File.dirname(__FILE__)) # Default task for `rake` is to run rspec task :default => [:spec] @@ -10,19 +10,19 @@ RSpec::Core::RakeTask.new # Configure `rake clobber` to delete all generated files -CLOBBER.include('pkg', 'doc', 'coverage') +CLOBBER.include("pkg", "doc", "coverage") # To limit needed compatibility with versions of dependencies, only configure -# yard doc generation when *not* on Travis, JRuby, or 1.8 -if !on_travis? && !on_jruby? && !on_1_8? - require 'github/markup' - require 'redcarpet' - require 'yard' - require 'yard/rake/yardoc_task' +# yard doc generation when *not* on Travis, JRuby, or < 2.0 +if !on_travis? && !on_jruby? && !on_less_than_2_0? + require "github/markup" + require "redcarpet" + require "yard" + require "yard/rake/yardoc_task" YARD::Rake::YardocTask.new do |t| OTHER_PATHS = %w() - t.files = ['lib/**/*.rb', OTHER_PATHS] + t.files = ["lib/**/*.rb", OTHER_PATHS] t.options = %w(--markup-provider=redcarpet --markup=markdown --main=README.md) end end Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/docile.gemspec new/docile.gemspec --- old/docile.gemspec 2014-06-15 21:51:29.000000000 +0200 +++ new/docile.gemspec 2018-02-07 23:25:18.000000000 +0100 @@ -1,43 +1,54 @@ -require File.expand_path('on_what', File.dirname(__FILE__)) -$:.push File.expand_path('../lib', __FILE__) -require 'docile/version' +$:.push File.expand_path("../lib", __FILE__) +require File.expand_path("on_what", File.dirname(__FILE__)) +require "docile/version" Gem::Specification.new do |s| - s.name = 'docile' + s.name = "docile" s.version = Docile::VERSION - s.author = 'Marc Siegel' - s.email = 'm...@usainnov.com' - s.homepage = 'https://ms-ati.github.io/docile/' - s.summary = 'Docile keeps your Ruby DSLs tame and well-behaved' - s.description = 'Docile turns any Ruby object into a DSL. Especially useful with the Builder pattern.' - s.license = 'MIT' + s.author = "Marc Siegel" + s.email = "m...@usainnov.com" + s.homepage = "https://ms-ati.github.io/docile/" + s.summary = "Docile keeps your Ruby DSLs tame and well-behaved." + s.description = "Docile treats the methods of a given ruby object as a DSL " \ + "(domain specific language) within a given block. \n\n" \ + "Killer feature: you can also reference methods, instance " \ + "variables, and local variables from the original (non-DSL) "\ + "context within the block. \n\n" \ + "Docile releases follow Semantic Versioning as defined at " \ + "semver.org." + s.license = "MIT" # Files included in the gem - s.files = `git ls-files`.split("\n") - s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") - s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } - s.require_paths = %w(lib) + s.files = `git ls-files -z`.split("\x0").reject do |f| + f.match(%r{^(test|spec|features)/}) + end + s.require_paths = ["lib"] # Specify oldest supported Ruby version - s.required_ruby_version = '>= 1.8.7' - - # Run rspec tests from rake - s.add_development_dependency 'rake' - s.add_development_dependency 'rspec', '~> 3.0.0' + s.required_ruby_version = ">= 1.8.7" - # NOTE: needed for Travis builds on 1.8, but can't yet reproduce failure locally - s.add_development_dependency 'mime-types', '~> 1.25.1' if on_1_8? + # Run rspec tests from rake even on old Ruby versions + 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 + 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 + s.add_development_dependency "rspec", "~> 3.0" + + # Run code coverage where possible - not on Rubinius + unless on_rubinius? + # Pin versions for Travis builds on 1.9 + s.add_development_dependency "json", "< 2.0" if on_less_than_2_0? + + # Pin versions for Travis builds on 1.8 + s.add_development_dependency "mime-types" , "~> 1.25.1" if on_1_8? + s.add_development_dependency "rest-client", "~> 1.6.8" if on_1_8? + end # To limit needed compatibility with versions of dependencies, only configure - # yard doc generation when *not* on Travis, JRuby, or 1.8 - if !on_travis? && !on_jruby? && !on_1_8? + # yard doc generation when *not* on Travis, JRuby, Rubinius, or < 2.0 + if !on_travis? && !on_jruby? && !on_rubinius? && !on_less_than_2_0? # Github flavored markdown in YARD documentation # http://blog.nikosd.com/2011/11/github-flavored-markdown-in-yard.html - s.add_development_dependency 'yard' - s.add_development_dependency 'redcarpet' - s.add_development_dependency 'github-markup' + s.add_development_dependency "yard" + s.add_development_dependency "redcarpet" + s.add_development_dependency "github-markup" end - - # Coveralls test coverage tool, basically hosted SimpleCov - s.add_development_dependency 'coveralls' end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/docile/chaining_fallback_context_proxy.rb new/lib/docile/chaining_fallback_context_proxy.rb --- old/lib/docile/chaining_fallback_context_proxy.rb 2014-06-15 21:51:29.000000000 +0200 +++ new/lib/docile/chaining_fallback_context_proxy.rb 2018-02-07 23:25:18.000000000 +0100 @@ -1,4 +1,4 @@ -require 'docile/fallback_context_proxy' +require "docile/fallback_context_proxy" module Docile # @api private @@ -17,4 +17,4 @@ @__receiver__ = super(method, *args, &block) end end -end \ No newline at end of file +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/docile/execution.rb new/lib/docile/execution.rb --- old/lib/docile/execution.rb 2014-06-15 21:51:29.000000000 +0200 +++ new/lib/docile/execution.rb 2018-02-07 23:25:18.000000000 +0100 @@ -15,15 +15,20 @@ # @param block [Proc] the block of DSL commands to be executed # @return [Object] the return value of the block def exec_in_proxy_context(dsl, proxy_type, *args, &block) - block_context = eval('self', block.binding) + block_context = eval("self", block.binding) proxy_context = proxy_type.new(dsl, block_context) begin block_context.instance_variables.each do |ivar| value_from_block = block_context.instance_variable_get(ivar) proxy_context.instance_variable_set(ivar, value_from_block) end + proxy_context.instance_exec(*args, &block) ensure + if block_context.respond_to?(:__docile_undo_fallback__) + block_context.send(:__docile_undo_fallback__) + end + block_context.instance_variables.each do |ivar| value_from_dsl_proxy = proxy_context.instance_variable_get(ivar) block_context.instance_variable_set(ivar, value_from_dsl_proxy) @@ -32,4 +37,4 @@ end module_function :exec_in_proxy_context end -end \ No newline at end of file +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/docile/fallback_context_proxy.rb new/lib/docile/fallback_context_proxy.rb --- old/lib/docile/fallback_context_proxy.rb 2014-06-15 21:51:29.000000000 +0200 +++ new/lib/docile/fallback_context_proxy.rb 2018-02-07 23:25:18.000000000 +0100 @@ -1,4 +1,4 @@ -require 'set' +require "set" module Docile # @api private @@ -17,7 +17,7 @@ # The set of methods which will **not** be proxied, but instead answered # by this object directly. NON_PROXIED_METHODS = Set[:__send__, :object_id, :__id__, :==, :equal?, - :'!', :'!=', :instance_exec, :instance_variables, + :"!", :"!=", :instance_exec, :instance_variables, :instance_variable_get, :instance_variable_set, :remove_instance_variable] @@ -38,6 +38,31 @@ def initialize(receiver, fallback) @__receiver__ = receiver @__fallback__ = fallback + + # Enables calling DSL methods from helper methods in the block's context + unless fallback.respond_to?(:method_missing) + # NOTE: There's no {#define_singleton_method} on Ruby 1.8.x + singleton_class = (class << fallback; self; end) + + # instrument {#method_missing} on the block's context to fallback to + # the DSL object. This allows helper methods in the block's context to + # contain calls to methods on the DSL object. + singleton_class. + send(:define_method, :method_missing) do |method, *args, &block| + if receiver.respond_to?(method.to_sym) + receiver.__send__(method.to_sym, *args, &block) + else + super(method, *args, &block) + end + end + + # instrument a helper method to remove the above instrumentation + singleton_class. + send(:define_method, :__docile_undo_fallback__) do + singleton_class.send(:remove_method, :method_missing) + singleton_class.send(:remove_method, :__docile_undo_fallback__) + end + end end # @return [Array<Symbol>] Instance variable names, excluding diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/docile/version.rb new/lib/docile/version.rb --- old/lib/docile/version.rb 2014-06-15 21:51:29.000000000 +0200 +++ new/lib/docile/version.rb 2018-02-07 23:25:18.000000000 +0100 @@ -1,4 +1,4 @@ module Docile # The current version of this library - VERSION = '1.1.5' + VERSION = "1.3.0" end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/docile.rb new/lib/docile.rb --- old/lib/docile.rb 2014-06-15 21:51:29.000000000 +0200 +++ new/lib/docile.rb 2018-02-07 23:25:18.000000000 +0100 @@ -1,7 +1,7 @@ -require 'docile/version' -require 'docile/execution' -require 'docile/fallback_context_proxy' -require 'docile/chaining_fallback_context_proxy' +require "docile/version" +require "docile/execution" +require "docile/fallback_context_proxy" +require "docile/chaining_fallback_context_proxy" # Docile keeps your Ruby DSLs tame and well-behaved. module Docile @@ -45,6 +45,45 @@ end module_function :dsl_eval + # Execute a block in the context of an object whose methods represent the + # commands in a DSL, and return *the block's return value*. + # + # @note Use with an *imperative* DSL (commands modify the context object) + # + # Use this method to execute an *imperative* DSL, which means that: + # + # 1. Each command mutates the state of the DSL context object + # 2. The return value of each command is ignored + # 3. The final return value is the original context object + # + # @example Use a String as a DSL + # Docile.dsl_eval_with_block_return("Hello, world!") do + # reverse! + # upcase! + # first + # end + # #=> "!" + # + # @example Use an Array as a DSL + # Docile.dsl_eval_with_block_return([]) do + # push "a" + # push "b" + # pop + # push "c" + # length + # end + # #=> 2 + # + # @param dsl [Object] context object whose methods make up the DSL + # @param args [Array] arguments to be passed to the block + # @param block [Proc] the block of DSL commands to be executed against the + # `dsl` context object + # @return [Object] the return value from executing the block + def dsl_eval_with_block_return(dsl, *args, &block) + exec_in_proxy_context(dsl, FallbackContextProxy, *args, &block) + end + module_function :dsl_eval_with_block_return + # Execute a block in the context of an immutable object whose methods, # and the methods of their return values, represent the commands in a DSL. # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2014-06-15 21:51:29.000000000 +0200 +++ new/metadata 2018-02-07 23:25:18.000000000 +0100 @@ -1,43 +1,43 @@ --- !ruby/object:Gem::Specification name: docile version: !ruby/object:Gem::Version - version: 1.1.5 + version: 1.3.0 platform: ruby authors: - Marc Siegel autorequire: bindir: bin cert_chain: [] -date: 2014-06-15 00:00:00.000000000 Z +date: 2018-02-07 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - - ">=" + - - "<" - !ruby/object:Gem::Version - version: '0' + version: '11.0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - - ">=" + - - "<" - !ruby/object:Gem::Version - version: '0' + version: '11.0' - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version - version: 3.0.0 + version: '3.0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version - version: 3.0.0 + version: '3.0' - !ruby/object:Gem::Dependency name: yard requirement: !ruby/object:Gem::Requirement @@ -80,22 +80,10 @@ - - ">=" - !ruby/object:Gem::Version version: '0' -- !ruby/object:Gem::Dependency - name: coveralls - requirement: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: '0' - type: :development - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: '0' -description: Docile turns any Ruby object into a DSL. Especially useful with the Builder - pattern. +description: "Docile treats the methods of a given ruby object as a DSL (domain specific + language) within a given block. \n\nKiller feature: you can also reference methods, + instance variables, and local variables from the original (non-DSL) context within + the block. \n\nDocile releases follow Semantic Versioning as defined at semver.org." email: m...@usainnov.com executables: [] extensions: [] @@ -103,8 +91,6 @@ files: - ".gitignore" - ".rspec" -- ".ruby-gemset" -- ".ruby-version" - ".travis.yml" - ".yardopts" - Gemfile @@ -119,8 +105,6 @@ - lib/docile/fallback_context_proxy.rb - lib/docile/version.rb - on_what.rb -- spec/docile_spec.rb -- spec/spec_helper.rb homepage: https://ms-ati.github.io/docile/ licenses: - MIT @@ -141,11 +125,8 @@ version: '0' requirements: [] rubyforge_project: -rubygems_version: 2.2.2 +rubygems_version: 2.7.4 signing_key: specification_version: 4 -summary: Docile keeps your Ruby DSLs tame and well-behaved -test_files: -- spec/docile_spec.rb -- spec/spec_helper.rb -has_rdoc: +summary: Docile keeps your Ruby DSLs tame and well-behaved. +test_files: [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/on_what.rb new/on_what.rb --- old/on_what.rb 2014-06-15 21:51:29.000000000 +0200 +++ new/on_what.rb 2018-02-07 23:25:18.000000000 +0100 @@ -2,13 +2,25 @@ # between Rakefile, gemspec, and spec_helper. Not for use in actual library. def on_travis? - ENV['CI'] == 'true' + ENV["CI"] == "true" end def on_jruby? - (defined?(RUBY_ENGINE) && 'jruby' == RUBY_ENGINE) + defined?(RUBY_ENGINE) && "jruby" == RUBY_ENGINE +end + +def on_rubinius? + defined?(RUBY_ENGINE) && "rbx" == RUBY_ENGINE end def on_1_8? - RUBY_VERSION.start_with? '1.8' -end \ No newline at end of file + RUBY_VERSION.start_with? "1.8" +end + +def on_less_than_1_9_3? + RUBY_VERSION < "1.9.3" +end + +def on_less_than_2_0? + RUBY_VERSION < "2.0.0" +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/docile_spec.rb new/spec/docile_spec.rb --- old/spec/docile_spec.rb 2014-06-15 21:51:29.000000000 +0200 +++ new/spec/docile_spec.rb 1970-01-01 01:00:00.000000000 +0100 @@ -1,339 +0,0 @@ -require 'spec_helper' -require 'singleton' - -describe Docile do - - describe '.dsl_eval' do - - context 'when DSL context object is an Array' do - let(:array) { [] } - let!(:result) { execute_dsl_against_array } - - def execute_dsl_against_array - Docile.dsl_eval(array) do - push 1 - push 2 - pop - push 3 - end - end - - it 'executes the block against the DSL context object' do - expect(array).to eq([1, 3]) - end - - it 'returns the DSL object after executing block against it' do - expect(result).to eq(array) - end - - it "doesn't proxy #__id__" do - Docile.dsl_eval(array) { expect(__id__).not_to eq(array.__id__) } - end - - it "raises NoMethodError if the DSL object doesn't implement the method" do - expect { Docile.dsl_eval(array) { no_such_method } }.to raise_error(NoMethodError) - end - end - - Pizza = Struct.new(:cheese, :pepperoni, :bacon, :sauce) - - class PizzaBuilder - def cheese(v=true); @cheese = v; end - def pepperoni(v=true); @pepperoni = v; end - def bacon(v=true); @bacon = v; end - def sauce(v=nil); @sauce = v; end - def build - Pizza.new(!!@cheese, !!@pepperoni, !!@bacon, @sauce) - end - end - - context 'when DSL context object is a Builder pattern' do - let(:builder) { PizzaBuilder.new } - let(:result) { execute_dsl_against_builder_and_call_build } - - def execute_dsl_against_builder_and_call_build - @sauce = :extra - Docile.dsl_eval(builder) do - bacon - cheese - sauce @sauce - end.build - end - - it 'returns correctly built object' do - expect(result).to eq(Pizza.new(true, false, true, :extra)) - end - end - - class InnerDSL - def initialize; @b = 'b'; end - attr_accessor :b - end - - class OuterDSL - def initialize; @a = 'a'; end - attr_accessor :a - - def inner(&block) - Docile.dsl_eval(InnerDSL.new, &block) - end - - def inner_with_params(param, &block) - Docile.dsl_eval(InnerDSL.new, param, :foo, &block) - end - end - - def outer(&block) - Docile.dsl_eval(OuterDSL.new, &block) - end - - context 'when given parameters for the DSL block' do - def parameterized(*args, &block) - Docile.dsl_eval(OuterDSL.new, *args, &block) - end - - it 'passes parameters to the block' do - parameterized(1,2,3) do |x,y,z| - expect(x).to eq(1) - expect(y).to eq(2) - expect(z).to eq(3) - end - end - - it 'finds parameters before methods' do - parameterized(1) { |a| expect(a).to eq(1) } - end - - it 'find outer dsl parameters in inner dsl scope' do - parameterized(1,2,3) do |a,b,c| - inner_with_params(c) do |d,e| - expect(a).to eq(1) - expect(b).to eq(2) - expect(c).to eq(3) - expect(d).to eq(c) - expect(e).to eq(:foo) - end - end - end - end - - class DSLWithNoMethod - def initialize(b); @b = b; end - attr_accessor :b - def push_element - @b.push 1 - end - end - - context 'when DSL have NoMethod error inside' do - it 'raise error from nil' do - Docile.dsl_eval(DSLWithNoMethod.new(nil)) do - expect { push_element }.to raise_error(NoMethodError, /undefined method `push' (for|on) nil:NilClass/) - end - end - end - - context 'when DSL blocks are nested' do - - context 'method lookup' do - it 'finds method of outer dsl in outer dsl scope' do - outer { expect(a).to eq('a') } - end - - it 'finds method of inner dsl in inner dsl scope' do - outer { inner { expect(b).to eq('b') } } - end - - it 'finds method of outer dsl in inner dsl scope' do - outer { inner { expect(a).to eq('a') } } - end - - it "finds method of block's context in outer dsl scope" do - def c; 'c'; end - outer { expect(c).to eq('c') } - end - - it "finds method of block's context in inner dsl scope" do - def c; 'c'; end - outer { inner { expect(c).to eq('c') } } - end - - it 'finds method of outer dsl in preference to block context' do - def a; 'not a'; end - outer { expect(a).to eq('a') } - outer { inner { expect(a).to eq('a') } } - end - end - - context 'local variable lookup' do - it 'finds local variable from block context in outer dsl scope' do - foo = 'foo' - outer { expect(foo).to eq('foo') } - end - - it 'finds local variable from block definition in inner dsl scope' do - bar = 'bar' - outer { inner { expect(bar).to eq('bar') } } - end - end - - context 'instance variable lookup' do - it 'finds instance variable from block definition in outer dsl scope' do - @iv1 = 'iv1'; outer { expect(@iv1).to eq('iv1') } - end - - it "proxies instance variable assignments in block in outer dsl scope back into block's context" do - @iv1 = 'foo'; outer { @iv1 = 'bar' }; expect(@iv1).to eq('bar') - end - - it 'finds instance variable from block definition in inner dsl scope' do - @iv2 = 'iv2'; outer { inner { expect(@iv2).to eq('iv2') } } - end - - it "proxies instance variable assignments in block in inner dsl scope back into block's context" do - @iv2 = 'foo'; outer { inner { @iv2 = 'bar' } }; expect(@iv2).to eq('bar') - end - end - - end - - context 'when DSL context object is a Dispatch pattern' do - class DispatchScope - def params - { :a => 1, :b => 2, :c => 3 } - end - end - - class MessageDispatch - include Singleton - - def initialize - @responders = {} - end - - def add_responder path, &block - @responders[path] = block - end - - def dispatch path, request - Docile.dsl_eval(DispatchScope.new, request, &@responders[path]) - end - end - - def respond(path, &block) - MessageDispatch.instance.add_responder(path, &block) - end - - def send_request(path, request) - MessageDispatch.instance.dispatch(path, request) - end - - it 'dispatches correctly' do - @first = @second = nil - - respond '/path' do |request| - @first = request - end - - respond '/new_bike' do |bike| - @second = "Got a new #{bike}" - end - - def x(y) ; "Got a #{y}"; end - respond '/third' do |third| - expect(x(third)).to eq('Got a third thing') - end - - fourth = nil - respond '/params' do |arg| - fourth = params[arg] - end - - send_request '/path', 1 - send_request '/new_bike', 'ten speed' - send_request '/third', 'third thing' - send_request '/params', :b - - expect(@first).to eq(1) - expect(@second).to eq('Got a new ten speed') - expect(fourth).to eq(2) - end - - end - - end - - describe '.dsl_eval_immutable' do - - context 'when DSL context object is a frozen String' do - let(:original) { "I'm immutable!".freeze } - let!(:result) { execute_non_mutating_dsl_against_string } - - def execute_non_mutating_dsl_against_string - Docile.dsl_eval_immutable(original) do - reverse - upcase - end - end - - it "doesn't modify the original string" do - expect(original).to eq("I'm immutable!") - end - - it 'chains the commands in the block against the DSL context object' do - expect(result).to eq("!ELBATUMMI M'I") - end - end - - context 'when DSL context object is a number' do - let(:original) { 84.5 } - let!(:result) { execute_non_mutating_dsl_against_number } - - def execute_non_mutating_dsl_against_number - Docile.dsl_eval_immutable(original) do - fdiv(2) - floor - end - end - - it 'chains the commands in the block against the DSL context object' do - expect(result).to eq(42) - end - end - end - -end - -describe Docile::FallbackContextProxy do - - describe '#instance_variables' do - subject { create_fcp_and_set_one_instance_variable.instance_variables } - let(:expected_type_of_names) { type_of_ivar_names_on_this_ruby } - let(:actual_type_of_names) { subject.first.class } - let(:excluded) { Docile::FallbackContextProxy::NON_PROXIED_INSTANCE_VARIABLES } - - def create_fcp_and_set_one_instance_variable - fcp = Docile::FallbackContextProxy.new(nil, nil) - fcp.instance_variable_set(:@foo, 'foo') - fcp - end - - def type_of_ivar_names_on_this_ruby - @a = 1 - instance_variables.first.class - end - - it 'returns proxied instance variables' do - expect(subject.map(&:to_sym)).to include(:@foo) - end - - it "doesn't return non-proxied instance variables" do - expect(subject.map(&:to_sym)).not_to include(*excluded) - end - - it 'preserves the type (String or Symbol) of names on this ruby version' do - expect(actual_type_of_names).to eq(expected_type_of_names) - end - end - -end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/spec_helper.rb new/spec/spec_helper.rb --- old/spec/spec_helper.rb 2014-06-15 21:51:29.000000000 +0200 +++ new/spec/spec_helper.rb 1970-01-01 01:00:00.000000000 +0100 @@ -1,30 +0,0 @@ -require File.expand_path('on_what', File.dirname(File.dirname(__FILE__))) - -begin - require 'simplecov' - require 'coveralls' - - # On Ruby 1.9+ use SimpleCov and publish to Coveralls.io - if !on_1_8? - SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ - SimpleCov::Formatter::HTMLFormatter, - Coveralls::SimpleCov::Formatter - ] - SimpleCov.start do - add_filter '/spec/' # exclude test code - add_filter '/vendor/' # exclude gems which are vendored on Travis CI - end - - # Remove Docile, which was required by SimpleCov, to require again later - Object.send(:remove_const, :Docile) - $LOADED_FEATURES.reject! { |f| f =~ /\/docile\// } - end -rescue LoadError - warn 'warning: simplecov/coveralls gems not found; skipping coverage' -end - -lib_dir = File.join(File.dirname(File.dirname(__FILE__)), 'lib') -$LOAD_PATH.unshift lib_dir unless $LOAD_PATH.include? lib_dir - -# Require Docile again, now with coverage enabled on 1.9+ -require 'docile'