missingly 0.0.4 → 0.0.5

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