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.
- data/LICENSE +21 -21
- data/README.md +193 -193
- data/lib/functional.rb +4 -4
- data/lib/functional/behavior.rb +132 -124
- data/lib/functional/behaviour.rb +2 -2
- data/lib/functional/pattern_matching.rb +133 -133
- data/lib/functional/utilities.rb +192 -192
- data/lib/functional/version.rb +3 -3
- data/lib/functional_ruby.rb +1 -1
- data/md/behavior.md +188 -188
- data/md/pattern_matching.md +512 -512
- data/md/utilities.md +55 -55
- data/spec/functional/behavior_spec.rb +464 -369
- data/spec/functional/integration_spec.rb +205 -205
- data/spec/functional/pattern_matching_spec.rb +418 -418
- data/spec/functional/utilities_spec.rb +271 -271
- data/spec/spec_helper.rb +18 -18
- metadata +20 -21
- checksums.yaml +0 -7
@@ -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
|