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.
@@ -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