pattern-matching 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dc55948d3772a5d4805826b923d12ae1f84f09cd
4
+ data.tar.gz: 6cf392b7bd93afe0e752c0fca586c2454ca583c1
5
+ SHA512:
6
+ metadata.gz: 630fb32894b0074e0e3f273f8d83c37e845c1becc3609e532d226e2e7308389bf744296d9ea411fa8e0b2dbb3c51f21c87e22db37db46726128d4be6dbbd0a15
7
+ data.tar.gz: 9369b4042e2cd7760cd2ade36aa5c7e8cdc545f5071e20b67af0c0f98e6971ba17c193899ff6fdd8f6efd1608093e3b4530d129f3f8e16419a66af051224c254
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) Jerry D'Antonio -- released under the MIT license.
2
+
3
+ http://www.opensource.org/licenses/mit-license.php
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # PatternMatching [![Build Status](https://secure.travis-ci.org/jdantonio/pattern_matching.png)](http://travis-ci.org/jdantonio/pattern_matching?branch=master) [![Dependency Status](https://gemnasium.com/jdantonio/pattern_matching.png)](https://gemnasium.com/jdantonio/pattern_matching)
2
+
3
+ A gem for adding Erlang-style pattern matching to Ruby classes.
4
+
5
+ *NOTE: This is a work in progress. Expect changes.*
6
+
7
+ ## Introduction
8
+
9
+ [Ruby](http://www.ruby-lang.org/en/) is my favorite programming by far. As much as I love Ruby I've always been a little disappointed that Ruby doesn't support function overloading. Function overloading tends to reduce branching and keep functions signatures simpler. No sweat, I learned to do without. Then I started programming in [Erlang](http://www.erlang.org/)…
10
+
11
+ I've really started to enjoy working in Erlang. Erlang is good at all the things Ruby is bad at and vice versa. Together, Ruby and Erlang make me happy. My favotite Erlang feature is, without question, [pattern matching](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching). Pattern matching is like function overloading cranked to 11. So one day I was musing on Twitter and one of my friends responded with "Build it!" So I did. And here it is.
12
+
13
+ ### Goals
14
+
15
+ * Stay true to the spirit of Erlang pattern matching, if not the semantics
16
+ * Keep the semantics as idiomatic Ruby as possible
17
+ * Support features that make sense in Ruby
18
+ * Exclude features that only make sense in Erlang
19
+ * Avoid using #method_missing
20
+
21
+ ### Features
22
+
23
+ * Basic pattern matching for instance methods.
24
+ * Parameter count matching
25
+ * Mathing against primitive values
26
+ * Matching by class/datatype
27
+ * Matching against specific key/vaue pairs in hashes
28
+ * Matching against the presence of keys within hashes
29
+ * Reasonable error messages when no match is found
30
+ * Dispatching to superclass methods when no match is found
31
+ * Recursive calls to other pattern matches
32
+ * Recursive calls to superclass methods
33
+
34
+ ### To-do
35
+
36
+ * Variable-length argument lists
37
+ * Matching against array elements
38
+ * Guard clauses
39
+ * Support class methods
40
+ * Support module instance methods
41
+ * Support module methods
42
+
43
+ ## Install
44
+
45
+ gem install pattern-matching
46
+
47
+ or add the following line to Gemfile:
48
+
49
+ gem 'pattern-matching
50
+
51
+ and run `bundle install` from your shell.
52
+
53
+ ## Supported Ruby versions
54
+
55
+ MRI 1.9.x and above. Anything else and your mileage may vary.
56
+
57
+ ## Examples
58
+
59
+ For more examples see the integration tests in *spec/integration_spec.rb*.
60
+
61
+ ### Simple Functions
62
+
63
+ This example is based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions) in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
64
+
65
+ Erlang:
66
+
67
+ greet(male, Name) ->
68
+ io:format("Hello, Mr. ~s!", [Name]);
69
+ greet(female, Name) ->
70
+ io:format("Hello, Mrs. ~s!", [Name]);
71
+ greet(_, Name) ->
72
+ io:format("Hello, ~s!", [Name]).
73
+
74
+ Ruby:
75
+
76
+ require 'pattern_matching'
77
+
78
+ class Foo
79
+ include PatternMatching
80
+
81
+ defn(:greet, _) do |name|
82
+ "Hello, #{name}!"
83
+ end
84
+
85
+ defn(:greet, :male, _) { |name|
86
+ "Hello, Mr. #{name}!"
87
+ }
88
+ defn(:greet, :female, _) { |name|
89
+ "Hello, Ms. #{name}!"
90
+ }
91
+ defn(:greet, _, _) { |_, name|
92
+ "Hello, #{name}!"
93
+ }
94
+ end
95
+
96
+ ### Simple Functions with Overloading
97
+
98
+ This example is based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions) in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
99
+
100
+ Erlang:
101
+
102
+ greet(Name) ->
103
+ io:format("Hello, ~s!", [Name]).
104
+
105
+ greet(male, Name) ->
106
+ io:format("Hello, Mr. ~s!", [Name]);
107
+ greet(female, Name) ->
108
+ io:format("Hello, Mrs. ~s!", [Name]);
109
+ greet(_, Name) ->
110
+ io:format("Hello, ~s!", [Name]).
111
+
112
+ Ruby:
113
+
114
+ require 'pattern_matching'
115
+
116
+ class Foo
117
+ include PatternMatching
118
+
119
+ defn(:greet, _) do |name|
120
+ "Hello, #{name}!"
121
+ end
122
+
123
+ defn(:greet, :male, _) { |name|
124
+ "Hello, Mr. #{name}!"
125
+ }
126
+ defn(:greet, :female, _) { |name|
127
+ "Hello, Ms. #{name}!"
128
+ }
129
+ defn(:greet, nil, _) { |name|
130
+ "Goodbye, #{name}!"
131
+ }
132
+ defn(:greet, _, _) { |_, name|
133
+ "Hello, #{name}!"
134
+ }
135
+ end
136
+
137
+ ### Matching by Class/Datatype
138
+
139
+ Ruby:
140
+
141
+ require 'pattern_matching'
142
+
143
+ class Foo
144
+ include PatternMatching
145
+
146
+ defn(:concat, Integer, Integer) { |first, second|
147
+ first + second
148
+ }
149
+ defn(:concat, Integer, String) { |first, second|
150
+ "#{first} #{second}"
151
+ }
152
+ defn(:concat, String, String) { |first, second|
153
+ first + second
154
+ }
155
+ defn(:concat, Integer, _) { |first, second|
156
+ first + second.to_i
157
+ }
158
+ end
159
+
160
+ ### Matching a Hash Parameter
161
+
162
+ Ruby:
163
+
164
+ require 'pattern_matching'
165
+
166
+ class Foo
167
+ include PatternMatching
168
+
169
+ defn(:hashable, {foo: :bar}) { |opts|
170
+ # matches any hash with key :foo and value :bar
171
+ :foo_bar
172
+ }
173
+ defn(:hashable, {foo: _}) { |opts|
174
+ # matches any hash with :key foo regardless of value
175
+ :foo_unbound
176
+ }
177
+ defn(:hashable, {}) { |opts|
178
+ # matches any hash
179
+ :unbound_unbound
180
+ }
181
+ end
@@ -0,0 +1,90 @@
1
+ module PatternMatching
2
+
3
+ VERSION = '0.0.1'
4
+
5
+ UNBOUND = Unbound = Class.new
6
+
7
+ def self.included(base)
8
+
9
+ base.instance_variable_set(:@__function_pattern_matches__, Hash.new)
10
+
11
+ def __match_pattern__(args, pattern) # :nodoc:
12
+ return unless args.length == pattern.length
13
+ pattern.each_with_index do |p, i|
14
+ arg = args[i]
15
+ next if p.is_a?(Class) && arg.is_a?(p)
16
+ if p.is_a?(Hash) && arg.is_a?(Hash)
17
+ next if p.empty?
18
+ p.each do |key, value|
19
+ return false unless arg.has_key?(key)
20
+ next if value == UNBOUND
21
+ return false unless arg[key] == value
22
+ end
23
+ next
24
+ end
25
+ return false unless p == UNBOUND || p == arg
26
+ end
27
+ return true
28
+ end
29
+
30
+ def __pattern_match__(func, *args, &block) # :nodoc:
31
+ clazz = self.class
32
+
33
+ matchers = clazz.instance_variable_get(:@__function_pattern_matches__)
34
+ matchers = matchers[func]
35
+
36
+ # scan through all patterns for this function
37
+ match = nil
38
+ matchers.each do |matcher|
39
+ if __match_pattern__(args.first, matcher.first)
40
+ match = matcher
41
+ break(matcher)
42
+ end
43
+ end
44
+
45
+ # if a match is found call the block
46
+ if match.nil?
47
+ [:nomatch, nil]
48
+ else
49
+ argv = []
50
+ match.first.each_with_index do |p, i|
51
+ argv << args.first[i] if p == UNBOUND || p.is_a?(Class) || p.is_a?(Hash)
52
+ end
53
+ return [:ok, self.instance_exec(*argv, &match.last)]
54
+ end
55
+ end
56
+
57
+ class << base
58
+
59
+ def _() # :nodoc:
60
+ return UNBOUND
61
+ end
62
+
63
+ def __add_pattern_for__(func, *args, &block) # :nodoc:
64
+ block = Proc.new{} unless block_given?
65
+ matchers = self.instance_variable_get(:@__function_pattern_matches__)
66
+ matchers[func] = [] unless matchers.has_key?(func)
67
+ matchers[func] << [args, block]
68
+ end
69
+
70
+ def defn(func, *args, &block)
71
+
72
+ block = Proc.new{} unless block_given?
73
+ __add_pattern_for__(func, *args, &block)
74
+
75
+ unless self.instance_methods(false).include?(func)
76
+ self.send(:define_method, func) do |*args, &block|
77
+ result, value = __pattern_match__(func, args, block)
78
+ return value if result == :ok
79
+ begin
80
+ super(*args, &block)
81
+ rescue NoMethodError
82
+ raise NoMethodError.new("no method `#{func}` matching #{args} found for class #{self.class}")
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ describe 'integration' do
5
+
6
+ class Bar
7
+
8
+ def greet
9
+ return 'Hello, World!'
10
+ end
11
+ end
12
+
13
+ class Foo < Bar
14
+ include PatternMatching
15
+
16
+ attr_accessor :name
17
+
18
+ def initialize(name = 'baz')
19
+ @name = name
20
+ end
21
+
22
+ defn(:greet, _) do |name|
23
+ "Hello, #{name}!"
24
+ end
25
+
26
+ defn(:greet, :male, _) { |name|
27
+ "Hello, Mr. #{name}!"
28
+ }
29
+ defn(:greet, :female, _) { |name|
30
+ "Hello, Ms. #{name}!"
31
+ }
32
+ defn(:greet, nil, _) { |name|
33
+ "Goodbye, #{name}!"
34
+ }
35
+ defn(:greet, _, _) { |_, name|
36
+ "Hello, #{name}!"
37
+ }
38
+
39
+ defn(:hashable, _, {foo: :bar}, _) { |_, opts, _|
40
+ :foo_bar
41
+ }
42
+ defn(:hashable, _, {foo: _}, _) { |_, opts, _|
43
+ :foo_unbound
44
+ }
45
+ defn(:hashable, _, {}, _) { |_, opts, _|
46
+ :unbound_unbound
47
+ }
48
+
49
+ defn(:recurse) {
50
+ 'w00t!'
51
+ }
52
+ defn(:recurse, :match) {
53
+ recurse()
54
+ }
55
+ defn(:recurse, :super) {
56
+ greet()
57
+ }
58
+ defn(:recurse, :instance) {
59
+ @name
60
+ }
61
+ defn(:recurse, _) { |arg|
62
+ arg
63
+ }
64
+
65
+ defn(:concat, Integer, Integer) { |first, second|
66
+ first + second
67
+ }
68
+ defn(:concat, Integer, String) { |first, second|
69
+ "#{first} #{second}"
70
+ }
71
+ defn(:concat, String, String) { |first, second|
72
+ first + second
73
+ }
74
+ defn(:concat, Integer, _) { |first, second|
75
+ first + second.to_i
76
+ }
77
+ end
78
+
79
+ let(:name) { 'Pattern Matcher' }
80
+ subject { Foo.new(name) }
81
+
82
+ specify { subject.greet.should eq 'Hello, World!' }
83
+
84
+ specify { subject.greet('Jerry').should eq 'Hello, Jerry!' }
85
+
86
+ specify { subject.greet(:male, 'Jerry').should eq 'Hello, Mr. Jerry!' }
87
+ specify { subject.greet(:female, 'Jeri').should eq 'Hello, Ms. Jeri!' }
88
+ specify { subject.greet(:unknown, 'Jerry').should eq 'Hello, Jerry!' }
89
+ specify { subject.greet(nil, 'Jerry').should eq 'Goodbye, Jerry!' }
90
+
91
+ specify { subject.hashable(:male, {foo: :bar}, :female).should eq :foo_bar }
92
+ specify { subject.hashable(:male, {foo: :baz}, :female).should eq :foo_unbound }
93
+ specify { subject.hashable(:male, {foo: :bar, bar: :baz}, :female).should eq :foo_bar }
94
+ specify { subject.hashable(:male, {bar: :baz}, :female).should eq :unbound_unbound }
95
+
96
+ specify { subject.recurse.should eq 'w00t!' }
97
+ specify { subject.recurse(:match).should eq 'w00t!' }
98
+ specify { subject.recurse(:super).should eq 'Hello, World!' }
99
+ specify { subject.recurse(:instance).should eq name }
100
+ specify { subject.recurse(:foo).should eq :foo }
101
+
102
+ specify { subject.concat(1, 1).should eq 2 }
103
+ specify { subject.concat(1, 'shoe').should eq '1 shoe' }
104
+ specify { subject.concat('shoe', 'fly').should eq 'shoefly' }
105
+ specify { subject.concat(1, 2.9).should eq 3 }
106
+
107
+ end
@@ -0,0 +1,324 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ describe PatternMatching do
5
+
6
+ def new_clazz(&block)
7
+ clazz = Class.new
8
+ clazz.send(:include, PatternMatching)
9
+ clazz.instance_eval(&block) if block_given?
10
+ return clazz
11
+ end
12
+
13
+ subject { new_clazz }
14
+
15
+ context '#defn declaration' do
16
+
17
+ it 'can be used within a class declaration' do
18
+ lambda {
19
+ class Clazz
20
+ include PatternMatching
21
+ defn :foo
22
+ end
23
+ }.should_not raise_error
24
+ end
25
+
26
+ it 'can be used on a class object' do
27
+ lambda {
28
+ clazz = Class.new
29
+ clazz.send(:include, PatternMatching)
30
+ clazz.defn(:foo)
31
+ }.should_not raise_error
32
+ end
33
+ end
34
+
35
+ context 'parameter count' do
36
+
37
+ it 'does not match a call with not enough arguments' do
38
+
39
+ subject.defn(:foo, true) { 'expected' }
40
+
41
+ lambda {
42
+ subject.new.foo()
43
+ }.should raise_error(NoMethodError)
44
+ end
45
+
46
+ it 'does not match a call with too many arguments' do
47
+
48
+ subject.defn(:foo, true) { 'expected' }
49
+
50
+ lambda {
51
+ subject.new.foo(true, false)
52
+ }.should raise_error(NoMethodError)
53
+ end
54
+
55
+ end
56
+
57
+ context 'recursion' do
58
+
59
+ it 'defers unmatched calls to the superclass' do
60
+
61
+ class UnmatchedCallTesterSuperclass
62
+ def foo(bar)
63
+ return bar
64
+ end
65
+ end
66
+
67
+ class UnmatchedCallTesterSubclass < UnmatchedCallTesterSuperclass
68
+ include PatternMatching
69
+ defn(:foo) { 'baz' }
70
+ end
71
+
72
+ subject = UnmatchedCallTesterSubclass.new
73
+ subject.foo(:bar).should eq :bar
74
+ end
75
+
76
+ it 'can call another match from within a match' do
77
+
78
+ subject.defn(:foo, :bar) { |arg| foo(:baz) }
79
+ subject.defn(:foo, :baz) { |arg| 'expected' }
80
+
81
+ subject.new.foo(:bar).should eq 'expected'
82
+ end
83
+
84
+ it 'can call a superclass method from within a match' do
85
+
86
+ class RecursiveCallTesterSuperclass
87
+ def foo(bar)
88
+ return bar
89
+ end
90
+ end
91
+
92
+ class RecursiveCallTesterSubclass < RecursiveCallTesterSuperclass
93
+ include PatternMatching
94
+ defn(:foo, :bar) { foo(:baz) }
95
+ end
96
+
97
+ subject = RecursiveCallTesterSubclass.new
98
+ subject.foo(:bar).should eq :baz
99
+ end
100
+ end
101
+
102
+ context 'datatypes' do
103
+
104
+ it 'matches an argument of the class given in the match parameter' do
105
+
106
+ subject.defn(:foo, Integer) { 'expected' }
107
+ subject.new.foo(100).should eq 'expected'
108
+
109
+ lambda {
110
+ subject.new.foo('hello')
111
+ }.should raise_error(NoMethodError)
112
+ end
113
+
114
+ it 'passes the matched argument to the block' do
115
+
116
+ subject.defn(:foo, Integer) { |arg| arg }
117
+ subject.new.foo(100).should eq 100
118
+ end
119
+ end
120
+
121
+ context 'function with no parameters' do
122
+
123
+ it 'accepts no parameters' do
124
+
125
+ subject.defn(:foo)
126
+ obj = subject.new
127
+
128
+ lambda {
129
+ obj.foo
130
+ }.should_not raise_error(NoMethodError)
131
+ end
132
+
133
+ it 'does not accept any parameters' do
134
+
135
+ subject.defn(:foo)
136
+ obj = subject.new
137
+
138
+ lambda {
139
+ obj.foo(1)
140
+ }.should raise_error(NoMethodError)
141
+ end
142
+
143
+ it 'returns the correct value' do
144
+ subject.defn(:foo){ true }
145
+ subject.new.foo.should be_true
146
+ end
147
+ end
148
+
149
+ context 'function with one parameter' do
150
+
151
+ it 'matches a nil parameter' do
152
+
153
+ subject.defn(:foo, nil) { 'expected' }
154
+ subject.new.foo(nil).should eq 'expected'
155
+
156
+ lambda {
157
+ subject.new.foo('no match should be found')
158
+ }.should raise_error(NoMethodError)
159
+ end
160
+
161
+ it 'matches a boolean parameter' do
162
+
163
+ subject.defn(:foo, true) { 'expected' }
164
+ subject.defn(:foo, false) { 'false case' }
165
+
166
+ subject.new.foo(true).should eq 'expected'
167
+ subject.new.foo(false).should eq 'false case'
168
+
169
+ lambda {
170
+ subject.new.foo('no match should be found')
171
+ }.should raise_error(NoMethodError)
172
+ end
173
+
174
+ it 'matches a symbol parameter' do
175
+
176
+ subject.defn(:foo, :bar) { 'expected' }
177
+ subject.new.foo(:bar).should eq 'expected'
178
+
179
+ lambda {
180
+ subject.new.foo(:baz)
181
+ }.should raise_error(NoMethodError)
182
+ end
183
+
184
+ it 'matches a number parameter' do
185
+
186
+ subject.defn(:foo, 10) { 'expected' }
187
+ subject.new.foo(10).should eq 'expected'
188
+
189
+ lambda {
190
+ subject.new.foo(11.0)
191
+ }.should raise_error(NoMethodError)
192
+ end
193
+
194
+ it 'matches a string parameter' do
195
+
196
+ subject.defn(:foo, 'bar') { 'expected' }
197
+ subject.new.foo('bar').should eq 'expected'
198
+
199
+ lambda {
200
+ subject.new.foo('baz')
201
+ }.should raise_error(NoMethodError)
202
+ end
203
+
204
+ it 'matches an array parameter' do
205
+
206
+ subject.defn(:foo, [1, 2, 3]) { 'expected' }
207
+ subject.new.foo([1, 2, 3]).should eq 'expected'
208
+
209
+ lambda {
210
+ subject.new.foo([3, 4, 5])
211
+ }.should raise_error(NoMethodError)
212
+ end
213
+
214
+ it 'matches a hash parameter' do
215
+
216
+ subject.defn(:foo, bar: 1, baz: 2) { 'expected' }
217
+ subject.new.foo(bar: 1, baz: 2).should eq 'expected'
218
+
219
+ lambda {
220
+ subject.new.foo(foo: 0, bar: 1)
221
+ }.should raise_error(NoMethodError)
222
+ end
223
+
224
+ it 'matches an object parameter' do
225
+
226
+ subject.defn(:foo, OpenStruct.new(foo: :bar)) { 'expected' }
227
+ subject.new.foo(OpenStruct.new(foo: :bar)).should eq 'expected'
228
+
229
+ lambda {
230
+ subject.new.foo(OpenStruct.new(bar: :baz))
231
+ }.should raise_error(NoMethodError)
232
+ end
233
+
234
+ it 'matches an unbound parameter' do
235
+
236
+ subject.defn(:foo, PatternMatching::UNBOUND) {|arg| arg }
237
+ subject.new.foo(:foo).should eq :foo
238
+ end
239
+ end
240
+
241
+ context 'function with two parameters' do
242
+
243
+ it 'matches two bound arguments' do
244
+
245
+ subject.defn(:foo, :male, :female){ 'expected' }
246
+ subject.new.foo(:male, :female).should eq 'expected'
247
+
248
+ lambda {
249
+ subject.new.foo(1, 2)
250
+ }.should raise_error(NoMethodError)
251
+ end
252
+
253
+ it 'matches two unbound arguments' do
254
+
255
+ subject.defn(:foo, PatternMatching::UNBOUND, PatternMatching::UNBOUND) do |first, second|
256
+ [first, second]
257
+ end
258
+ subject.new.foo(:male, :female).should eq [:male, :female]
259
+ end
260
+
261
+ it 'matches when the first argument is bound and the second is not' do
262
+
263
+ subject.defn(:foo, :male, PatternMatching::UNBOUND) do |second|
264
+ second
265
+ end
266
+ subject.new.foo(:male, :female).should eq :female
267
+ end
268
+
269
+ it 'matches when the second argument is bound and the first is not' do
270
+
271
+ subject.defn(:foo, PatternMatching::UNBOUND, :female) do |first|
272
+ first
273
+ end
274
+ subject.new.foo(:male, :female).should eq :male
275
+ end
276
+ end
277
+
278
+ context 'functions with hash arguments' do
279
+
280
+ it 'matches when all hash keys and values match' do
281
+
282
+ subject.defn(:foo, {bar: :baz}) { true }
283
+ subject.new.foo(bar: :baz).should be_true
284
+
285
+ lambda {
286
+ subject.new.foo({one: :two})
287
+ }.should raise_error(NoMethodError)
288
+ end
289
+
290
+ it 'matches when the pattern uses an empty hash' do
291
+
292
+ subject.defn(:foo, {}) { true }
293
+ subject.new.foo(bar: :baz).should be_true
294
+ end
295
+
296
+ it 'matches when every pattern key/value are in the argument' do
297
+
298
+ subject.defn(:foo, {bar: :baz}) { true }
299
+ subject.new.foo(foo: :bar, bar: :baz).should be_true
300
+ end
301
+
302
+ it 'matches when all keys with unbound values in the pattern have an argument' do
303
+
304
+ subject.defn(:foo, {bar: PatternMatching::UNBOUND}) { true }
305
+ subject.new.foo(bar: :baz).should be_true
306
+ end
307
+
308
+ it 'passes the matched hash to the block' do
309
+
310
+ subject.defn(:foo, {bar: PatternMatching::UNBOUND}) { |args| args }
311
+ subject.new.foo(bar: :baz).should == {bar: :baz}
312
+ end
313
+
314
+ it 'does not match a non-hash argument' do
315
+
316
+ subject.defn(:foo, {}) { true }
317
+
318
+ lambda {
319
+ subject.new.foo(:bar)
320
+ }.should raise_error(NoMethodError)
321
+ end
322
+ end
323
+
324
+ end
@@ -0,0 +1,17 @@
1
+ require 'pattern_matching'
2
+
3
+ # import all the support files
4
+ Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require File.expand_path(f) }
5
+
6
+ RSpec.configure do |config|
7
+
8
+ config.before(:suite) do
9
+ end
10
+
11
+ config.before(:each) do
12
+ end
13
+
14
+ config.after(:each) do
15
+ end
16
+
17
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pattern-matching
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jerry D'Antonio
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: |2
28
+ A gem for adding Erlang-style pattern matching to Ruby classes.
29
+ email: jerry.dantonio@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files:
33
+ - README.md
34
+ - LICENSE
35
+ files:
36
+ - README.md
37
+ - LICENSE
38
+ - lib/pattern_matching.rb
39
+ - spec/integration_spec.rb
40
+ - spec/pattern_matching_spec.rb
41
+ - spec/spec_helper.rb
42
+ homepage: https://github.com/jdantonio/pattern_matching/
43
+ licenses:
44
+ - MIT
45
+ metadata: {}
46
+ post_install_message: Happy matching!
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.9.2
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.0.3
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Erlang-style pattern matching to Ruby classes.
66
+ test_files:
67
+ - spec/integration_spec.rb
68
+ - spec/pattern_matching_spec.rb
69
+ - spec/spec_helper.rb
70
+ has_rdoc: