functional-ruby 0.7.2 → 0.7.3

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