eager_beaver 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -5,12 +5,122 @@
5
5
  `EagerBeaver` provides an interface for adding `#method_missing`-related abilities
6
6
  to a class or module.
7
7
 
8
+ ## Baseline Implementation
9
+
10
+ The following is a bare-bones implementation of class which defines `#method_missing`:
11
+
12
+ ```ruby
13
+ class NeedsMethods
14
+ def method_missing(method_name, *args, &block)
15
+ if data = NeedsMethods.match_pattern1(method_name)
16
+ puts "pattern1: #{data[:val]}"
17
+ elsif data = NeedsMethods.match_pattern2(method_name)
18
+ puts "pattern2: #{data[:val1]} #{data[:val2]}"
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def respond_to_missing?(method_name, include_private=false)
25
+ NeedsMethods.match_pattern1(method_name) || NeedsMethods.match_pattern2(method_name)
26
+ end
27
+
28
+ def self.match_pattern1(method_name)
29
+ return {val: $1} if /\Apattern1_(\w+)/ =~ method_name
30
+ end
31
+
32
+ def self.match_pattern2(method_name)
33
+ return {val1: $1, val2: $2} if /\Apattern2_(\w+)_(\w+)/ =~ method_name
34
+ end
35
+ end
36
+
37
+ nm1 = NeedsMethods.new
38
+ puts "#{nm1.methods.grep /pattern/}"
39
+ # => [] ## overriding #method_missing doesn't actually add methods
40
+ puts "#{nm1.respond_to? :pattern1_match}"
41
+ # => true ## #respond_to_missing? in action!
42
+ puts "#{nm1.method :pattern1_match}"
43
+ # => #<Method: NeedsMethods#pattern1_match> ## #respond_to_missing? in action!
44
+ nm1.pattern1_match
45
+ # => pattern1: match
46
+
47
+ nm2 = NeedsMethods.new
48
+ puts "#{nm2.methods.grep /pattern/}"
49
+ # => [] ## missing method was NOT added!
50
+ nm2.pattern1_match
51
+ # => pattern1: match
52
+ nm2.pattern2_another_match
53
+ # => pattern2: another match
54
+ puts "#{nm1.methods.grep /pattern/}"
55
+ # => [] ## missing methods were NOT added!
56
+ puts "#{nm2.methods.grep /pattern/}"
57
+ # => [] ## missing methods were NOT added!
58
+
59
+ nm.blah
60
+ # => undefined method `blah' for #<NeedsMethods:0x007fb37b086548> (NoMethodError)
61
+ ```
62
+
63
+ ## Downsides to the Baseline Implementation
64
+
65
+ ### It's easy to forget something
66
+
67
+ Changes to `#method_missing` should be accompanied by corresponding changes to `#respond_to_missing?`,
68
+ which allows instances of a class to correcly respond to `#respond_to?` and `#method` calls. It's
69
+ easy to overlook this detail since it's likely not the primary focus of adding handlers to
70
+ `#method_missing`.
71
+
72
+ It's also easy to forget the call to `super` when no pattern match is found - in which case all
73
+ unmatched method patterns are silently handled!
74
+
75
+ ### Extension requires changes in multiple places
76
+
77
+ To add handling of another method pattern, the following changes need to be made:
78
+ - addition of another `elsif` block in `#method_missing`
79
+ - addition of another `||`-ed value in `#respond_to_missing?`
80
+ - addition of another pattern-matching class method
81
+
82
+ ### Large method size and method proliferation
83
+
84
+ As more and more method patterns are added, `#method_missing` and `#respond_to_missing?` will grow
85
+ endlessly, as will the number of pattern-matching class methods.
86
+
87
+ ### Tight coupling
88
+
89
+ Pattern-matching class methods and their corresponding `elsif` blocks in `#method_missing` are
90
+ tightly coupled, despite their spatial separation in the code.
91
+
92
+ The shared code in `#method_missing` and `#respond_to_missing?` means that changes to one pattern
93
+ handler can break another if not done properly.
94
+
95
+ ### Handled methods are not added to the class
96
+
97
+ Each time a matched method is called, the entire `#method_missing` infrastructure is executed.
98
+
99
+ ### Dynamic updates
100
+
101
+ The baseline implementation assumes that all method patterns should be handled at all times,
102
+ which is not always the case. Sometimes the matched patterns are derived from data not
103
+ available to the class until the code is executing. Correctly redefining (or perpetually
104
+ re-aliasing) `#method_missing` and `#respond_to_missing?` can get tricky fast.
105
+
106
+ ## Correcting the Downsides
107
+
108
+ Most of the downsides to the baseline implementation can be solved by adding an array
109
+ of `MethodHandler`s to the class. Each `MethodHandler` has two parts: one which checks
110
+ if the missing method should be handled, and one which does the work. `#method_missing`
111
+ and `#respond_to_missing?` could then be rewritten to iterate over the `MethodHandler`
112
+ array and act accordingly.
113
+
114
+ `EagerBeaver` does this (essentially) but goes one step further: it actually adds the
115
+ missing method to the including class and invokes it so that future calls to that
116
+ method won't need to invoke the `#method_missing` infrastructure.
117
+
8
118
  ## Key Features
9
119
 
10
- - Method matchers can be added dynamically and cumulatively, reducing the risk
120
+ - Method matchers can be added dynamically and independently, reducing the risk
11
121
  of accidentally altering or removing previously-added functionality.
12
122
  - Matched methods are automatically reflected in calls to `#respond_to?` and
13
- `#method`, DRY-ing up code by centralizing method-matching logic.
123
+ `#method`.
14
124
  - Matched methods are automatically added to the including class/module and
15
125
  invoked. Subsequent calls won't trigger `#method_missing`.
16
126
  - When a method cannot be matched, `super`'s `#method_missing` is automatically
@@ -34,9 +144,8 @@ Or install it yourself as:
34
144
 
35
145
  ### Inclusion
36
146
 
37
- Any class or module which includes `EagerBeaver` will gain the `add_method_matcher`
38
- pseudo-keyword, which [indirectly] yields an `EagerBeaver::MethodMatcher` to the
39
- given block:
147
+ Any class or module which includes `EagerBeaver` will gain the `add_method_handler`
148
+ pseudo-keyword, which [indirectly] yields a `MethodHandler` to the given block:
40
149
 
41
150
  ```ruby
42
151
  require 'eager_beaver'
@@ -44,22 +153,22 @@ require 'eager_beaver'
44
153
  class NeedsMethods
45
154
  include EagerBeaver
46
155
 
47
- add_method_matcher do |mm|
156
+ add_method_handler do |mh|
48
157
  ...
49
158
  end
50
159
  end
51
160
  ```
52
161
 
53
- In this case, the resulting `MethodMatcher` is added to the end of a `MethodMatcher` list
162
+ In this case, the resulting `MethodHandler` is added to the end of a `MethodHandler` list
54
163
  associated with `NeedsMethods`.
55
164
 
56
- Each `MethodMatcher` needs two things: a lambda for matching missing method names
57
- and a lambda for creating the code for any method names it matches:
165
+ Each `MethodHandler` needs two things: a lambda for matching missing method names
166
+ and a lambda for handling any method names it matches:
58
167
 
59
168
  ```ruby
60
- add_method_matcher do |mm|
61
- mm.match = lambda { ... }
62
- mm.new_method_code = lambda { ...}
169
+ add_method_handler do |mh|
170
+ mh.match = lambda { ... }
171
+ mh.handle = lambda { ... }
63
172
  end
64
173
  end
65
174
  ```
@@ -67,14 +176,12 @@ end
67
176
  ### Matching
68
177
 
69
178
  The `match` lambda should return a true value if the missing method name is one
