docile 1.0.4 → 1.0.5
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 +4 -4
- data/HISTORY.md +5 -0
- data/README.md +5 -0
- data/Rakefile +11 -5
- data/lib/docile.rb +34 -15
- data/lib/docile/fallback_context_proxy.rb +23 -0
- data/lib/docile/version.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75054a395059cd55ba3132c00dc49ae15cc6b954
|
4
|
+
data.tar.gz: 71f6114e6ac5d727240bd50c8e967127af688a53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57476b49adfb0e49f909c391420f6c7b1fdacd2fcf5aaced53443bd3c138ca6ca70ba047b261c9c20dc899a1a35e70950d3582f0dc267d33a2d5748dfd53aee1
|
7
|
+
data.tar.gz: 46b4708db7b9cb68b3f1415edb0f07d5dcc49ba6d86831a51b96e1774abcea346aa3f7412e106572d32893f2473ff7ccddae0596470efad85a1c08a1d737b91c
|
data/HISTORY.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# HISTORY
|
2
2
|
|
3
|
+
## [v1.0.5 (Jul 28, 2013)](http://github.com/ms-ati/docile/compare/v1.0.4...v1.0.5)
|
4
|
+
|
5
|
+
- achieve 100% yard docs coverage
|
6
|
+
- fix rendering of docs at http://rubydoc.info/gems/docile
|
7
|
+
|
3
8
|
## [v1.0.4 (Jul 25, 2013)](http://github.com/ms-ati/docile/compare/v1.0.3...v1.0.4)
|
4
9
|
|
5
10
|
- simplify and clarify code
|
data/README.md
CHANGED
@@ -30,6 +30,7 @@ end
|
|
30
30
|
```
|
31
31
|
|
32
32
|
No problem, just define the method `with_array` like this:
|
33
|
+
|
33
34
|
``` ruby
|
34
35
|
def with_array(arr=[], &block)
|
35
36
|
Docile.dsl_eval(arr, &block)
|
@@ -43,6 +44,7 @@ Easy!
|
|
43
44
|
Mutating (changing) an Array instance is fine, but what usually makes a good DSL is a [Builder Pattern][2].
|
44
45
|
|
45
46
|
For example, let's say you want a DSL to specify how you want to build a Pizza:
|
47
|
+
|
46
48
|
```ruby
|
47
49
|
@sauce_level = :extra
|
48
50
|
|
@@ -55,6 +57,7 @@ end
|
|
55
57
|
```
|
56
58
|
|
57
59
|
And let's say we have a PizzaBuilder, which builds a Pizza like this:
|
60
|
+
|
58
61
|
```ruby
|
59
62
|
Pizza = Struct.new(:cheese, :pepperoni, :bacon, :sauce)
|
60
63
|
|
@@ -89,6 +92,7 @@ It's just that easy!
|
|
89
92
|
Parameters can be passed to the DSL block.
|
90
93
|
|
91
94
|
Supposing you want to make some sort of cheap [Sinatra][3] knockoff:
|
95
|
+
|
92
96
|
```ruby
|
93
97
|
@last_request = nil
|
94
98
|
respond '/path' do |request|
|
@@ -106,6 +110,7 @@ end
|
|
106
110
|
```
|
107
111
|
|
108
112
|
You'd put together a dispatcher something like this:
|
113
|
+
|
109
114
|
```ruby
|
110
115
|
require 'singleton'
|
111
116
|
|
data/Rakefile
CHANGED
@@ -1,7 +1,17 @@
|
|
1
|
+
require "rake/clean"
|
1
2
|
require "bundler/gem_tasks"
|
2
3
|
require "rspec/core/rake_task"
|
3
4
|
|
4
|
-
#
|
5
|
+
# Default task for `rake` is to run rspec
|
6
|
+
task :default => [:spec]
|
7
|
+
|
8
|
+
# Use default rspec rake task
|
9
|
+
RSpec::Core::RakeTask.new
|
10
|
+
|
11
|
+
# Configure `rake clobber` to delete all generated files
|
12
|
+
CLOBBER.include("pkg", "doc", "coverage")
|
13
|
+
|
14
|
+
# Only configure yard doc generation when *not* on Travis
|
5
15
|
if ENV['CI'] != 'true'
|
6
16
|
require "github/markup"
|
7
17
|
require "redcarpet"
|
@@ -14,7 +24,3 @@ if ENV['CI'] != 'true'
|
|
14
24
|
t.options = %w(--markup-provider=redcarpet --markup=markdown --main=README.md)
|
15
25
|
end
|
16
26
|
end
|
17
|
-
|
18
|
-
RSpec::Core::RakeTask.new
|
19
|
-
|
20
|
-
task :default => [:spec]
|
data/lib/docile.rb
CHANGED
@@ -1,34 +1,53 @@
|
|
1
1
|
require "docile/version"
|
2
2
|
require "docile/fallback_context_proxy"
|
3
3
|
|
4
|
+
# Docile keeps your Ruby DSLs tame and well-behaved
|
5
|
+
# @see http://ms-ati.github.com/docile/
|
4
6
|
module Docile
|
5
|
-
#
|
7
|
+
# Execute a block in the context of an object whose methods represent the
|
8
|
+
# commands in a DSL.
|
6
9
|
#
|
7
|
-
#
|
10
|
+
# @note Use with an *imperative* DSL (commands modify the context object)
|
8
11
|
#
|
9
|
-
#
|
10
|
-
# push 1
|
11
|
-
# push 2
|
12
|
-
# pop
|
13
|
-
# push 3
|
14
|
-
# end
|
15
|
-
# #=> [1, 3]
|
12
|
+
# Use this method to execute an *imperative* DSL, which means that:
|
16
13
|
#
|
17
|
-
#
|
14
|
+
# 1. each command mutates the state of the DSL context object
|
15
|
+
# 2. the return values of the commands are ignored
|
16
|
+
#
|
17
|
+
# @example Use a String as a DSL
|
18
|
+
# Docile.dsl_eval("Hello, world!") do
|
19
|
+
# reverse!
|
20
|
+
# upcase!
|
21
|
+
# end
|
22
|
+
# #=> "!DLROW ,OLLEH"
|
23
|
+
#
|
24
|
+
# @example Use an Array as a DSL
|
25
|
+
# Docile.dsl_eval([]) do
|
26
|
+
# push 1
|
27
|
+
# push 2
|
28
|
+
# pop
|
29
|
+
# push 3
|
30
|
+
# end
|
31
|
+
# #=> [1, 3]
|
32
|
+
#
|
33
|
+
# @param dsl [Object] context object whose methods make up the DSL
|
18
34
|
# @param args [Array] arguments to be passed to the block
|
19
|
-
# @
|
20
|
-
#
|
35
|
+
# @yield the block of DSL commands to be executed against the
|
36
|
+
# `dsl` context object
|
37
|
+
# @return [Object] the `dsl` context object after executing the block
|
21
38
|
def dsl_eval(dsl, *args, &block)
|
22
39
|
block_context = eval("self", block.binding)
|
23
40
|
proxy_context = FallbackContextProxy.new(dsl, block_context)
|
24
41
|
begin
|
25
42
|
block_context.instance_variables.each do |ivar|
|
26
|
-
|
43
|
+
value_from_block = block_context.instance_variable_get(ivar)
|
44
|
+
proxy_context.instance_variable_set(ivar, value_from_block)
|
27
45
|
end
|
28
|
-
proxy_context.instance_exec(*args
|
46
|
+
proxy_context.instance_exec(*args, &block)
|
29
47
|
ensure
|
30
48
|
block_context.instance_variables.each do |ivar|
|
31
|
-
|
49
|
+
value_from_dsl_proxy = proxy_context.instance_variable_get(ivar)
|
50
|
+
block_context.instance_variable_set(ivar, value_from_dsl_proxy)
|
32
51
|
end
|
33
52
|
end
|
34
53
|
dsl
|
@@ -1,28 +1,51 @@
|
|
1
1
|
require 'set'
|
2
2
|
|
3
3
|
module Docile
|
4
|
+
# A proxy object with a primary receiver as well as a secondary
|
5
|
+
# fallback receiver.
|
6
|
+
#
|
7
|
+
# Will attempt to forward all method calls first to the primary receiver,
|
8
|
+
# and then to the fallback receiver if the primary does not handle that
|
9
|
+
# method.
|
4
10
|
class FallbackContextProxy
|
11
|
+
# The set of methods which will **not** be proxied, but instead answered
|
12
|
+
# by this object directly.
|
5
13
|
NON_PROXIED_METHODS = Set[:__send__, :object_id, :__id__, :==, :equal?,
|
6
14
|
:"!", :"!=", :instance_exec, :instance_variables,
|
7
15
|
:instance_variable_get, :instance_variable_set,
|
8
16
|
:remove_instance_variable]
|
9
17
|
|
18
|
+
# The set of instance variables which are local to this object and hidden.
|
19
|
+
# All other instance variables will be copied in and out of this object
|
20
|
+
# from the scope in which this proxy was created.
|
10
21
|
NON_PROXIED_INSTANCE_VARIABLES = Set[:@__receiver__, :@__fallback__]
|
11
22
|
|
23
|
+
# Undefine all instance methods except those in {NON_PROXIED_METHODS}
|
12
24
|
instance_methods.each do |method|
|
13
25
|
undef_method(method) unless NON_PROXIED_METHODS.include?(method.to_sym)
|
14
26
|
end
|
15
27
|
|
28
|
+
# @param [Object] receiver the primary proxy target to which all methods
|
29
|
+
# initially will be forwarded
|
30
|
+
# @param [Object] fallback the fallback proxy target to which any methods
|
31
|
+
# not handled by `receiver` will be forwarded
|
16
32
|
def initialize(receiver, fallback)
|
17
33
|
@__receiver__ = receiver
|
18
34
|
@__fallback__ = fallback
|
19
35
|
end
|
20
36
|
|
37
|
+
# @return [Array<Symbol>] Instance variable names, excluding
|
38
|
+
# {NON_PROXIED_INSTANCE_VARIABLES}
|
39
|
+
#
|
40
|
+
# @note on Ruby 1.8.x, the instance variable names are actually of
|
41
|
+
# type `String`.
|
21
42
|
def instance_variables
|
22
43
|
# Ruby 1.8.x returns string names, convert to symbols for compatibility
|
23
44
|
super.select { |v| !NON_PROXIED_INSTANCE_VARIABLES.include?(v.to_sym) }
|
24
45
|
end
|
25
46
|
|
47
|
+
# Proxy all methods, excluding {NON_PROXIED_METHODS}, first to `receiver`
|
48
|
+
# and then to `fallback` if not found.
|
26
49
|
def method_missing(method, *args, &block)
|
27
50
|
@__receiver__.__send__(method.to_sym, *args, &block)
|
28
51
|
rescue ::NoMethodError => e
|
data/lib/docile/version.rb
CHANGED
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.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc Siegel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|