binder 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- binder allows you to change the closure in which a proc is executed. This is helpful for anyone developing their own domain specific language in ruby. For more information about proc binding, check out this post I wrote which in turn inspired me to write this gem: http://moonmaster9000.tumblr.com/post/398991873/creating-cleaner-apis
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
- The #bind_to method on a proc instance allows you to change the closure in which the proc is run:
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
- The "tell" method is essentially short hand for an instance_eval on an object - it simply presents you with a more human readable way of using this language feature:
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
- tell fido do
176
+ Tell fido do
54
177
  speak
55
178
  end
56
179
 
57
180
  # or
58
181
 
59
- tell(fido) { speak }
182
+ Tell(fido) { speak }
60
183
 
61
184
  # or
62
185
 
63
- tell(fido, :to) { speak }
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
- tell fido, commands
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"
@@ -1,2 +1,2 @@
1
1
  require 'binder/proc'
2
- require 'binder/object'
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
@@ -1,54 +1,3 @@
1
1
  class Object
2
- def tell(closure, relayed_message=nil, &message)
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
@@ -0,0 +1,4 @@
1
+ require 'binder/proc'
2
+ require 'binder/binder'
3
+ require 'binder/object'
4
+ require 'binder/tell'
@@ -0,0 +1,3 @@
1
+ def Tell(obj, &block)
2
+ obj.instance_eval(&block) if block
3
+ end
@@ -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
@@ -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 and #bind_class_method")
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 "#bind_class_method" do
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
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'binder/proc'
2
3
 
3
4
  describe Proc do
4
5
  describe "#bind_to" do
@@ -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
@@ -1,2 +1 @@
1
1
  $LOAD_PATH.unshift './lib'
2
- require 'binder'
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.6
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-02-23 00:00:00 -05:00
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