missingly 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 242b35ec57e5447c85e8ae0e22a9aac429e7eda5
4
- data.tar.gz: c866e36784eada8f7b9fac4c598045fd5391b78c
3
+ metadata.gz: 3a83705cf44cec6dd38ce2c141dc8b01056c8c93
4
+ data.tar.gz: 33d31bba15f08fb1ca5a7fe815a5c10d83763a54
5
5
  SHA512:
6
- metadata.gz: c74940512b88e2413720f84eeef11b59c5d39c0885fc8b87d175b26f20a2ff8b4b2e469caba81624b01187b72fe4715e3b62c0901a5d2777578fb99248e4ede4
7
- data.tar.gz: a7da137a6fa783261daf4411d63012057baf08d304b78fda09a3d70b14e5a8c552d1c3f7b4ed764b30dcb8f1040607ff8116132658affa6088b8db40e27cb8c8
6
+ metadata.gz: 210ccf7b8492f0c25c09795578ee69a367a836e3e4878a10da0b1fb8eddce9c19bc3c30bf37843b9ead71762680bc76192fb65c37776cc046a2e3dac132301cc
7
+ data.tar.gz: 6031dd4754856133dfcfa7f24fef13b47d152fdcc30e73eb2a593784cf5c0b6017fb0b9a9673dd6c2d863a4a944216c679122d82c1080d45a3ac90cb5487c021
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ - jruby-19mode
6
+ - jruby-20mode
7
+ - rbx-19mode
8
+ - rbx-20mode
data/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  A DSL for handling method\_missing hooks.
4
4
 
5
+ [![Code Climate](https://codeclimate.com/github/moger777/missingly.png)](https://codeclimate.com/github/moger777/missingly)
6
+ [![Build Status](https://travis-ci.org/moger777/missingly.png?branch=master)](https://travis-ci.org/moger777/missingly)
7
+ [![Coverage Status](https://coveralls.io/repos/moger777/missingly/badge.png?branch=master)](https://coveralls.io/r/moger777/missingly?branch=master)
8
+
5
9
  ## Installation
6
10
 
7
11
  Add this line to your application's Gemfile:
@@ -110,6 +114,164 @@ class UserDecorator
110
114
  end
111
115
  ```
112
116
 
117
+ ### Use custom matchers
118
+
119
+ In the example with the regex block matchers, our code has to do a
120
+ fair amount of work which is not looking up a value in a hash, for example:
121
+
122
+ ```ruby
123
+ fields = matches[1].split("_and_")
124
+ ```
125
+
126
+ will run every time and can have a performance impact. Likewise we
127
+ are always running:
128
+
129
+ ```ruby
130
+ field.to_sym
131
+ ```
132
+
133
+ In the hash lookup. If the field was already a symbol, there would be less work. And
134
+ the fields were already split up, there would be less work each time. Custom block
135
+ matchers can be done as follows:
136
+
137
+ ```ruby
138
+ class OurMatcher < Missingly::BlockMatcher
139
+ attr_reader :some_matcher, :options_hash, :method_block
140
+
141
+ def initialize(some_matcher, options_hash, method_block)
142
+ @some_matcher, @method_block = some_matcher, method_block
143
+ end
144
+
145
+ def should_respond_to?(instance, name)
146
+ # our custom code
147
+ end
148
+
149
+ def setup_method_name_args(method_name)
150
+ # args we will pass to block
151
+ end
152
+
153
+ def matchable; some_matcher; end
154
+ end
155
+ ```
156
+
157
+ Since we essentially want to re-use the regex block helper, we can inherit and override
158
+ setup_method_name_args. These args will be passed to the block in the handle_missingly
159
+ call:
160
+
161
+ ```ruby
162
+ class FindByFieldsWithAndsMatcher < Missingly::RegexBlockMatcher
163
+ attr_reader :options
164
+
165
+ def initialize(regex, options, block)
166
+ super regex, block
167
+ end
168
+
169
+ def setup_method_name_args(method_name)
170
+ matches = regex.match(method_name)
171
+ fields = matches[1].split("_and_")
172
+ fields.map(&:to_sym)
173
+ end
174
+ end
175
+ ```
176
+
177
+ From here, we can use our custom matcher:
178
+
179
+ ```ruby
180
+ class ArrayWithHashes
181
+ include Missingly::Matchers
182
+
183
+ handle_missingly /^find_by_(\w+)$/, with: FindByFieldsWithAndsMatcher do |fields, *args, &block|
184
+ hashes.find do |hash|
185
+ fields.inject(true) do |fields_match, field|
186
+ index_of_field = fields.index(field)
187
+ arg_for_field = args[index_of_field]
188
+
189
+ fields_match = fields_match && hash[field] == arg_for_field
190
+ break false unless fields_match
191
+ true
192
+ end
193
+ end
194
+ end
195
+
196
+ attr_reader :hashes
197
+
198
+ def initialize(hashes)
199
+ @hashes = hashes
200
+ end
201
+ end
202
+
203
+ hashes = [
204
+ { id: 1, name: 'Pat', gender: 'f' },
205
+ { id: 2, name: 'Pat', gender: 'm' },
206
+ { id: 3, name: 'Steve', gender: 'm' },
207
+ { id: 4, name: 'Sue', gender: 'f' },
208
+ ]
209
+
210
+ instance = ArrayWithHashes.new(hashes)
211
+ instance.find_by_name_and_gender('Pat', 'm') # { id: 2, name: 'Pat', gender: 'm' }
212
+ instance.respond_to?(:find_by_name_and_gender) # true
213
+ instance.method(:find_by_name_and_gender) # method object
214
+ ```
215
+
216
+ For more fine grain controll, you can write should_respond_to? which should
217
+ return true if method responds to, and handle, which should define method and
218
+ return results of first run of method.
219
+
220
+ ### How inheritance works
221
+
222
+ The handle_missingly method is designed to be both inherited and overwritable by
223
+ child classes. The following scenarios should work:
224
+
225
+ Straight up inheritance:
226
+
227
+ ```ruby
228
+ class Parent
229
+ handle_missingly /foo/ do
230
+ :foo
231
+ end
232
+ end
233
+
234
+ class Child < Parent
235
+ end
236
+
237
+ Child.new.foo # should return :foo
238
+ ```
239
+
240
+ Overwriting:
241
+
242
+ ```ruby
243
+ class Parent
244
+ handle_missingly /foo/ do
245
+ :foo
246
+ end
247
+ end
248
+
249
+ class Child < Parent
250
+ handle_missingly /foo/ do
251
+ :bar
252
+ end
253
+ end
254
+
255
+ Child.new.foo # should return :bar
256
+ ```
257
+
258
+ Missingly handlers are based off of "matchable" passed to matcher, so the following
259
+ will also be overwritten:
260
+
261
+ ```ruby
262
+ class Parent
263
+ handle_missingly /foo/ do
264
+ :foo
265
+ end
266
+ end
267
+
268
+ class Child < Parent
269
+ handle_missingly /foo/, to: :something
270
+ end
271
+
272
+ Child.new.foo # should return whatever something returns
273
+ ```
274
+
113
275
  ## Contributing
114
276
 
115
277
  1. Fork it
@@ -117,3 +279,5 @@ end
117
279
  3. Commit your changes (`git commit -am 'Add some feature'`)
118
280
  4. Push to the branch (`git push origin my-new-feature`)
119
281
  5. Create new Pull Request
282
+ 6. Please no tabs or trailing whitespace
283
+ 7. Features and bug fixes should have specs
data/Rakefile CHANGED
@@ -1 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Run the specs'
6
+ RSpec::Core::RakeTask.new do |r|
7
+ r.verbose = false
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,19 @@
1
+ module Missingly
2
+ class ArrayBlockMatcher < BlockMatcher
3
+ attr_reader :array, :method_block, :options
4
+
5
+ def initialize(array, options, method_block)
6
+ @array, @method_block, @options = array, method_block, options
7
+ end
8
+
9
+ def should_respond_to?(instance, name)
10
+ array.include?(name)
11
+ end
12
+
13
+ def setup_method_name_args(method_name)
14
+ method_name
15
+ end
16
+
17
+ def matchable; array; end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Missingly
2
+ class ArrayDelegateMatcher < DelegateMatcher
3
+ attr_reader :array, :options, :delegate_name
4
+
5
+ def initialize(array, options, delegate_name)
6
+ @array, @options, @delegate_name = array, options, delegate_name
7
+ end
8
+
9
+ def should_respond_to?(instance, name)
10
+ included_in_array = array.include?(name)
11
+ delegate_responds_to = instance.send(delegate_name).respond_to?(name)
12
+ included_in_array && delegate_responds_to
13
+ end
14
+
15
+ def matchable; array; end
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ module Missingly
2
+ class BlockMatcher
3
+ def define(instance, method_name)
4
+
5
+ klass = instance.class == Class ? instance : instance.class
6
+
7
+ if instance.class == Class
8
+ define_class_method(klass, method_name)
9
+ else
10
+ define_instance_method(klass, method_name)
11
+ end
12
+ end
13
+
14
+ def define_class_method(klass, method_name)
15
+ sub_name = "_#{method_name}_submethod"
16
+ method_name_args = setup_method_name_args(method_name)
17
+
18
+ klass.define_singleton_method method_name do |*the_args, &the_block|
19
+ public_send(sub_name, method_name_args, *the_args, &the_block)
20
+ end
21
+ klass.define_singleton_method(sub_name, &method_block)
22
+ end
23
+
24
+ def define_instance_method(klass, method_name)
25
+ sub_name = "_#{method_name}_submethod"
26
+ method_name_args = setup_method_name_args(method_name)
27
+
28
+ klass._define_method method_name do |*the_args, &the_block|
29
+ public_send(sub_name, method_name_args, *the_args, &the_block)
30
+ end
31
+ klass._define_method(sub_name, &method_block)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ module Missingly
2
+ class DelegateMatcher
3
+ def define(instance, name)
4
+ instance.class.class_eval <<-RUBY, __FILE__, __LINE__
5
+ def #{name}(*args, &block)
6
+ #{delegate_name}.#{name}(*args, &block)
7
+ end
8
+ RUBY
9
+ end
10
+ end
11
+ end
@@ -11,20 +11,64 @@ module Missingly
11
11
 
12
12
  module Matchers
13
13
  module ClassMethods
14
- def handle_missingly(regular_expression_or_array, options={}, &block)
15
- undef_parent_missingly_methods regular_expression_or_array
14
+ def handle_missingly(matcher, options={}, &block)
15
+ undef_parent_missingly_methods matcher
16
+ undef_normal_missingly_methods matcher
17
+
18
+ if options[:with]
19
+ setup_custom_handler(matcher, options, &block)
20
+ elsif block_given?
21
+ setup_block_handlers(matcher, options, &block)
22
+ elsif options[:to]
23
+ setup_delegation_handlers(matcher, options, options[:to])
24
+ end
25
+ end
26
+
27
+ def setup_custom_handler(matcher, options, &block)
28
+ missingly_matchers[matcher] = options[:with].new(matcher, options, block)
29
+ end
30
+
31
+ def setup_block_handlers(matcher, options, &block)
32
+ case matcher
33
+ when Array then missingly_matchers[matcher] = ArrayBlockMatcher.new(matcher, options, block)
34
+ when Regexp then missingly_matchers[matcher] = RegexBlockMatcher.new(matcher, options, block)
35
+ end
36
+ end
16
37
 
17
- case regular_expression_or_array
18
- when Array then missingly_matchers[regular_expression_or_array] = ArrayMatcher.new(regular_expression_or_array, options, block)
19
- when Regexp then missingly_matchers[regular_expression_or_array] = RegexMatcher.new(regular_expression_or_array, options, block)
38
+ def setup_delegation_handlers(matcher, options, to)
39
+ case matcher
40
+ when Array then missingly_matchers[matcher] = ArrayDelegateMatcher.new(matcher, options, to)
41
+ when Regexp then missingly_matchers[matcher] = RegexDelegateMatcher.new(matcher, options, to)
20
42
  end
21
43
  end
22
44
 
23
45
  def undef_parent_missingly_methods(matcher)
24
- return unless superclass.respond_to?(:missingly_methods_for_matcher)
46
+ superclass = self.superclass
47
+ matchers = []
25
48
 
26
- superclass.missingly_methods_for_matcher(matcher).each do |method|
27
- undef_method method
49
+ while superclass.respond_to?(:missingly_methods_for_matcher)
50
+ matchers.concat superclass.missingly_methods_for_matcher(matcher)
51
+ superclass = superclass.superclass
52
+ end
53
+
54
+ undef_missingly_methods(matchers)
55
+ end
56
+
57
+ def undef_normal_missingly_methods(matcher)
58
+ undef_missingly_methods(missingly_methods_for_matcher(matcher))
59
+ end
60
+
61
+ def undef_missingly_methods(methods)
62
+ methods.each do |method|
63
+ begin
64
+ undef_method method
65
+ rescue NameError
66
+ eval <<-RUBY
67
+ class << self
68
+ undef_method #{method.inspect}
69
+ end
70
+ RUBY
71
+ end
28
72
  end
29
73
  end
30
74
 
@@ -55,11 +99,38 @@ module Missingly
55
99
  end
56
100
  missingly_subclasses << subclass
57
101
  end
102
+
103
+ def method_missing(method_name, *args, &block)
104
+ missingly_matchers.values.each do |matcher|
105
+ next unless matcher.should_respond_to?(self, method_name)
106
+ next unless matcher.options[:class_method]
107
+
108
+ Missingly::Mutex.synchronize do
109
+ missingly_methods_for_matcher(matcher.matchable) << method_name
110
+
111
+ returned_value = matcher.define(self, method_name, *args, &block)
112
+
113
+ missingly_subclasses.each do |subclass|
114
+ subclass.undef_parent_missingly_methods matcher.matchable
115
+ end
116
+
117
+ return public_send(method_name, *args, &block)
118
+ end
119
+ end
120
+ super
121
+ end
122
+
123
+ def respond_to_missing?(method_name, include_all)
124
+ self.missingly_matchers.values.each do |matcher|
125
+ return true if matcher.should_respond_to?(self, method_name.to_sym) && matcher.options[:class_method]
126
+ end
127
+ super
128
+ end
58
129
  end
59
130
 
60
131
  def respond_to_missing?(method_name, include_all)
61
132
  self.class.missingly_matchers.values.each do |matcher|
62
- return true if matcher.should_respond_to?(method_name.to_sym)
133
+ return true if matcher.should_respond_to?(self, method_name.to_sym) && !(matcher.options[:class_method])
63
134
  end
64
135
  super
65
136
  end
@@ -67,18 +138,19 @@ module Missingly
67
138
 
68
139
  def method_missing(method_name, *args, &block)
69
140
  self.class.missingly_matchers.values.each do |matcher|
70
- next unless matcher.should_respond_to?(method_name)
141
+ next unless matcher.should_respond_to?(self, method_name)
142
+ next if matcher.options[:class_method]
71
143
 
72
144
  Missingly::Mutex.synchronize do
73
145
  self.class.missingly_methods_for_matcher(matcher.matchable) << method_name
74
146
 
75
- returned_value = matcher.handle(self, method_name, *args, &block)
147
+ matcher.define(self, method_name)
76
148
 
77
149
  self.class.missingly_subclasses.each do |subclass|
78
150
  subclass.undef_parent_missingly_methods matcher.matchable
79
151
  end
80
152
 
81
- return returned_value
153
+ return public_send(method_name, *args, &block)
82
154
  end
83
155
  end
84
156
  super
@@ -0,0 +1,19 @@
1
+ module Missingly
2
+ class RegexBlockMatcher < BlockMatcher
3
+ attr_reader :regex, :method_block, :options
4
+
5
+ def initialize(regex, options, method_block)
6
+ @regex, @options, @method_block = regex, options, method_block
7
+ end
8
+
9
+ def should_respond_to?(instance, name)
10
+ regex.match(name)
11
+ end
12
+
13
+ def setup_method_name_args(method_name)
14
+ regex.match method_name
15
+ end
16
+
17
+ def matchable; regex; end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Missingly
2
+ class RegexDelegateMatcher < DelegateMatcher
3
+ attr_reader :regex, :options, :delegate_name
4
+
5
+ def initialize(regex, options, delegate_name)
6
+ @regex, @options, @delegate_name = regex, options, delegate_name
7
+ end
8
+
9
+ def should_respond_to?(instance, name)
10
+ matches = regex.match name
11
+ delegate_responds_to = instance.send(delegate_name).respond_to?(name)
12
+ matches && delegate_responds_to
13
+ end
14
+
15
+ def matchable; regex; end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Missingly
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
data/lib/missingly.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  require "missingly/version"
2
2
  require "missingly/matchers"
3
- require "missingly/array_matcher"
4
- require "missingly/regex_matcher"
3
+ require "missingly/block_matcher"
4
+ require "missingly/array_block_matcher"
5
+ require "missingly/regex_block_matcher"
6
+ require "missingly/delegate_matcher"
7
+ require "missingly/array_delegate_matcher"
8
+ require "missingly/regex_delegate_matcher"
5
9
 
6
10
  module Missingly
7
11
 
data/missingly.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
- spec.add_development_dependency "rspec"
24
- spec.add_development_dependency "pry"
23
+ spec.add_development_dependency "rspec", "~> 2.14.0"
24
+ spec.add_development_dependency "pry", "0.9.12"
25
+ spec.add_development_dependency "coveralls"
25
26
  end
data/spec/array_spec.rb CHANGED
@@ -55,13 +55,6 @@ module Missingly
55
55
  instance.block.should == prock
56
56
  end
57
57
 
58
- it "should define the method on call preventing further method missing calls on same class" do
59
- args = [1, 2, 3]
60
- prock = Proc.new { puts 'foo' }
61
- instance.derp(*args, &prock)
62
- Method.should === instance.method(:derp)
63
- end
64
-
65
58
  it "should work with subsequent calls" do
66
59
  args = [1, 2, 3]
67
60
  prock = Proc.new { puts 'foo' }
@@ -22,8 +22,13 @@ describe Missingly::Matchers do
22
22
  end
23
23
 
24
24
  describe "overriding methods" do
25
- let(:subclass_with_overrides) do
25
+ let(:another_inheritance_layer) do
26
26
  Class.new(super_class) do
27
+ end
28
+ end
29
+
30
+ let(:subclass_with_overrides) do
31
+ Class.new(another_inheritance_layer) do
27
32
  handle_missingly [:foo] do |method|
28
33
  :super_duper
29
34
  end
@@ -45,4 +50,44 @@ describe Missingly::Matchers do
45
50
  subclass_with_overrides.new.foo.should eq :super_duper
46
51
  end
47
52
  end
53
+
54
+ describe "overriding class methods" do
55
+ let(:super_class) do
56
+ Class.new do
57
+ include Missingly::Matchers
58
+
59
+ handle_missingly [:foo], class_method: true do |method|
60
+ return method
61
+ end
62
+ end
63
+ end
64
+
65
+ let(:another_inheritance_layer) do
66
+ Class.new(super_class) do
67
+ end
68
+ end
69
+
70
+ let(:subclass_with_overrides) do
71
+ Class.new(another_inheritance_layer) do
72
+ handle_missingly [:foo], class_method: true do |_|
73
+ :super_duper
74
+ end
75
+ end
76
+ end
77
+
78
+ it "should work when called before parent" do
79
+ subclass_with_overrides.foo.should eq :super_duper
80
+ end
81
+
82
+ it "should work when called after parent" do
83
+ super_class.foo
84
+ subclass_with_overrides.foo.should eq :super_duper
85
+ end
86
+
87
+ it "should work when subclass initiated before parent method defined" do
88
+ subclass_with_overrides
89
+ super_class.foo
90
+ subclass_with_overrides.foo.should eq :super_duper
91
+ end
92
+ end
48
93
  end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Missingly::Matchers do
4
+ let(:search_class) do
5
+ Class.new do
6
+ include Missingly::Matchers
7
+
8
+ handle_missingly [:find_by_name], class_method: true do |name|
9
+ return {foo: 'bar'}
10
+ end
11
+
12
+ handle_missingly /^find_all_by_(\w+)$/, class_method: true do |matches, *args, &block|
13
+ return matches
14
+ end
15
+ end
16
+ end
17
+
18
+ let(:delegation_test) do
19
+ Class.new(search_class) do
20
+ handle_missingly [:find_by_foo], to: :proxy, class_method: true
21
+
22
+ def self.proxy
23
+ OpenStruct.new({find_by_foo: "foo"})
24
+ end
25
+ end
26
+ end
27
+
28
+
29
+ it "should not break normal method_missing" do
30
+ search_class.new.respond_to?("foo_bar_widget").should be_false
31
+ end
32
+
33
+ it "should allow you to define class methods" do
34
+ search_class.respond_to?("find_by_name").should be_true
35
+ search_class.respond_to?("find_all_by_name").should be_true
36
+ search_class.find_all_by_name.should be_a MatchData
37
+ search_class.find_by_name.should be_a Hash
38
+ end
39
+
40
+ it "should support delegation matchers" do
41
+ delegation_test.respond_to?("find_by_foo").should be_true
42
+ delegation_test.find_by_foo.should be_true
43
+ end
44
+
45
+ it "should not make class methods avliable to instances" do
46
+ search_class.new.respond_to?("find_by_name").should be_false
47
+ lambda { search_class.new.find_by_name("foo") }.should raise_exception
48
+ end
49
+
50
+ it "should work through inheritence" do
51
+ delegation_test.respond_to?("find_all_by_name").should be_true
52
+ delegation_test.find_all_by_name.should be_a MatchData
53
+ end
54
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ module Missingly
4
+ describe Matchers do
5
+ let(:find_by_matcher) do
6
+ FindByMatcher = Class.new(Missingly::RegexBlockMatcher) do
7
+ attr_reader :method_block, :options
8
+
9
+ def initialize(regex, options, block)
10
+ @regex = regex
11
+ @options = options
12
+ @method_block = block
13
+ end
14
+
15
+ def setup_method_name_args(method_name)
16
+ matches = regex.match(method_name)
17
+ matches[1].split("_and_").map(&:to_sym)
18
+ end
19
+ end
20
+ end
21
+
22
+ let(:our_class) do
23
+ find_by_matcher
24
+ Class.new do
25
+ include Missingly::Matchers
26
+ attr_reader :hashes
27
+
28
+ def initialize(hashes)
29
+ @hashes = hashes
30
+ end
31
+
32
+ handle_missingly /^find_by_(\w+)$/, with: FindByMatcher do |fields, *args|
33
+ hashes.find do |hash|
34
+ fields.inject(true) do |fields_match, field|
35
+ index_of_field = fields.index(field)
36
+ arg_for_field = args[index_of_field]
37
+
38
+ fields_match = fields_match && hash[field.to_sym] == arg_for_field
39
+ break false unless fields_match
40
+ true
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ let(:our_instance) do
48
+ hashes = [
49
+ { first_name: 'Bob', last_name: 'Dole' },
50
+ { first_name: 'Bill', last_name: 'Clinton' },
51
+ { first_name: 'George', last_name: 'Bush' }
52
+ ]
53
+ our_class.new(hashes)
54
+ end
55
+
56
+ it "should allow us to define custom matchers" do
57
+ our_instance.find_by_first_name_and_last_name('Bill', 'Douglas').should be_nil
58
+ our_instance.find_by_first_name_and_last_name('Bill', 'Clinton').should == { first_name: 'Bill', last_name: 'Clinton' }
59
+ end
60
+ end
61
+ end
@@ -11,11 +11,11 @@ module Missingly
11
11
  end
12
12
  end
13
13
 
14
- let(:proxy){ stub }
14
+ let(:proxy){ double }
15
15
 
16
16
  let(:instance) do
17
17
  i = our_class.new
18
- i.stub(:proxy).and_return(proxy)
18
+ allow(i).to receive(:proxy).and_return(proxy)
19
19
  i
20
20
  end
21
21
 
@@ -0,0 +1,39 @@
1
+ module Missingly
2
+ describe Matchers do
3
+ describe "#handle_missingly" do
4
+ it "can be used to override previous missingly with same matcher" do
5
+ klass = Class.new do
6
+ include Missingly::Matchers
7
+
8
+ handle_missingly /foo/ do |*args|
9
+ 'foo'
10
+ end
11
+
12
+ handle_missingly /foo/ do |*args|
13
+ 'bar'
14
+ end
15
+ end
16
+
17
+ klass.new.foo.should eq('bar')
18
+
19
+ another_klass = Class.new do
20
+ include Missingly::Matchers
21
+
22
+ handle_missingly /foo/ do |*args|
23
+ 'foo'
24
+ end
25
+ end
26
+
27
+ another_klass.new.foo
28
+
29
+ another_klass.module_eval do
30
+ handle_missingly /foo/ do |*args|
31
+ 'bar'
32
+ end
33
+ end
34
+
35
+ another_klass.new.foo.should eq('bar')
36
+ end
37
+ end
38
+ end
39
+ end
data/spec/regex_spec.rb CHANGED
@@ -84,13 +84,6 @@ module Missingly
84
84
  instance.block.should == prock
85
85
  end
86
86
 
87
- it "should define the method on call preventing further method missing calls on same class" do
88
- args = [1, 2, 3]
89
- prock = Proc.new { puts 'foo' }
90
- instance.find_by_id_and_first_name(*args, &prock)
91
- Method.should === instance.method(:find_by_id_and_first_name)
92
- end
93
-
94
87
  it "should work with subsequent calls" do
95
88
  args = [1, 2, 3]
96
89
  prock = Proc.new { puts 'foo' }
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,5 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
1
4
  require_relative '../lib/missingly'
2
5
  require 'pry'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: missingly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thijs de Vries
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-20 00:00:00.000000000 Z
11
+ date: 2013-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -42,18 +42,32 @@ dependencies:
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 2.14.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 2.14.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.9.12
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.9.12
69
+ - !ruby/object:Gem::Dependency
70
+ name: coveralls
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - '>='
@@ -74,19 +88,27 @@ extensions: []
74
88
  extra_rdoc_files: []
75
89
  files:
76
90
  - .gitignore
91
+ - .travis.yml
77
92
  - Gemfile
78
93
  - LICENSE.txt
79
94
  - README.md
80
95
  - Rakefile
81
96
  - lib/missingly.rb
82
- - lib/missingly/array_matcher.rb
97
+ - lib/missingly/array_block_matcher.rb
98
+ - lib/missingly/array_delegate_matcher.rb
99
+ - lib/missingly/block_matcher.rb
100
+ - lib/missingly/delegate_matcher.rb
83
101
  - lib/missingly/matchers.rb
84
- - lib/missingly/regex_matcher.rb
102
+ - lib/missingly/regex_block_matcher.rb
103
+ - lib/missingly/regex_delegate_matcher.rb
85
104
  - lib/missingly/version.rb
86
105
  - missingly.gemspec
87
106
  - spec/array_spec.rb
88
107
  - spec/class_inheritance_spec.rb
108
+ - spec/class_methods_spec.rb
109
+ - spec/custom_matcher_spec.rb
89
110
  - spec/delegation_spec.rb
111
+ - spec/matchers_spec.rb
90
112
  - spec/regex_spec.rb
91
113
  - spec/spec_helper.rb
92
114
  homepage: ''
@@ -116,6 +138,9 @@ summary: A DSL for defining method missing methods
116
138
  test_files:
117
139
  - spec/array_spec.rb
118
140
  - spec/class_inheritance_spec.rb
141
+ - spec/class_methods_spec.rb
142
+ - spec/custom_matcher_spec.rb
119
143
  - spec/delegation_spec.rb
144
+ - spec/matchers_spec.rb
120
145
  - spec/regex_spec.rb
121
146
  - spec/spec_helper.rb
@@ -1,40 +0,0 @@
1
- module Missingly
2
- class ArrayMatcher
3
- attr_reader :array, :options, :method_block
4
-
5
- def initialize(array, options, method_block)
6
- @array, @options, @method_block = array, options, method_block
7
- end
8
-
9
- def matchable
10
- array
11
- end
12
-
13
- def should_respond_to?(name)
14
- array.include?(name)
15
- end
16
-
17
- def handle(instance, method_name, *args, &block)
18
- if method_block
19
- sub_name = "#{method_name}_with_method_name"
20
-
21
- instance.class._define_method method_name do |*the_args, &the_block|
22
- public_send(sub_name, method_name, *the_args, &the_block)
23
- end
24
- instance.class._define_method(sub_name, &method_block)
25
-
26
- instance.public_send(method_name, *args, &block)
27
- elsif options[:to]
28
- instance.class.class_eval <<-CODE
29
- def #{method_name}(*args, &block)
30
- #{options[:to]}.#{method_name}(*args, &block)
31
- end
32
- CODE
33
-
34
- instance.public_send(method_name, *args, &block)
35
- else
36
- raise ArgumentError, "either block, or to option should be passed"
37
- end
38
- end
39
- end
40
- end
@@ -1,41 +0,0 @@
1
- module Missingly
2
- class RegexMatcher
3
- attr_reader :regex, :options, :method_block
4
-
5
- def initialize(regex, options, method_block)
6
- @regex, @options, @method_block = regex, options, method_block
7
- end
8
-
9
- def matchable
10
- regex
11
- end
12
-
13
- def should_respond_to?(name)
14
- regex.match(name)
15
- end
16
-
17
- def handle(instance, method_name, *args, &block)
18
- if method_block
19
- matches = regex.match method_name
20
-
21
- sub_name = "#{method_name}_with_matches"
22
- instance.class._define_method method_name do |*the_args, &the_block|
23
- public_send(sub_name, matches, *the_args, &the_block)
24
- end
25
- instance.class._define_method(sub_name, &method_block)
26
-
27
- instance.public_send(method_name, *args, &block)
28
- elsif options[:to]
29
- instance.class.class_eval <<-CODE
30
- def #{method_name}(*args, &block)
31
- #{options[:to]}.#{method_name}(*args, &block)
32
- end
33
- CODE
34
-
35
- instance.public_send(method_name, *args, &block)
36
- else
37
- raise ArgumentError, "either block, or to option should be passed"
38
- end
39
- end
40
- end
41
- end