70
- can be handled by the `MethodMatcher`. The following example will match
71
- missing methods of the form `#make_<attr_name>`:
179
+ can be handled by the `MethodHandler`. The following example will match
180
+ missing methods of the form `#pattern1_<data>`:
72
181
 
73
182
  ```ruby
74
- mm.match = lambda {
75
- /\Amake_(\w+)\z/ =~ context.missing_method_name
76
- context.attr_name = Regexp.last_match ? Regexp.last_match[1] : nil
77
- return Regexp.last_match
183
+ mh.match = lambda {
184
+ context.data = $1 if /\Apattern1_(\w+)/ =~ context.missing_method_name
78
185
  }
79
186
  ```
80
187
 
@@ -84,173 +191,95 @@ As the example shows, each `MethodMatcher` contains a `context` which provides:
84
191
 
85
192
  - the name of the missing method (`context.missing_method_name`)
86
193
  - the original method receiver instance (`context.original_receiver`)
87
- - a place to stash information (`context.<attr_name>` and `context.<attr_name>=`)
194
+ - a place to stash information (dynamically-generated accessors `context.<attr_name>` and `context.<attr_name>=`)
88
195
 
89
- This `context` is shared between the `match` and `new_method_code` lambdas, and
196
+ This `context` is shared between the `match` and `handle` lambdas, and
90
197
  is reset between uses of each `MethodMatcher`.
91
198
 
92
- ### Code Generation
199
+ ### Handling
93
200
 
94
- The `new_method_code` lambda should return a string which will create the
201
+ The `handle` lambda should return a string which will create the
95
202
  missing method in `NeedsMethods`:
96
203
 
97
204
  ```ruby
98
- mm.new_method_code = lambda {
99
- code = %Q{
100
- def #{context.missing_method_name}(arg)
101
- puts "method \##{context.missing_method_name} has been called"
102
- puts "\##{context.missing_method_name} was originally called on #{context.original_receiver}"
103
- puts "#{context.attr_name} was passed from matching to code generation"
104
- puts "the current call has arguments: \#{arg}"
105
- return "result = \#{arg}"
106
- end
107
- }
108
- return code
205
+ mh.handle = lambda {
206
+ %Q{ def #{context.missing_method_name}
207
+ puts "pattern1: #{context.data}"
208
+ end }
109
209
  }
110
210
  ```
111
211
 
112
212
  As the example shows, it is perfectly reasonable to take advantage of work done
113
- by the `match` lambda (in this case, the parsing of `<attr_name>`).
213
+ by the `match` lambda (in this case, the parsing of `<data>`).
114
214
 
115
215
  After the generated code is inserted into `NeedsMethods`, the missing method
116
216
  call is resent to the original receiver.
117
217
 
118
218
  ### Complete Example
119
219
 
220
+ The following is the baseline implementation above using `EagerBeaver`:
221
+
120
222
  ```ruby
121
223
  require 'eager_beaver'
122
224
 
123
225
  class NeedsMethods
124
226
  include EagerBeaver
125
227
 
126
- add_method_matcher do |mm|
127
- mm.match = lambda {
128
- /\Amake_(\w+)\z/ =~ context.missing_method_name
129
- context.attr_name = Regexp.last_match ? Regexp.last_match[1] : nil
130
- return Regexp.last_match
228
+ add_method_handler do |mh|
229
+ mh.match = lambda {
230
+ context.data = $1 if /\Apattern1_(\w+)/ =~ context.missing_method_name
131
231
  }
232
+ mh.handle = lambda {
233
+ %Q{ def #{context.missing_method_name}
234
+ puts "pattern1: #{context.data}"
235
+ end }
236
+ }
237
+ end
132
238
 
133
- mm.new_method_code = lambda {
134
- code = %Q{
135
- def #{context.missing_method_name}(arg)
136
- puts "method \##{context.missing_method_name} has been called"
137
- puts "\##{context.missing_method_name} was originally called on #{context.original_receiver}"
138
- puts "#{context.attr_name} was passed from matching to code generation"
139
- puts "the current call has arguments: \#{arg}"
140
- return "result = \#{arg}"
141
- end
142
- }
143
- return code
239
+ add_method_handler do |mh|
240
+ mh.match = lambda {
241
+ context.data = {val1: $1, val2: $2} if /\Apattern2_(\w+)_(\w+)/ =~ context.missing_method_name
242
+ }
243
+ mh.handle = lambda {
244
+ %Q{ def #{context.missing_method_name}
245
+ puts "pattern2: #{context.data[:val1]} #{context.data[:val2]}"
246
+ end }
144
247
  }
145
248
  end
146
249
  end
147
- ```
148
-
149
- ## Execution
150
-
151
- Given the `NeedsMethods` class in the example above, let's work through the
152
- following code:
153
250
 
