pattern-matching 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,351 +1,393 @@
1
- require 'spec_helper'
2
- require 'ostruct'
3
-
4
- describe PatternMatching do
5
-
6
- def new_clazz(&block)
7
- clazz = Class.new
8
- clazz.send(:include, PatternMatching)
9
- clazz.instance_eval(&block) if block_given?
10
- return clazz
11
- end
12
-
13
- subject { new_clazz }
14
-
15
- context '#defn declaration' do
16
-
17
- it 'can be used within a class declaration' do
18
- lambda {
19
- class Clazz
20
- include PatternMatching
21
- defn :foo
22
- end
23
- }.should_not raise_error
24
- end
25
-
26
- it 'can be used on a class object' do
27
- lambda {
28
- clazz = Class.new
29
- clazz.send(:include, PatternMatching)
30
- clazz.defn(:foo)
31
- }.should_not raise_error
32
- end
33
- end
34
-
35
- context 'parameter count' do
36
-
37
- it 'does not match a call with not enough arguments' do
38
-
39
- subject.defn(:foo, true) { 'expected' }
40
-
41
- lambda {
42
- subject.new.foo()
43
- }.should raise_error(NoMethodError)
44
- end
45
-
46
- it 'does not match a call with too many arguments' do
47
-
48
- subject.defn(:foo, true) { 'expected' }
49
-
50
- lambda {
51
- subject.new.foo(true, false)
52
- }.should raise_error(NoMethodError)
53
- end
54
-
55
- end
56
-
57
- context 'recursion' do
58
-
59
- it 'defers unmatched calls to the superclass' do
60
-
61
- class UnmatchedCallTesterSuperclass
62
- def foo(bar)
63
- return bar
64
- end
65
- end
66
-
67
- class UnmatchedCallTesterSubclass < UnmatchedCallTesterSuperclass
68
- include PatternMatching
69
- defn(:foo) { 'baz' }
70
- end
71
-
72
- subject = UnmatchedCallTesterSubclass.new
73
- subject.foo(:bar).should eq :bar
74
- end
75
-
76
- it 'can call another match from within a match' do
77
-
78
- subject.defn(:foo, :bar) { |arg| foo(:baz) }
79
- subject.defn(:foo, :baz) { |arg| 'expected' }
80
-
81
- subject.new.foo(:bar).should eq 'expected'
82
- end
83
-
84
- it 'can call a superclass method from within a match' do
85
-
86
- class RecursiveCallTesterSuperclass
87
- def foo(bar)
88
- return bar
89
- end
90
- end
91
-
92
- class RecursiveCallTesterSubclass < RecursiveCallTesterSuperclass
93
- include PatternMatching
94
- defn(:foo, :bar) { foo(:baz) }
95
- end
96
-
97
- subject = RecursiveCallTesterSubclass.new
98
- subject.foo(:bar).should eq :baz
99
- end
100
- end
101
-
102
- context 'datatypes' do
103
-
104
- it 'matches an argument of the class given in the match parameter' do
105
-
106
- subject.defn(:foo, Integer) { 'expected' }
107
- subject.new.foo(100).should eq 'expected'
108
-
109
- lambda {
110
- subject.new.foo('hello')
111
- }.should raise_error(NoMethodError)
112
- end
113
-
114
- it 'passes the matched argument to the block' do
115
-
116
- subject.defn(:foo, Integer) { |arg| arg }
117
- subject.new.foo(100).should eq 100
118
- end
119
- end
120
-
121
- context 'function with no parameters' do
122
-
123
- it 'accepts no parameters' do
124
-
125
- subject.defn(:foo)
126
- obj = subject.new
127
-
128
- lambda {
129
- obj.foo
130
- }.should_not raise_error(NoMethodError)
131
- end
132
-
133
- it 'does not accept any parameters' do
134
-
135
- subject.defn(:foo)
136
- obj = subject.new
137
-
138
- lambda {
139
- obj.foo(1)
140
- }.should raise_error(NoMethodError)
141
- end
142
-
143
- it 'returns the correct value' do
144
- subject.defn(:foo){ true }
145
- subject.new.foo.should be_true
146
- end
147
- end
148
-
149
- context 'function with one parameter' do
150
-
151
- it 'matches a nil parameter' do
152
-
153
- subject.defn(:foo, nil) { 'expected' }
154
- subject.new.foo(nil).should eq 'expected'
155
-
156
- lambda {
157
- subject.new.foo('no match should be found')
158
- }.should raise_error(NoMethodError)
159
- end
160
-
161
- it 'matches a boolean parameter' do
162
-
163
- subject.defn(:foo, true) { 'expected' }
164
- subject.defn(:foo, false) { 'false case' }
165
-
166
- subject.new.foo(true).should eq 'expected'
167
- subject.new.foo(false).should eq 'false case'
168
-
169
- lambda {
170
- subject.new.foo('no match should be found')
171
- }.should raise_error(NoMethodError)
172
- end
173
-
174
- it 'matches a symbol parameter' do
175
-
176
- subject.defn(:foo, :bar) { 'expected' }
177
- subject.new.foo(:bar).should eq 'expected'
178
-
179
- lambda {
180
- subject.new.foo(:baz)
181
- }.should raise_error(NoMethodError)
182
- end
183
-
184
- it 'matches a number parameter' do
185
-
186
- subject.defn(:foo, 10) { 'expected' }
187
- subject.new.foo(10).should eq 'expected'
188
-
189
- lambda {
190
- subject.new.foo(11.0)
191
- }.should raise_error(NoMethodError)
192
- end
193
-
194
- it 'matches a string parameter' do
195
-
196
- subject.defn(:foo, 'bar') { 'expected' }
197
- subject.new.foo('bar').should eq 'expected'
198
-
199
- lambda {
200
- subject.new.foo('baz')
201
- }.should raise_error(NoMethodError)
202
- end
203
-
204
- it 'matches an array parameter' do
205
-
206
- subject.defn(:foo, [1, 2, 3]) { 'expected' }
207
- subject.new.foo([1, 2, 3]).should eq 'expected'
208
-
209
- lambda {
210
- subject.new.foo([3, 4, 5])
211
- }.should raise_error(NoMethodError)
212
- end
213
-
214
- it 'matches a hash parameter' do
215
-
216
- subject.defn(:foo, bar: 1, baz: 2) { 'expected' }
217
- subject.new.foo(bar: 1, baz: 2).should eq 'expected'
218
-
219
- lambda {
220
- subject.new.foo(foo: 0, bar: 1)
221
- }.should raise_error(NoMethodError)
222
- end
223
-
224
- it 'matches an object parameter' do
225
-
226
- subject.defn(:foo, OpenStruct.new(foo: :bar)) { 'expected' }
227
- subject.new.foo(OpenStruct.new(foo: :bar)).should eq 'expected'
228
-
229
- lambda {
230
- subject.new.foo(OpenStruct.new(bar: :baz))
231
- }.should raise_error(NoMethodError)
232
- end
233
-
234
- it 'matches an unbound parameter' do
235
-
236
- subject.defn(:foo, PatternMatching::UNBOUND) {|arg| arg }
237
- subject.new.foo(:foo).should eq :foo
238
- end
239
- end
240
-
241
- context 'function with two parameters' do
242
-
243
- it 'matches two bound arguments' do
244
-
245
- subject.defn(:foo, :male, :female){ 'expected' }
246
- subject.new.foo(:male, :female).should eq 'expected'
247
-
248
- lambda {
249
- subject.new.foo(1, 2)
250
- }.should raise_error(NoMethodError)
251
- end
252
-
253
- it 'matches two unbound arguments' do
254
-
255
- subject.defn(:foo, PatternMatching::UNBOUND, PatternMatching::UNBOUND) do |first, second|
256
- [first, second]
257
- end
258
- subject.new.foo(:male, :female).should eq [:male, :female]
259
- end
260
-
261
- it 'matches when the first argument is bound and the second is not' do
262
-
263
- subject.defn(:foo, :male, PatternMatching::UNBOUND) do |second|
264
- second
265
- end
266
- subject.new.foo(:male, :female).should eq :female
267
- end
268
-
269
- it 'matches when the second argument is bound and the first is not' do
270
-
271
- subject.defn(:foo, PatternMatching::UNBOUND, :female) do |first|
272
- first
273
- end
274
- subject.new.foo(:male, :female).should eq :male
275
- end
276
- end
277
-
278
- context 'functions with hash arguments' do
279
-
280
- it 'matches an empty argument hash with an empty parameter hash' do
281
-
282
- subject.defn(:foo, {}) { true }
283
- subject.new.foo({}).should be_true
284
-
285
- lambda {
286
- subject.new.foo({one: :two})
287
- }.should raise_error(NoMethodError)
288
- end
289
-
290
- it 'matches when all hash keys and values match' do
291
-
292
- subject.defn(:foo, {bar: :baz}) { true }
293
- subject.new.foo(bar: :baz).should be_true
294
-
295
- lambda {
296
- subject.new.foo({one: :two})
297
- }.should raise_error(NoMethodError)
298
- end
299
-
300
- it 'matches when every pattern key/value are in the argument' do
301
-
302
- subject.defn(:foo, {bar: :baz}) { true }
303
- subject.new.foo(foo: :bar, bar: :baz).should be_true
304
- end
305
-
306
- it 'matches when all keys with unbound values in the pattern have an argument' do
307
-
308
- subject.defn(:foo, {bar: PatternMatching::UNBOUND}) { true }
309
- subject.new.foo(bar: :baz).should be_true
310
- end
311
-
312
- it 'passes unbound values to the block' do
313
-
314
- subject.defn(:foo, {bar: PatternMatching::UNBOUND}) {|arg| arg }
315
- subject.new.foo(bar: :baz).should eq :baz
316
- end
317
-
318
- it 'passes the matched hash to the block' do
319
-
320
- subject.defn(:foo, {bar: :baz}) { |opts| opts }
321
- subject.new.foo(bar: :baz).should == {bar: :baz}
322
- end
323
-
324
- it 'does not match a non-hash argument' do
325
-
326
- subject.defn(:foo, {}) { true }
327
-
328
- lambda {
329
- subject.new.foo(:bar)
330
- }.should raise_error(NoMethodError)
331
- end
332
-
333
- it 'supports idiomatic has-as-last-argument syntax' do
334
-
335
- subject.defn(:foo, PatternMatching::UNBOUND) { |opts| opts }
336
- subject.new.foo(bar: :baz, one: 1, many: 2).should == {bar: :baz, one: 1, many: 2}
337
- end
338
- end
339
-
340
- context 'varaible-length argument lists' do
341
-
342
- it 'supports ALL as the last parameter' do
343
-
344
- subject.defn(:foo, 1, 2, PatternMatching::ALL) { |args| args }
345
- subject.new.foo(1, 2, 3).should == [3]
346
- subject.new.foo(1, 2, :foo, :bar).should == [:foo, :bar]
347
- subject.new.foo(1, 2, :foo, :bar, one: 1, two: 2).should == [:foo, :bar, {one: 1, two: 2}]
348
- end
349
- end
350
-
351
- end
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ describe PatternMatching do
5
+
6
+ def new_clazz(&block)
7
+ clazz = Class.new
8
+ clazz.send(:include, PatternMatching)
9
+ clazz.instance_eval(&block) if block_given?
10
+ return clazz
11
+ end
12
+
13
+ subject { new_clazz }
14
+
15
+ context '#defn declaration' do
16
+
17
+ it 'can be used within a class declaration' do
18
+ lambda {
19
+ class Clazz
20
+ include PatternMatching
21
+ defn :foo
22
+ end
23
+ }.should_not raise_error
24
+ end
25
+
26
+ it 'can be used on a class object' do
27
+ lambda {
28
+ clazz = Class.new
29
+ clazz.send(:include, PatternMatching)
30
+ clazz.defn(:foo)
31
+ }.should_not raise_error
32
+ end
33
+ end
34
+
35
+ context 'parameter count' do
36
+
37
+ it 'does not match a call with not enough arguments' do
38
+
39
+ subject.defn(:foo, true) { 'expected' }
40
+
41
+ lambda {
42
+ subject.new.foo()
43
+ }.should raise_error(NoMethodError)
44
+ end
45
+
46
+ it 'does not match a call with too many arguments' do
47
+
48
+ subject.defn(:foo, true) { 'expected' }
49
+
50
+ lambda {
51
+ subject.new.foo(true, false)
52
+ }.should raise_error(NoMethodError)
53
+ end
54
+
55
+ end
56
+
57
+ context 'recursion' do
58
+
59
+ it 'defers unmatched calls to the superclass' do
60
+
61
+ class UnmatchedCallTesterSuperclass
62
+ def foo(bar)
63
+ return bar
64
+ end
65
+ end
66
+
67
+ class UnmatchedCallTesterSubclass < UnmatchedCallTesterSuperclass
68
+ include PatternMatching
69
+ defn(:foo) { 'baz' }
70
+ end
71
+
72
+ subject = UnmatchedCallTesterSubclass.new
73
+ subject.foo(:bar).should eq :bar
74
+ end
75
+
76
+ it 'can call another match from within a match' do
77
+
78
+ subject.defn(:foo, :bar) { |arg| foo(:baz) }
79
+ subject.defn(:foo, :baz) { |arg| 'expected' }
80
+
81
+ subject.new.foo(:bar).should eq 'expected'
82
+ end
83
+
84
+ it 'can call a superclass method from within a match' do
85
+
86
+ class RecursiveCallTesterSuperclass
87
+ def foo(bar)
88
+ return bar
89
+ end
90
+ end
91
+
92
+ class RecursiveCallTesterSubclass < RecursiveCallTesterSuperclass
93
+ include PatternMatching
94
+ defn(:foo, :bar) { foo(:baz) }
95
+ end
96
+
97
+ subject = RecursiveCallTesterSubclass.new
98
+ subject.foo(:bar).should eq :baz
99
+ end
100
+ end
101
+
102
+ context 'datatypes' do
103
+
104
+ it 'matches an argument of the class given in the match parameter' do
105
+
106
+ subject.defn(:foo, Integer) { 'expected' }
107
+ subject.new.foo(100).should eq 'expected'
108
+
109
+ lambda {
110
+ subject.new.foo('hello')
111
+ }.should raise_error(NoMethodError)
112
+ end
113
+
114
+ it 'passes the matched argument to the block' do
115
+
116
+ subject.defn(:foo, Integer) { |arg| arg }
117
+ subject.new.foo(100).should eq 100
118
+ end
119
+ end
120
+
121
+ context 'function with no parameters' do
122
+
123
+ it 'accepts no parameters' do
124
+
125
+ subject.defn(:foo)
126
+ obj = subject.new
127
+
128
+ lambda {
129
+ obj.foo
130
+ }.should_not raise_error(NoMethodError)
131
+ end
132
+
133
+ it 'does not accept any parameters' do
134
+
135
+ subject.defn(:foo)
136
+ obj = subject.new
137
+
138
+ lambda {
139
+ obj.foo(1)
140
+ }.should raise_error(NoMethodError)
141
+ end
142
+
143
+ it 'returns the correct value' do
144
+ subject.defn(:foo){ true }
145
+ subject.new.foo.should be_true
146
+ end
147
+ end
148
+
149
+ context 'function with one parameter' do
150
+
151
+ it 'matches a nil parameter' do
152
+
153
+ subject.defn(:foo, nil) { 'expected' }
154
+ subject.new.foo(nil).should eq 'expected'
155
+
156
+ lambda {
157
+ subject.new.foo('no match should be found')
158
+ }.should raise_error(NoMethodError)
159
+ end
160
+
161
+ it 'matches a boolean parameter' do
162
+
163
+ subject.defn(:foo, true) { 'expected' }
164
+ subject.defn(:foo, false) { 'false case' }
165
+
166
+ subject.new.foo(true).should eq 'expected'
167
+ subject.new.foo(false).should eq 'false case'
168
+
169
+ lambda {
170
+ subject.new.foo('no match should be found')
171
+ }.should raise_error(NoMethodError)
172
+ end
173
+
174
+ it 'matches a symbol parameter' do
175
+
176
+ subject.defn(:foo, :bar) { 'expected' }
177
+ subject.new.foo(:bar).should eq 'expected'
178
+
179
+ lambda {
180
+ subject.new.foo(:baz)
181
+ }.should raise_error(NoMethodError)
182
+ end
183
+
184
+ it 'matches a number parameter' do
185
+
186
+ subject.defn(:foo, 10) { 'expected' }
187
+ subject.new.foo(10).should eq 'expected'
188
+
189
+ lambda {
190
+ subject.new.foo(11.0)
191
+ }.should raise_error(NoMethodError)
192
+ end
193
+
194
+ it 'matches a string parameter' do
195
+
196
+ subject.defn(:foo, 'bar') { 'expected' }
197
+ subject.new.foo('bar').should eq 'expected'
198
+
199
+ lambda {
200
+ subject.new.foo('baz')
201
+ }.should raise_error(NoMethodError)
202
+ end
203
+
204
+ it 'matches an array parameter' do
205
+
206
+ subject.defn(:foo, [1, 2, 3]) { 'expected' }
207
+ subject.new.foo([1, 2, 3]).should eq 'expected'
208
+
209
+ lambda {
210
+ subject.new.foo([3, 4, 5])
211
+ }.should raise_error(NoMethodError)
212
+ end
213
+
214
+ it 'matches a hash parameter' do
215
+
216
+ subject.defn(:foo, bar: 1, baz: 2) { 'expected' }
217
+ subject.new.foo(bar: 1, baz: 2).should eq 'expected'
218
+
219
+ lambda {
220
+ subject.new.foo(foo: 0, bar: 1)
221
+ }.should raise_error(NoMethodError)
222
+ end
223
+
224
+ it 'matches an object parameter' do
225
+
226
+ subject.defn(:foo, OpenStruct.new(foo: :bar)) { 'expected' }
227
+ subject.new.foo(OpenStruct.new(foo: :bar)).should eq 'expected'
228
+
229
+ lambda {
230
+ subject.new.foo(OpenStruct.new(bar: :baz))
231
+ }.should raise_error(NoMethodError)
232
+ end
233
+
234
+ it 'matches an unbound parameter' do
235
+
236
+ subject.defn(:foo, PatternMatching::UNBOUND) {|arg| arg }
237
+ subject.new.foo(:foo).should eq :foo
238
+ end
239
+ end
240
+
241
+ context 'function with two parameters' do
242
+
243
+ it 'matches two bound arguments' do
244
+
245
+ subject.defn(:foo, :male, :female){ 'expected' }
246
+ subject.new.foo(:male, :female).should eq 'expected'
247
+
248
+ lambda {
249
+ subject.new.foo(1, 2)
250
+ }.should raise_error(NoMethodError)
251
+ end
252
+
253
+ it 'matches two unbound arguments' do
254
+
255
+ subject.defn(:foo, PatternMatching::UNBOUND, PatternMatching::UNBOUND) do |first, second|
256
+ [first, second]
257
+ end
258
+ subject.new.foo(:male, :female).should eq [:male, :female]
259
+ end
260
+
261
+ it 'matches when the first argument is bound and the second is not' do
262
+
263
+ subject.defn(:foo, :male, PatternMatching::UNBOUND) do |second|
264
+ second
265
+ end
266
+ subject.new.foo(:male, :female).should eq :female
267
+ end
268
+
269
+ it 'matches when the second argument is bound and the first is not' do
270
+
271
+ subject.defn(:foo, PatternMatching::UNBOUND, :female) do |first|
272
+ first
273
+ end
274
+ subject.new.foo(:male, :female).should eq :male
275
+ end
276
+ end
277
+
278
+ context 'functions with hash arguments' do
279
+
280
+ it 'matches an empty argument hash with an empty parameter hash' do
281
+
282
+ subject.defn(:foo, {}) { true }
283
+ subject.new.foo({}).should be_true
284
+
285
+ lambda {
286
+ subject.new.foo({one: :two})
287
+ }.should raise_error(NoMethodError)
288
+ end
289
+
290
+ it 'matches when all hash keys and values match' do
291
+
292
+ subject.defn(:foo, {bar: :baz}) { true }
293
+ subject.new.foo(bar: :baz).should be_true
294
+
295
+ lambda {
296
+ subject.new.foo({one: :two})
297
+ }.should raise_error(NoMethodError)
298
+ end
299
+
300
+ it 'matches when every pattern key/value are in the argument' do
301
+
302
+ subject.defn(:foo, {bar: :baz}) { true }
303
+ subject.new.foo(foo: :bar, bar: :baz).should be_true
304
+ end
305
+
306
+ it 'matches when all keys with unbound values in the pattern have an argument' do
307
+
308
+ subject.defn(:foo, {bar: PatternMatching::UNBOUND}) { true }
309
+ subject.new.foo(bar: :baz).should be_true
310
+ end
311
+
312
+ it 'passes unbound values to the block' do
313
+
314
+ subject.defn(:foo, {bar: PatternMatching::UNBOUND}) {|arg| arg }
315
+ subject.new.foo(bar: :baz).should eq :baz
316
+ end
317
+
318
+ it 'passes the matched hash to the block' do
319
+
320
+ subject.defn(:foo, {bar: :baz}) { |opts| opts }
321
+ subject.new.foo(bar: :baz).should == {bar: :baz}
322
+ end
323
+
324
+ it 'does not match a non-hash argument' do
325
+
326
+ subject.defn(:foo, {}) { true }
327
+
328
+ lambda {
329
+ subject.new.foo(:bar)
330
+ }.should raise_error(NoMethodError)
331
+ end
332
+
333
+ it 'supports idiomatic has-as-last-argument syntax' do
334
+
335
+ subject.defn(:foo, PatternMatching::UNBOUND) { |opts| opts }
336
+ subject.new.foo(bar: :baz, one: 1, many: 2).should == {bar: :baz, one: 1, many: 2}
337
+ end
338
+ end
339
+
340
+ context 'varaible-length argument lists' do
341
+
342
+ it 'supports ALL as the last parameter' do
343
+
344
+ subject.defn(:foo, 1, 2, PatternMatching::ALL) { |args| args }
345
+ subject.new.foo(1, 2, 3).should == [3]
346
+ subject.new.foo(1, 2, :foo, :bar).should == [:foo, :bar]
347
+ subject.new.foo(1, 2, :foo, :bar, one: 1, two: 2).should == [:foo, :bar, {one: 1, two: 2}]
348
+ end
349
+ end
350
+
351
+ context 'guard clauses' do
352
+
353
+ it 'matches when the guard clause returns true' do
354
+
355
+ subject.defn(:old_enough, PatternMatching::UNBOUND){
356
+ true
357
+ }.when{|x| x > 16 }
358
+
359
+ subject.new.old_enough(20).should be_true
360
+ end
361
+
362
+ it 'does not match when the guard clause returns false' do
363
+
364
+ subject.defn(:old_enough, PatternMatching::UNBOUND){
365
+ true
366
+ }.when{|x| x > 16 }
367
+
368
+ lambda {
369
+ subject.new.old_enough(10)
370
+ }.should raise_error(NoMethodError)
371
+ end
372
+
373
+ it 'continues pattern matching when the guard clause returns false' do
374
+
375
+ subject.defn(:old_enough, PatternMatching::UNBOUND){
376
+ true
377
+ }.when{|x| x > 16 }
378
+
379
+ subject.defn(:old_enough, PatternMatching::UNBOUND) { false }
380
+
381
+ subject.new.old_enough(10).should be_false
382
+ end
383
+
384
+ it 'raises an exception when the guard clause does not have a block' do
385
+
386
+ lambda {
387
+ subject.defn(:foo).when
388
+ }.should raise_error(ArgumentError)
389
+ end
390
+
391
+ end
392
+
393
+ end