functional-ruby 0.7.1 → 0.7.2

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