154
- ```ruby
155
251
  nm1 = NeedsMethods.new
156
- puts nm1.make_thingy(10)
157
- puts nm1.make_widget("hi")
158
-
159
- nm2 = NeedsMethods.new
160
- puts nm2.make_thingy(20)
161
- puts nm2.make_widget("hello")
162
-
163
- nm2.dont_make_this
164
- ```
165
-
166
- As instances of `NeedsMethods`, `nm1` and `nm2` will automatically hande
167
- methods of the form `#make_<attr_name>`.
168
-
169
- The line:
170
- ```ruby
171
- puts nm1.make_thingy(10)
172
- ```
173
- will trigger `nm1`'s `#method_missing`, which `NeedsMethods` implements thanks to
174
- `EagerBeaver`. Each `MethodMatcher` associated with `EagerBeaver` is run against
175
- the method name `make_thingy`, and sure enough one matches. This causes the
176
- following methods to be inserted to `NeedsMethods`:
177
- ```ruby
178
- def make_thingy(arg)
179
- puts "method #make_thingy has been called"
180
- puts "#make_thingy was originally called on #<NeedsMethods:0x007fa1bc17f498>"
181
- puts "thingy was passed from matching to code generation"
182
- puts "the current call has arguments: #{arg}"
183
- return "result = #{arg}"
184
- end
185
- ```
186
- and when `#make_thingy` is resent to `nm1`, the existing method is called and
187
- outputs:
188
252
 
189
- > method \#make_thingy has been called<br/>
190
- > \#make_thingy was originally called on \#\<NeedsMethods:0x007fa1bc17f498\><br/>
191
- > thingy was passed from matching to code generation<br/>
192
- > the current call has arguments: 10<br/>
193
- > result = 10
253
+ puts "#{nm1.methods.grep /pattern/}"
254
+ # => [] ## overriding #method_missing doesn't actually add methods
255
+ puts "#{nm1.respond_to? :pattern1_match}"
256
+ # => true ## #respond_to_missing? in action!
257
+ puts "#{nm1.method :pattern1_match}"
258
+ # => #<Method: NeedsMethods#pattern1_match> ## #respond_to_missing? in action!
259
+ nm1.pattern1_match
260
+ # => pattern1: match
194
261
 
