eager_beaver 0.0.3 → 0.0.4

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