docile 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|