195
- Similarly, the line:
196
- ```ruby
197
- puts nm1.make_widget("hi")
198
- ```
199
- generates the code:
200
- ```ruby
201
- def make_widget(arg)
202
- puts "method #make_widget has been called"
203
- puts "#make_widget was originally called on #<NeedsMethods:0x007fa1bc17f498>"
204
- puts "widget was passed from matching to code generation"
205
- puts "the current call has arguments: #{arg}"
206
- return "result = #{arg}"
207
- end
208
- ```
209
- and outputs:
210
- > method \#make_widget has been called<br/>
211
- > \#make_widget was originally called on \#\<NeedsMethods:0x007fa1bc17f498\><br/>
212
- > widget was passed from matching to code generation<br/>
213
- > the current call has arguments: hi<br/>
214
- > result = hi
215
-
216
- Note that the following lines do NOT trigger `#method_missing` because both methods
217
- have already been added to `NeedsMethods`:
218
- ```ruby
219
- puts nm2.make_thingy(20)
220
- puts nm2.make_widget("hello")
221
- ```
222
- This can be seen by examining the identity of the original receiver in the output:
223
-
224
- > **method \#make_thingy has been called**<br/>
225
- > **\#make_thingy was originally called on \#\<NeedsMethods:0x007fa1bc17f498\>**<br/>
226
- > **thingy was passed from matching to code generation**<br/>
227
- > the current call has arguments: 20<br/>
228
- > result = 20
229
-
230
- > **method \#make_widget has been called**<br/>
231
- > **\#make_widget was originally called on \#\<NeedsMethods:0x007fa1bc17f498\>**<br/>
232
- > **widget was passed from matching to code generation**<br/>
233
- > the current call has arguments: hello<br/>
234
- > result = hello
235
-
236
- String substitutions which were part of the generated code body (emphasized)
237
- reflect the circumstances of the first set of method calls, as opposed to
238
- those which reflect the current call's argument.
262
+ nm2 = NeedsMethods.new
239
263
 
240
- Finally, the call:
264
+ puts "#{nm2.methods.grep /pattern/}"
265
+ # => [:pattern1_match] ## missing method added to NeedsMethods!
266
+ nm2.pattern1_match
267
+ # => pattern1: match ## no call to #method_missing
268
+ nm2.pattern2_another_match
269
+ # => pattern2: another match
270
+ puts "#{nm1.methods.grep /pattern/}"
271
+ # => [:pattern1_match, :pattern2_another_match]
272
+ puts "#{nm2.methods.grep /pattern/}"
273
+ # => [:pattern1_match, :pattern2_another_match]
274
+
275
+ nm2.blah
276
+ # => undefined method `blah' for #<NeedsMethods:0x007fefac1a8080> (NoMethodError)
241
277
  ```
242
- nm2.dont_make_this
243
- ```
244
- will cause `NeedsMethods` to examine all of its `MethodMatcher`s and finally call
245
- `super`'s `#method_missing`. Because no superclass of `NeedsMethods` handles
246
- `#dont_make_this`, the output is:
247
-
248
- > undefined method `dont_make_this' for \#\<NeedsMethods:0x007f8e2b991f90\> (NoMethodError)
249
278
 
250
279
  ## Contributing
251
280
 
252
281
  1. Fork it
253
282
  2. Create your feature branch (`git checkout -b my-new-feature`)
254
- 3. Commit your changes (`git commit -am 'Added some feature'`)
283
+ 3. Comhit your changes (`git commit -am 'Added some feature'`)
255
284
  4. Push to the branch (`git push origin my-new-feature`)
256
285
  5. Create new Pull Request
@@ -1,5 +1,5 @@
1
1
  require "eager_beaver/version"
2
- require "eager_beaver/method_matcher"
2
+ require "eager_beaver/method_handler"
3
3
 
4
4
  module EagerBeaver
5
5
 
@@ -8,10 +8,10 @@ module EagerBeaver
8
8
  end
9
9
 
10
10
  def method_missing(method_name, *args, &block)
11
- self.class.method_matchers.each do |method_matcher|
12
- mm = configure_matcher method_matcher
13
- if mm.match?(method_name)
14
- method_string = mm.evaluate mm.new_method_code_maker
11
+ self.class.method_handlers.each do |method_handler|
12
+ mh = configure_handler method_handler
13
+ if mh.handles?(method_name)
14
+ method_string = mh.evaluate mh.handle
15
15
  self.class.class_eval method_string, __FILE__, __LINE__ + 1
16
16
  return self.send(method_name, *args, &block)
17
17
  end
@@ -20,26 +20,26 @@ module EagerBeaver
20
20
  end
21
21
 
22
22
  def respond_to_missing?(method_name, include_private=false)
23
- self.class.method_matchers.each do |method_matcher|
24
- mm = configure_matcher method_matcher
25
- return true if mm.match?(method_name)
23
+ self.class.method_handlers.each do |method_handler|
24
+ mh = configure_handler method_handler
25
+ return true if mh.handles?(method_name)
26
26
  end
27
27
  super
28
28
  end
29
29
 
30
- def configure_matcher(matcher)
31
- mm = matcher.dup
32
- mm.original_receiver = self
33
- mm.original_receiver.class.context = mm
30
+ def configure_handler(handler)
31
+ mh = handler.dup
32
+ mh.original_receiver = self
33
+ mh.original_receiver.class.context = mh
34
34
  end
35
35
 
36
36
  module ClassMethods
37
- def method_matchers
38
- @method_matchers ||= []
37
+ def method_handlers
38
+ @method_handlers ||= []
39
39
  end
40
40
 
41
- def add_method_matcher(&block)
42
- method_matchers << MethodMatcher.new(&block)
41
+ def add_method_handler(&block)
42
+ method_handlers << MethodHandler.new(&block)
43
43
  end
44
44
 
45
45
  attr_accessor :context
@@ -0,0 +1,52 @@
1
+ module EagerBeaver
2
+
3
+ class MethodHandler
4
+
5
+ attr_accessor :original_receiver
6
+ attr_accessor :missing_method_name
7
+ attr_accessor :match
8
+ attr_accessor :handle
9
+
10
+ def initialize(&block)
11
+ block.call(self)
12
+
13
+ raise "match must be given" \
14
+ if match.nil?
15
+ raise "match must be a lambda" \
16
+ unless match.lambda?
17
+
18
+ raise "handle must be given" \
19
+ if handle.nil?
20
+ raise "handle must be a lambda" \
21
+ unless handle.lambda?
22
+
23
+ self
24
+ end
25
+
26
+ def handles?(method_name)
27
+ self.missing_method_name = method_name.to_s
28
+ return evaluate(match)
29
+ end
30
+
31
+ def evaluate(inner)
32
+ outer = lambda { |*args|
33
+ args.shift
34
+ inner.call(*args)
35
+ }
36
+ self.instance_eval &outer
37
+ end
38
+
39
+ def method_missing(method_name, *args, &block)
40
+ if /\A(?<attr_name>[a-zA-Z]\w*)=?\z/ =~ method_name
41
+ code = %Q{
42
+ attr_accessor :#{attr_name}
43
+ }
44
+ self.singleton_class.instance_eval code, __FILE__, __LINE__ + 1
45
+ return self.send(method_name, *args, &block)
46
+ end
47
+ super
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -1,3 +1,3 @@
1
1
  module EagerBeaver
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -7,13 +7,13 @@ describe "EagerBeaver matcher context" do
7
7
  klass = Class.new do
8
8
  include EagerBeaver
9
9
 
10
- add_method_matcher do |mm|
11
- mm.matcher = lambda {
10
+ add_method_handler do |mh|
11
+ mh.match = lambda {
12
12
  raise context.missing_method_name \
13
13
  unless context.missing_method_name == "aaa"
14
14
  /\Aaaa\z/ =~ context.missing_method_name
15
15
  }
16
- mm.new_method_code = lambda {
16
+ mh.handle = lambda {
17
17
  raise context.missing_method_name \
18
18
  unless context.missing_method_name == "aaa"
19
19
  %Q{
@@ -32,11 +32,11 @@ describe "EagerBeaver matcher context" do
32
32
  klass = Class.new do
33
33
  include EagerBeaver
34
34
 
35
- add_method_matcher do |mm|
36
- mm.matcher = lambda {
35
+ add_method_handler do |mh|
36
+ mh.match = lambda {
37
37
  /\Aaaa\z/ =~ context.missing_method_name
38
38
  }
39
- mm.new_method_code = lambda {
39
+ mh.handle = lambda {
40
40
  %Q{
41
41
  def #{context.missing_method_name}
42
42
  #{context.original_receiver.__id__}
@@ -56,12 +56,12 @@ describe "EagerBeaver matcher context" do
56
56
  klass = Class.new do
57
57
  include EagerBeaver
58
58
 
59
- add_method_matcher do |mm|
60
- mm.matcher = lambda {
59
+ add_method_handler do |mh|
60
+ mh.match = lambda {
61
61
  context.my_data = "hello"
62
62
  /\Aaaa\z/ =~ context.missing_method_name
63
63
  }
64
- mm.new_method_code = lambda {
64
+ mh.handle = lambda {
65
65
  %Q{
66
66
  def #{context.missing_method_name}
67
67
  "#{context.my_data}"
@@ -10,25 +10,25 @@ describe "EagerBeaver includer" do
10
10
  end
11
11
  end
12
12
 
13
- it "has #add_method_matcher" do
14
- expect(@klass.methods).to include :add_method_matcher
13
+ it "has #add_method_handler" do
14
+ expect(@klass.methods).to include :add_method_handler
15
15
  end
16
16
 
17
- it "has #method_matchers" do
18
- expect(@klass.methods).to include :method_matchers
17
+ it "has #method_handlers" do
18
+ expect(@klass.methods).to include :method_handlers
19
19
  end
20
20
 
21
21
  end
22
22
 
23
- describe "#add_method_matcher" do
23
+ describe "#add_method_handler" do
24
24
 
25
25
  it "registers a new method matcher" do
26
26
  klass = Class.new do
27
27
  include EagerBeaver
28
28
 
29
- add_method_matcher do |mm|
30
- mm.matcher = lambda { true }
31
- mm.new_method_code = lambda {
29
+ add_method_handler do |mh|
30
+ mh.match = lambda { true }
31
+ mh.handle = lambda {
32
32
  return %Q{
33
33
  def #{context.missing_method_name}
34
34
  end
@@ -37,7 +37,7 @@ describe "EagerBeaver includer" do
37
37
  end
38
38
  end
39
39
 
40
- klass.method_matchers.size.should == 1
40
+ klass.method_handlers.size.should == 1
41
41
  end
42
42
 
43
43
  end
@@ -22,9 +22,9 @@ describe "instance of EagerBeaver includer" do
22
22
  klass = Class.new do
23
23
  include EagerBeaver
24
24
 
25
- add_method_matcher do |mm|
26
- mm.matcher = lambda { /\Aaaa_\w+\z/ =~ context.missing_method_name }
27
- mm.new_method_code = lambda {
25
+ add_method_handler do |mh|
26
+ mh.match = lambda { /\Aaaa_\w+\z/ =~ context.missing_method_name }
27
+ mh.handle = lambda {
28
28
  %Q{
29
29
  def #{context.missing_method_name}
30
30
  end
@@ -53,9 +53,9 @@ describe "instance of EagerBeaver includer" do
53
53
  klass = Class.new do
54
54
  include EagerBeaver
55
55
 
56
- add_method_matcher do |mm|
57
- mm.matcher = lambda { /\Aaaa_\w+\z/ =~ context.missing_method_name }
58
- mm.new_method_code = lambda {
56
+ add_method_handler do |mh|
57
+ mh.match = lambda { /\Aaaa_\w+\z/ =~ context.missing_method_name }
58
+ mh.handle = lambda {
59
59
  %Q{
60
60
  def #{context.missing_method_name}
61
61
  end
@@ -83,9 +83,9 @@ describe "instance of EagerBeaver includer" do
83
83
  klass = Class.new do
84
84
  include EagerBeaver
85
85
 
86
- add_method_matcher do |mm|
87
- mm.matcher = lambda { /\Aaaa\z/ =~ context.missing_method_name }
88
- mm.new_method_code = lambda {
86
+ add_method_handler do |mh|
87
+ mh.match = lambda { /\Aaaa\z/ =~ context.missing_method_name }
88
+ mh.handle = lambda {
89
89
  %Q{
90
90
  def #{context.missing_method_name}
91
91
  1
@@ -94,9 +94,9 @@ describe "instance of EagerBeaver includer" do
94
94
  }
95
95
  end
96
96
 
97
- add_method_matcher do |mm|
98
- mm.matcher = lambda { /\Abbb\z/ =~ context.missing_method_name }
99
- mm.new_method_code = lambda {
97
+ add_method_handler do |mh|
98
+ mh.match = lambda { /\Abbb\z/ =~ context.missing_method_name }
99
+ mh.handle = lambda {
100
100
  %Q{
101
101
  def #{context.missing_method_name}
102
102
  2
@@ -105,9 +105,9 @@ describe "instance of EagerBeaver includer" do
105
105
  }
106
106
  end
107
107
 
108
- add_method_matcher do |mm|
109
- mm.matcher = lambda { /\Abbb\z/ =~ context.missing_method_name }
110
- mm.new_method_code = lambda {
108
+ add_method_handler do |mh|
109
+ mh.match = lambda { /\Abbb\z/ =~ context.missing_method_name }
110
+ mh.handle = lambda {
111
111
  %Q{
112
112
  def #{context.missing_method_name}
113
113
  3
@@ -130,9 +130,9 @@ describe "instance of EagerBeaver includer" do
130
130
  klass2 = Class.new(klass1) do
131
131
  include EagerBeaver
132
132
 
133
- add_method_matcher do |mm|
134
- mm.matcher = lambda { /\Aaaa\z/ =~ context.missing_method_name }
135
- mm.new_method_code = lambda {
133
+ add_method_handler do |mh|
134
+ mh.match = lambda { /\Aaaa\z/ =~ context.missing_method_name }
135
+ mh.handle = lambda {
136
136
  %Q{
137
137
  def #{context.missing_method_name}
138
138
  1
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eager_beaver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -43,7 +43,7 @@ files:
43
43
  - Rakefile
44
44
  - eager_beaver.gemspec
45
45
  - lib/eager_beaver.rb
46
- - lib/eager_beaver/method_matcher.rb
46
+ - lib/eager_beaver/method_handler.rb
47
47
  - lib/eager_beaver/version.rb
48
48
  - spec/eager_beaver/context_spec.rb
49
49
  - spec/eager_beaver/includer_spec.rb
@@ -1,60 +0,0 @@
1
- module EagerBeaver
2
-
3
- class MethodMatcher
4
-
5
- attr_accessor :original_receiver
6
- attr_accessor :matcher
7
- attr_accessor :new_method_code_maker
8
- attr_accessor :missing_method_name
9
-
10
- def initialize(&block)
11
- block.call(self)
12
-
13
- raise "matcher must be given" \
14
- if matcher.nil?
15
- raise "matcher lmust be a lambda" \
16
- unless matcher.lambda?
17
-
18
- raise "new_method_code_maker must be given" \
19
- if new_method_code_maker.nil?
20
- raise "new_method_code_maker must be a lambda" \
21
- unless new_method_code_maker.lambda?
22
-
23
- self
24
- end
25
-
26
- def match=(lambda_proc)
27
- self.matcher = lambda_proc
28
- end
29
-
30
- def match?(method_name)
31
- self.missing_method_name = method_name.to_s
32
- return evaluate(matcher)
33
- end
34
-
35
- def new_method_code=(lambda_proc)
36
- self.new_method_code_maker = lambda_proc
37
- end
38
-
39
- def evaluate(inner)
40
- outer = lambda { |*args|
41
- args.shift
42
- inner.call(*args)
43
- }
44
- self.instance_eval &outer
45
- end
46
-
47
- def method_missing(method_name, *args, &block)
48
- if /\A(?<attr_name>\w+)=?\z/ =~ method_name
49
- code = %Q{
50
- attr_accessor :#{attr_name}
51
- }
52
- self.singleton_class.instance_eval code, __FILE__, __LINE__ + 1
53
- return self.send(method_name, *args, &block)
54
- end
55
- super
56
- end
57
-
58
- end
59
-
60
- end