binder 0.0.6 → 0.1.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.
- data/README.rdoc +132 -103
- data/lib/binder.rb +1 -1
- data/lib/binder/binder.rb +39 -0
- data/lib/binder/object.rb +1 -52
- data/lib/binder/pervasive.rb +4 -0
- data/lib/binder/tell.rb +3 -0
- data/spec/binder/binder_spec.rb +95 -0
- data/spec/binder/object_spec.rb +6 -27
- data/spec/binder/proc_spec.rb +1 -0
- data/spec/binder/tell_spec.rb +23 -0
- data/spec/spec_helper.rb +0 -1
- metadata +7 -2
data/README.rdoc
CHANGED
@@ -1,18 +1,137 @@
|
|
1
|
+
= Motivation
|
2
|
+
|
3
|
+
If you're tired of creating APIs like this:
|
4
|
+
|
5
|
+
MyApp.configure do |c|
|
6
|
+
c.description "My app's Description"
|
7
|
+
end
|
8
|
+
|
9
|
+
and you long to create APIs like this:
|
10
|
+
|
11
|
+
MyApp.configure do
|
12
|
+
description "My app's Description"
|
13
|
+
end
|
14
|
+
|
15
|
+
Then look no further than this gem.
|
16
|
+
|
1
17
|
= Installation
|
2
18
|
|
3
19
|
# gem install binder
|
4
20
|
|
5
21
|
= Use
|
6
22
|
|
7
|
-
|
23
|
+
In truth, there's no magic to this. If you want to change the context in which a block
|
24
|
+
gets evaluated, you can use a combination of instance_eval and the fact that you can pass a proc
|
25
|
+
to a method as if it were a block by prefixing it with an "&":
|
26
|
+
|
27
|
+
class Dog
|
28
|
+
def do_tricks(&block)
|
29
|
+
instance_eval(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def speak
|
33
|
+
puts "ruff!"
|
34
|
+
end
|
35
|
+
|
36
|
+
def fetch
|
37
|
+
puts "fetching...."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Dog.new.do_tricks do
|
42
|
+
speak
|
43
|
+
fetch
|
44
|
+
end
|
45
|
+
|
46
|
+
If you're creating a large API like the Rails 3 router,
|
47
|
+
you may find yourself writing a whole lot of mehods that run the block passed to it
|
48
|
+
in some other context.
|
49
|
+
|
50
|
+
To dry up the process of creating these methods, you can use the binder gem:
|
51
|
+
|
52
|
+
require 'binder'
|
53
|
+
|
54
|
+
class Dog
|
55
|
+
extend Binder
|
56
|
+
bind :do_tricks, :self
|
57
|
+
|
58
|
+
def speak
|
59
|
+
puts "ruff!"
|
60
|
+
end
|
61
|
+
|
62
|
+
def fetch
|
63
|
+
puts "fetching...."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
Dog.new.do_tricks do
|
68
|
+
speak
|
69
|
+
fetch
|
70
|
+
end
|
71
|
+
|
72
|
+
First, we passed to bind the name of the method that we wanted to pass our block to; the second
|
73
|
+
argument represents the context in which we want our block evaluated. :self, in this case,
|
74
|
+
represents the instance of the Dog that we're calling "do_tricks" on. We could have alternatively
|
75
|
+
passed a symbol representing an instance method or an instance variable.
|
76
|
+
|
77
|
+
If you wanted to bind class method instead of instance methods, you simply have to extend
|
78
|
+
the singleton class:
|
79
|
+
|
80
|
+
require 'binder'
|
81
|
+
|
82
|
+
class Dog
|
83
|
+
class << self
|
84
|
+
extend Binder
|
85
|
+
bind :do_tricks, :self
|
86
|
+
|
87
|
+
def speak
|
88
|
+
puts "ruff!"
|
89
|
+
end
|
90
|
+
|
91
|
+
def fetch
|
92
|
+
puts "fetching...."
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
Dog.do_tricks do
|
98
|
+
speak
|
99
|
+
fetch
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
If you'd rather not have to extend all of your classes with "Binder" everytime you intend
|
105
|
+
to use the "bind" class method, you can require 'binder/pervasive' instead. Note, however, that
|
106
|
+
this will pollute your namespace, so if you've happened to define a "bind" method in any of your
|
107
|
+
existing classes, you may run into issues.
|
108
|
+
|
109
|
+
require 'binder/pervasive'
|
110
|
+
|
111
|
+
class Dog
|
112
|
+
bind :do_tricks, :self
|
113
|
+
|
114
|
+
def speak
|
115
|
+
puts "ruff!"
|
116
|
+
end
|
117
|
+
|
118
|
+
def fetch
|
119
|
+
puts "fetching...."
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
Dog.new.do_tricks do
|
124
|
+
speak
|
125
|
+
fetch
|
126
|
+
end
|
8
127
|
|
9
128
|
|
10
129
|
== Proc#bind_to
|
11
130
|
|
12
|
-
|
131
|
+
In addition to the "bind" method, binder also adds a "bind_to" instance method to the Proc class.
|
132
|
+
It allows you to change the context in which the proc is run:
|
13
133
|
|
14
134
|
require 'binder'
|
15
|
-
# ==> true
|
16
135
|
|
17
136
|
def speak
|
18
137
|
"why should i?"
|
@@ -32,9 +151,13 @@ The #bind_to method on a proc instance allows you to change the closure in which
|
|
32
151
|
command.bind_to(Dog.new).call
|
33
152
|
# ==> "ruff!"
|
34
153
|
|
35
|
-
== Object#tell
|
36
154
|
|
37
|
-
|
155
|
+
== "Tell"
|
156
|
+
|
157
|
+
The "Tell" method is essentially short hand for an instance_eval on an object -
|
158
|
+
it simply presents you with a more human readable way of using this language feature:
|
159
|
+
|
160
|
+
require 'binder/tell'
|
38
161
|
|
39
162
|
class Dog
|
40
163
|
def speak
|
@@ -50,17 +173,17 @@ The "tell" method is essentially short hand for an instance_eval on an object -
|
|
50
173
|
|
51
174
|
# ==> would print "ruff!"
|
52
175
|
|
53
|
-
|
176
|
+
Tell fido do
|
54
177
|
speak
|
55
178
|
end
|
56
179
|
|
57
180
|
# or
|
58
181
|
|
59
|
-
|
182
|
+
Tell(fido) { speak }
|
60
183
|
|
61
184
|
# or
|
62
185
|
|
63
|
-
|
186
|
+
Tell(fido, :to) { speak }
|
64
187
|
|
65
188
|
# ==> would all print "ruff!" - and these are all equivalent to the instance eval above
|
66
189
|
|
@@ -69,100 +192,6 @@ The "tell" method is essentially short hand for an instance_eval on an object -
|
|
69
192
|
fido.instance_eval(&commands)
|
70
193
|
# ==> would print "ruff"
|
71
194
|
|
72
|
-
|
195
|
+
Tell fido, commands
|
73
196
|
# ==> would also print "ruff!"
|
74
197
|
|
75
|
-
== Object##bind
|
76
|
-
|
77
|
-
binder also provides the "#bind" object class method to DRY up your DSL instance methods:
|
78
|
-
|
79
|
-
class Dog
|
80
|
-
# binds a "do_trick" instance method to the instance of Dog
|
81
|
-
bind :do_trick, :self
|
82
|
-
|
83
|
-
def speak
|
84
|
-
"ruff!"
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
Dog.new.do_trick { speak }
|
89
|
-
# ==> "ruff!"
|
90
|
-
|
91
|
-
class Cat
|
92
|
-
# binds a block passed to a "do_trick" instance method to the Cat class
|
93
|
-
bind :do_trick, :class
|
94
|
-
|
95
|
-
class << self
|
96
|
-
def speak
|
97
|
-
"screw you"
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def speak
|
102
|
-
"bugger off"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
Cat.new.do_trick { speak }
|
107
|
-
# ==> "screw you"
|
108
|
-
|
109
|
-
class Kitten
|
110
|
-
bind :do_trick, :mother # binds to @mother instance variable
|
111
|
-
|
112
|
-
def initialize(mom)
|
113
|
-
@mother = mom
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
Kitten.new(Cat).do_trick { speak }
|
118
|
-
# ==> "screw you"
|
119
|
-
|
120
|
-
Kitten.new(Cat.new).do_trick { speak }
|
121
|
-
# ==> "bugger off"
|
122
|
-
|
123
|
-
Kitten.new(Dog.new).do_trick { speak }
|
124
|
-
# ==> "ruff!"
|
125
|
-
|
126
|
-
== Object##bind_class_method
|
127
|
-
|
128
|
-
Whereas the Object##bind method created instance methods, Object##bind_class_method creates class methods:
|
129
|
-
|
130
|
-
class Cat
|
131
|
-
# binds to Cat class, not an instance of Cat
|
132
|
-
bind :do_trick, :class
|
133
|
-
|
134
|
-
class << self
|
135
|
-
def speak
|
136
|
-
"screw you"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
def speak
|
141
|
-
"bugger off"
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
class Lion
|
146
|
-
# binds to Lion, since "self" will be evaluated in the context
|
147
|
-
# of a class method
|
148
|
-
bind_class_method :tame, :self
|
149
|
-
|
150
|
-
# binds to value returned by method "child"
|
151
|
-
bind_class_method :do_trick, :child
|
152
|
-
|
153
|
-
class << self
|
154
|
-
def down_kitty
|
155
|
-
"meow"
|
156
|
-
end
|
157
|
-
|
158
|
-
def child
|
159
|
-
@child ||= Cat
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
Lion.tame { down_kitty }
|
165
|
-
# ==> "meow"
|
166
|
-
|
167
|
-
Lion.do_trick { speak }
|
168
|
-
# ==> "screw you"
|
data/lib/binder.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
require 'binder/proc'
|
2
|
-
require 'binder/
|
2
|
+
require 'binder/binder'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Binder
|
2
|
+
def bind(method_name, closure)
|
3
|
+
raise ArgumentError, "You may only pass symbols to #bind" unless closure.kind_of?(Symbol)
|
4
|
+
|
5
|
+
if closure == :self
|
6
|
+
self.class_eval do
|
7
|
+
eval(
|
8
|
+
"
|
9
|
+
def #{method_name}(&block)
|
10
|
+
if block
|
11
|
+
block.bind_to(self).call
|
12
|
+
end
|
13
|
+
end
|
14
|
+
"
|
15
|
+
)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
self.class_eval do
|
19
|
+
eval(
|
20
|
+
"
|
21
|
+
def #{method_name}(&block)
|
22
|
+
if block
|
23
|
+
# if we're suposed to bind an object returned by a method
|
24
|
+
if self.respond_to?(:#{closure})
|
25
|
+
block.bind_to(self.#{closure}).call
|
26
|
+
# if self has an instance variable
|
27
|
+
elsif @#{closure}
|
28
|
+
block.bind_to(@#{closure}).call
|
29
|
+
else
|
30
|
+
raise StandardError, \"Trying to bind to an unknown method or variable: #{closure}\"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/binder/object.rb
CHANGED
@@ -1,54 +1,3 @@
|
|
1
1
|
class Object
|
2
|
-
|
3
|
-
if relayed_message && relayed_message.kind_of?(Proc)
|
4
|
-
closure.instance_eval(&relayed_message)
|
5
|
-
elsif message
|
6
|
-
closure.instance_eval(&message)
|
7
|
-
else
|
8
|
-
raise StandardError, "What is your command?"
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
class << self
|
13
|
-
def bind_in_context(method_name, closure, eval_context=:class_eval)
|
14
|
-
raise ArgumentError, "You may only pass symbols to #bind and #bind_class_method" unless closure.kind_of?(Symbol)
|
15
|
-
if closure == :self
|
16
|
-
self.send(eval_context) do
|
17
|
-
eval(
|
18
|
-
"
|
19
|
-
def #{method_name}(&block)
|
20
|
-
if block
|
21
|
-
block.bind_to(self).call
|
22
|
-
end
|
23
|
-
end
|
24
|
-
"
|
25
|
-
)
|
26
|
-
end
|
27
|
-
else
|
28
|
-
self.send(eval_context) do
|
29
|
-
eval(
|
30
|
-
"
|
31
|
-
def #{method_name}(&block)
|
32
|
-
if block
|
33
|
-
if self.respond_to?(:#{closure})
|
34
|
-
block.bind_to(self.#{closure}).call
|
35
|
-
elsif @#{closure}
|
36
|
-
block.bind_to(@#{closure}).call
|
37
|
-
else
|
38
|
-
block.bind_to(self.#{closure}).call
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
"
|
43
|
-
)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
alias_method :bind, :bind_in_context
|
49
|
-
|
50
|
-
def bind_class_method(method_name, closure)
|
51
|
-
bind_in_context method_name, closure, :instance_eval
|
52
|
-
end
|
53
|
-
end
|
2
|
+
extend Binder
|
54
3
|
end
|
data/lib/binder/tell.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'binder'
|
3
|
+
|
4
|
+
describe Binder do
|
5
|
+
describe "##bind" do
|
6
|
+
it "should create a new instance method that evaluates the block passed it within the requested closure" do
|
7
|
+
proc do
|
8
|
+
class Platypus
|
9
|
+
extend Binder
|
10
|
+
bind :do_trick, "invalid argument"
|
11
|
+
end
|
12
|
+
end.should raise_error(ArgumentError, "You may only pass symbols to #bind")
|
13
|
+
|
14
|
+
class Dog
|
15
|
+
extend Binder
|
16
|
+
bind :do_trick, :self
|
17
|
+
|
18
|
+
def speak
|
19
|
+
"ruff!"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Dog.new.do_trick { speak }.should == "ruff!"
|
24
|
+
|
25
|
+
class Cat
|
26
|
+
extend Binder
|
27
|
+
bind :do_trick, :class
|
28
|
+
|
29
|
+
class << self
|
30
|
+
def speak
|
31
|
+
"screw you"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def speak
|
36
|
+
"bugger off"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Cat.new.do_trick { speak }.should == "screw you"
|
41
|
+
|
42
|
+
class Kitten
|
43
|
+
extend Binder
|
44
|
+
bind :do_trick, :mother
|
45
|
+
|
46
|
+
def initialize(mom)
|
47
|
+
@mother = mom
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Kitten.new(Cat).do_trick { speak }.should == "screw you"
|
52
|
+
Kitten.new(Cat.new).do_trick { speak }.should == "bugger off"
|
53
|
+
Kitten.new(Dog.new).do_trick { speak }.should == "ruff!"
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#bind in the singleton class" do
|
58
|
+
it "should create a class method responder that binds to either a new class or the return value of a class method" do
|
59
|
+
class Cat
|
60
|
+
extend Binder
|
61
|
+
bind :do_trick, :class
|
62
|
+
|
63
|
+
class << self
|
64
|
+
def speak
|
65
|
+
"screw you"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def speak
|
70
|
+
"bugger off"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Lion
|
75
|
+
class << self
|
76
|
+
extend Binder
|
77
|
+
bind :tame, :self
|
78
|
+
bind :do_trick, :child
|
79
|
+
|
80
|
+
def down_kitty
|
81
|
+
"meow"
|
82
|
+
end
|
83
|
+
|
84
|
+
def child
|
85
|
+
@child ||= Cat
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
Lion.tame {down_kitty}.should == "meow"
|
91
|
+
Lion.do_trick { speak }.should == "screw you"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/spec/binder/object_spec.rb
CHANGED
@@ -1,36 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'binder/pervasive'
|
2
3
|
|
3
4
|
describe Object do
|
4
|
-
describe "#tell" do
|
5
|
-
it "should run the given block or proc inside the requested object closure" do
|
6
|
-
class Baby
|
7
|
-
def say_dada
|
8
|
-
"waaaa"
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
Object.new.tell(Baby.new) { say_dada }.should == "waaaa"
|
13
|
-
Object.tell(Baby.new) { say_dada }.should == "waaaa"
|
14
|
-
tell(Baby.new) { say_dada }.should == "waaaa"
|
15
|
-
|
16
|
-
tell Baby.new, :to do
|
17
|
-
say_dada
|
18
|
-
end
|
19
|
-
|
20
|
-
to_say_dada = proc { say_dada }
|
21
|
-
Object.new.tell(Baby.new, to_say_dada).should == "waaaa"
|
22
|
-
Object.tell(Baby.new, to_say_dada).should == "waaaa"
|
23
|
-
tell(Baby.new, to_say_dada).should == "waaaa"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
5
|
describe "##bind" do
|
28
6
|
it "should create a new instance method that evaluates the block passed it within the requested closure" do
|
29
7
|
proc do
|
30
8
|
class Platypus
|
31
9
|
bind :do_trick, "invalid argument"
|
32
10
|
end
|
33
|
-
end.should raise_error(ArgumentError, "You may only pass symbols to #bind
|
11
|
+
end.should raise_error(ArgumentError, "You may only pass symbols to #bind")
|
34
12
|
|
35
13
|
class Dog
|
36
14
|
bind :do_trick, :self
|
@@ -72,7 +50,7 @@ describe Object do
|
|
72
50
|
|
73
51
|
end
|
74
52
|
|
75
|
-
describe "#
|
53
|
+
describe "#bind in the singleton class" do
|
76
54
|
it "should create a class method responder that binds to either a new class or the return value of a class method" do
|
77
55
|
class Cat
|
78
56
|
bind :do_trick, :class
|
@@ -89,9 +67,10 @@ describe Object do
|
|
89
67
|
end
|
90
68
|
|
91
69
|
class Lion
|
92
|
-
bind_class_method :tame, :self
|
93
|
-
bind_class_method :do_trick, :child
|
94
70
|
class << self
|
71
|
+
bind :tame, :self
|
72
|
+
bind :do_trick, :child
|
73
|
+
|
95
74
|
def down_kitty
|
96
75
|
"meow"
|
97
76
|
end
|
data/spec/binder/proc_spec.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'binder/tell'
|
3
|
+
|
4
|
+
describe "Tell" do
|
5
|
+
it "should raise an error if an object isn't passed to it" do
|
6
|
+
proc {Tell()}.should raise_error(ArgumentError)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should not raise an error if an object, but not a block, is passed to it" do
|
10
|
+
proc {Tell "hello"}.should_not raise_error
|
11
|
+
Tell("hello").should == nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should run an instance_eval on the object" do
|
15
|
+
str = "hello"
|
16
|
+
str.should_receive(:instance_eval).and_return :hello
|
17
|
+
Tell(str) { to_sym }
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should run the block within the context of the object" do
|
21
|
+
Tell("hi") { to_sym }.should == :hi
|
22
|
+
end
|
23
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: binder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Parker
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-03-07 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -24,8 +24,11 @@ extra_rdoc_files:
|
|
24
24
|
files:
|
25
25
|
- README.rdoc
|
26
26
|
- lib/binder.rb
|
27
|
+
- lib/binder/binder.rb
|
27
28
|
- lib/binder/object.rb
|
29
|
+
- lib/binder/pervasive.rb
|
28
30
|
- lib/binder/proc.rb
|
31
|
+
- lib/binder/tell.rb
|
29
32
|
has_rdoc: true
|
30
33
|
homepage: http://github.com/moonmaster9000/binder
|
31
34
|
licenses: []
|
@@ -55,6 +58,8 @@ signing_key:
|
|
55
58
|
specification_version: 3
|
56
59
|
summary: A tool for rebinding your ruby procs, with a helper for DSL creaters.
|
57
60
|
test_files:
|
61
|
+
- spec/binder/binder_spec.rb
|
58
62
|
- spec/binder/object_spec.rb
|
59
63
|
- spec/binder/proc_spec.rb
|
64
|
+
- spec/binder/tell_spec.rb
|
60
65
|
- spec/spec_helper.rb
|