docile 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg
5
+ .idea
6
+ doc
7
+ .yardoc
@@ -0,0 +1,7 @@
1
+ --title 'Docile Documentation'
2
+
3
+ --no-private
4
+ --hide-void-return
5
+
6
+ --markup-provider=redcarpet
7
+ --markup=markdown
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in docile.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Marc Siegel
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,75 @@
1
+ # Docile
2
+
3
+ Definition: *Ready to accept control or instruction; submissive* [[1]]
4
+
5
+ Tired of overly complex DSL libraries and hairy meta-programming?
6
+ Let's make our Ruby DSLs more *docile*...
7
+
8
+ [1]: http://www.google.com/search?q=docile+definition "Google"
9
+
10
+ ## Usage
11
+
12
+ Let's treat an Array's methods as its own DSL:
13
+
14
+ ``` ruby
15
+ Docile.dsl_eval [] do
16
+ push 1
17
+ push 2
18
+ end
19
+ #=> [1, 3]
20
+ ```
21
+
22
+ Mutating (changing) the array is fine, but what you probably really want as your DSL is actually a [Builder Pattern][2].
23
+
24
+ For example, if you have a PizzaBuilder class that can build a pizza:
25
+
26
+ ``` ruby
27
+ @sauce_level = :extra
28
+ pizza = PizzaBuilder.new.cheese.pepperoni.bacon.sauce(@sauce_level).build
29
+ ```
30
+
31
+ Then you can use this PizzaBuilder class as a DSL:
32
+
33
+ ``` ruby
34
+ @sauce_level = :extra
35
+ pizza = Docile.dsl_eval PizzaBuilder.new do
36
+ cheese
37
+ pepperoni
38
+ bacon
39
+ sauce @sauce_level
40
+ end.build
41
+ ```
42
+
43
+ [2]: http://stackoverflow.com/questions/328496/when-would-you-use-the-builder-pattern "Builder Pattern"
44
+
45
+ ## Features
46
+
47
+ 1. method lookup falls back from the DSL object to the block's context
48
+ 2. local variable lookup falls back from the DSL object to the block's context
49
+ 3. instance variables are from the block's context only
50
+
51
+ ## Installation
52
+
53
+ ``` bash
54
+ $ gem install docile
55
+ ```
56
+
57
+ ## Documentation
58
+
59
+ Documentation hosted on *rubydoc.info*: [Docile Documentation](http://rubydoc.info/gems/docile)
60
+ Or, read the code hosted on *github.com*: [Docile Code](https://github.com/ms-ati/docile)
61
+
62
+ ## Note on Patches/Pull Requests
63
+
64
+ * Fork the project.
65
+ * Setup your development environment with: gem install bundler; bundle install
66
+ * Make your feature addition or bug fix.
67
+ * Add tests for it. This is important so I don't break it in a
68
+ future version unintentionally.
69
+ * Commit, do not mess with rakefile, version, or history.
70
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
71
+ * Send me a pull request. Bonus points for topic branches.
72
+
73
+ ## Copyright
74
+
75
+ Copyright (c) 2011 Marc Siegel. See LICENSE for details.
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "github/markup"
3
+ require "redcarpet"
4
+ require "yard"
5
+ require "yard/rake/yardoc_task"
6
+
7
+ YARD::Rake::YardocTask.new do |t|
8
+ OTHER_PATHS = %w()
9
+ t.files = ['lib/**/*.rb', OTHER_PATHS]
10
+ t.options = %w(--markup-provider=redcarpet --markup=markdown)
11
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "docile/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "docile"
7
+ s.version = Docile::VERSION
8
+ s.authors = ["Marc Siegel"]
9
+ s.email = ["msiegel@usainnov.com"]
10
+ s.homepage = "http://docile.github.com"
11
+ s.summary = "Docile keeps your Ruby DSL's tame and well-behaved"
12
+ s.description = "Docile turns any Ruby object into a DSL. Especially useful with the Builder pattern."
13
+
14
+ s.rubyforge_project = "docile"
15
+
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 = ["lib"]
20
+
21
+ s.add_development_dependency "rspec", "~> 2.7.0"
22
+
23
+ # Github flavored markdown in YARD documentation
24
+ # http://blog.nikosd.com/2011/11/github-flavored-markdown-in-yard.html
25
+ s.add_development_dependency "yard"
26
+ s.add_development_dependency "redcarpet"
27
+ s.add_development_dependency "github-markup"
28
+ end
@@ -0,0 +1,31 @@
1
+ require "docile/version"
2
+ require "docile/fallback_context_proxy"
3
+
4
+ module Docile
5
+ # Executes a block in the context of an object whose interface represents a DSL.
6
+ #
7
+ # Example of using an Array as a DSL:
8
+ # Docile.dsl_eval [] do
9
+ # push 1
10
+ # push 2
11
+ # pop
12
+ # push 3
13
+ # end
14
+ # #=> [1, 3]
15
+ #
16
+ # @param dsl [Object] an object whose methods represent a DSL
17
+ # @param block [Proc] a block to execute in the DSL context
18
+ # @return [Object] the dsl object, after execution of the block
19
+ def dsl_eval(dsl, &block)
20
+ block_context = eval("self", block.binding)
21
+ proxy_context = FallbackContextProxy.new(dsl, block_context)
22
+ begin
23
+ block_context.instance_variables.each { |ivar| proxy_context.instance_variable_set(ivar, block_context.instance_variable_get(ivar)) }
24
+ proxy_context.instance_eval(&block)
25
+ ensure
26
+ block_context.instance_variables.each { |ivar| block_context.instance_variable_set(ivar, proxy_context.instance_variable_get(ivar)) }
27
+ end
28
+ dsl
29
+ end
30
+ module_function :dsl_eval
31
+ end
@@ -0,0 +1,52 @@
1
+ require 'set'
2
+
3
+ module Docile
4
+ class FallbackContextProxy
5
+ NON_PROXIED_METHODS = Set[:object_id, :__send__, :__id__, :==, :equal?, :"!", :"!=", :instance_eval,
6
+ :instance_variables, :instance_variable_get, :instance_variable_set,
7
+ :remove_instance_variable]
8
+
9
+ NON_PROXIED_INSTANCE_VARIABLES = Set[:@__receiver__, :@__fallback__]
10
+
11
+ instance_methods.each do |method|
12
+ unless NON_PROXIED_METHODS.include?(method.to_sym)
13
+ undef_method(method)
14
+ end
15
+ end
16
+
17
+ def initialize(receiver, fallback)
18
+ @__receiver__ = receiver
19
+ @__fallback__ = fallback
20
+ end
21
+
22
+ def id
23
+ @__receiver__.__send__(:id)
24
+ end
25
+
26
+ # Special case due to `Kernel#sub`'s existence
27
+ def sub(*args, &block)
28
+ __proxy_method__(:sub, *args, &block)
29
+ end
30
+
31
+ # Special case to allow proxy instance variables
32
+ def instance_variables
33
+ super - NON_PROXIED_INSTANCE_VARIABLES.to_a
34
+ end
35
+
36
+ def method_missing(method, *args, &block)
37
+ __proxy_method__(method, *args, &block)
38
+ end
39
+
40
+ def __proxy_method__(method, *args, &block)
41
+ begin
42
+ @__receiver__.__send__(method.to_sym, *args, &block)
43
+ rescue ::NoMethodError => e
44
+ begin
45
+ @__fallback__.__send__(method.to_sym, *args, &block)
46
+ rescue ::NoMethodError
47
+ raise(e)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module Docile
2
+ VERSION = "0.9.0"
3
+ end
@@ -0,0 +1,94 @@
1
+ require "spec_helper"
2
+
3
+ describe Docile do
4
+
5
+ context :dsl_eval do
6
+
7
+ class InnerDSL
8
+ def initialize; @b = 'b'; end
9
+ attr_accessor :b
10
+ end
11
+
12
+ class OuterDSL
13
+ def initialize; @a = 'a'; end
14
+ attr_accessor :a
15
+ def inner(&block)
16
+ Docile.dsl_eval(InnerDSL.new, &block)
17
+ end
18
+ end
19
+
20
+ def outer(&block)
21
+ Docile.dsl_eval(OuterDSL.new, &block)
22
+ end
23
+
24
+ it "should return the DSL object" do
25
+ Docile.dsl_eval([]) do
26
+ push 1
27
+ push 2
28
+ pop
29
+ push 3
30
+ end.should == [1, 3]
31
+ end
32
+
33
+ context "methods" do
34
+ it "should find method of outer dsl in outer dsl scope" do
35
+ outer { a.should == 'a' }
36
+ end
37
+
38
+ it "should find method of inner dsl in inner dsl scope" do
39
+ outer { inner { b.should == 'b' } }
40
+ end
41
+
42
+ it "should find method of outer dsl in inner dsl scope" do
43
+ outer { inner { a.should == 'a' } }
44
+ end
45
+
46
+ it "should find method of block's context in outer dsl scope" do
47
+ def c; 'c'; end
48
+ outer { c.should == 'c' }
49
+ end
50
+
51
+ it "should find method of block's context in inner dsl scope" do
52
+ def c; 'c'; end
53
+ outer { inner { c.should == 'c' } }
54
+ end
55
+
56
+ it "should find method of outer dsl in preference to block context" do
57
+ def a; 'not a'; end
58
+ outer { a.should == 'a' }
59
+ end
60
+ end
61
+
62
+ context "local variables" do
63
+ it "should find local variable from block context in outer dsl scope" do
64
+ foo = 'foo'
65
+ outer { foo.should == 'foo' }
66
+ end
67
+
68
+ it "should find local variable from block definition in inner dsl scope" do
69
+ bar = 'bar'
70
+ outer { inner { bar.should == 'bar' } }
71
+ end
72
+ end
73
+
74
+ context "instance variables" do
75
+ it "should find instance variable from block definition in outer dsl scope" do
76
+ @iv1 = 'iv1'; outer { @iv1.should == 'iv1' }
77
+ end
78
+
79
+ it "should write instance variable assigned in block into outer dsl scope" do
80
+ @iv1 = 'foo'; outer { @iv1 = 'bar' }; @iv1.should == 'bar'
81
+ end
82
+
83
+ it "should find instance variable from block definition in inner dsl scope" do
84
+ @iv2 = 'iv2'; outer { inner { @iv2.should == 'iv2' } }
85
+ end
86
+
87
+ it "should find instance variable from block definition in inner dsl scope" do
88
+ @iv2 = 'foo'; outer { inner { @iv2 = 'bar' } }; @iv2.should == 'bar'
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+
4
+ test_dir = File.dirname(__FILE__)
5
+ $LOAD_PATH.unshift test_dir unless $LOAD_PATH.include?(test_dir)
6
+
7
+ lib_dir = File.join(File.dirname(test_dir), 'lib')
8
+ $LOAD_PATH.unshift lib_dir unless $LOAD_PATH.include?(lib_dir)
9
+
10
+ require 'docile'
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docile
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.9.0
6
+ platform: ruby
7
+ authors:
8
+ - Marc Siegel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-12-06 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 2.7.0
24
+ type: :development
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: yard
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :development
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: redcarpet
39
+ prerelease: false
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: github-markup
50
+ prerelease: false
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ version_requirements: *id004
59
+ description: Docile turns any Ruby object into a DSL. Especially useful with the Builder pattern.
60
+ email:
61
+ - msiegel@usainnov.com
62
+ executables: []
63
+
64
+ extensions: []
65
+
66
+ extra_rdoc_files: []
67
+
68
+ files:
69
+ - .gitignore
70
+ - .yardopts
71
+ - Gemfile
72
+ - LICENSE
73
+ - README.md
74
+ - Rakefile
75
+ - docile.gemspec
76
+ - lib/docile.rb
77
+ - lib/docile/fallback_context_proxy.rb
78
+ - lib/docile/version.rb
79
+ - spec/docile_spec.rb
80
+ - spec/spec_helper.rb
81
+ homepage: http://docile.github.com
82
+ licenses: []
83
+
84
+ post_install_message:
85
+ rdoc_options: []
86
+
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ requirements: []
102
+
103
+ rubyforge_project: docile
104
+ rubygems_version: 1.8.11
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Docile keeps your Ruby DSL's tame and well-behaved
108
+ test_files:
109
+ - spec/docile_spec.rb
110
+ - spec/spec_helper.rb
111
+ has_rdoc: