matchete 0.0.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +60 -15
- data/lib/matchete.rb +26 -12
- data/matchete.gemspec +3 -3
- data/spec/matchete_spec.rb +37 -37
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c15b81dead014a1340a37b9172d0ae776022403e
|
4
|
+
data.tar.gz: 41f24df6592f9844db61c761244adc461a7f188d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 374025ed07b2683c768cb4129e72ec3b0d6293282ea6356122f630271bf6d53ff198175344d98e27d8673e11e280ba3443f644d6f71b68e607e82188358b65e8
|
7
|
+
data.tar.gz: ef2763cf51026c47d1e9fd04b8087ee0ea2183c8647c68414159af74b79e6bf771d216373130249bcc0a9a37bca162968733400316b8d647aedba0cc39618b18
|
data/README.md
CHANGED
@@ -5,10 +5,26 @@ Matchete provides a DSL for method overloading based on pattern matching for Rub
|
|
5
5
|
|
6
6
|
It's just a quick hack inspired by weissbier and the use-return-values-of-method-definitions DSL technique used in [harmonic](https://github.com/s2gatev/harmonic)
|
7
7
|
|
8
|
+
**It supports only ruby 2.1+**
|
9
|
+
|
10
|
+
Features
|
11
|
+
--------
|
12
|
+
|
13
|
+
* `on [:value, Integer]` matches an arg with the same internal structure
|
14
|
+
* `on '#method_name'` matches args responding to `method_name`
|
15
|
+
* `on AClass` matches instances of `AClass`
|
16
|
+
* `on a: 2, method:...` matches keyword args
|
17
|
+
* `on :test?` matches with user-defined predicate methods
|
18
|
+
* `on either('#count', Array)` matches if any of the tests returns true for an arg
|
19
|
+
* `on full_match('#count', '#combine')` matches if all of the tests return true for an arg
|
20
|
+
|
21
|
+
|
22
|
+
|
8
23
|
Install
|
9
24
|
-----
|
10
25
|
`gem install matchete`
|
11
26
|
|
27
|
+
|
12
28
|
Usage
|
13
29
|
-----
|
14
30
|
|
@@ -34,70 +50,99 @@ FactorialStrikesAgain.new.factorial(-2) #Matchete::NotResolvedError No matching
|
|
34
50
|
```
|
35
51
|
|
36
52
|
```ruby
|
37
|
-
require 'matchete'
|
38
|
-
|
39
53
|
class Converter
|
40
54
|
include Matchete
|
41
55
|
|
56
|
+
on '#special_convert',
|
57
|
+
def convert(value)
|
58
|
+
value.special_convert
|
59
|
+
end
|
60
|
+
|
42
61
|
on Integer,
|
43
62
|
def convert(value)
|
44
63
|
[:integer, value]
|
45
64
|
end
|
46
|
-
|
65
|
+
|
47
66
|
on Hash,
|
48
67
|
def convert(values)
|
49
68
|
[:dict, values.map { |k, v| [convert(k), convert(v)] }]
|
50
69
|
end
|
51
|
-
|
70
|
+
|
52
71
|
on /reserved_/,
|
53
72
|
def convert(value)
|
54
73
|
[:reserved_symbol, value]
|
55
74
|
end
|
56
|
-
|
75
|
+
|
57
76
|
on String,
|
58
77
|
def convert(value)
|
59
78
|
[:string, value]
|
60
79
|
end
|
61
|
-
|
80
|
+
|
62
81
|
on ['deleted', [Integer, Any]],
|
63
82
|
def convert(value)
|
64
83
|
['deleted', value[1]]
|
65
84
|
end
|
66
|
-
|
67
|
-
on :
|
85
|
+
|
86
|
+
on :starts_with_cat?,
|
68
87
|
def convert(value)
|
69
88
|
[:fail, value]
|
70
89
|
end
|
71
|
-
|
90
|
+
|
72
91
|
on free: Integer, method:
|
73
92
|
def convert(free:)
|
74
93
|
[:rofl, free]
|
75
94
|
end
|
76
95
|
|
96
|
+
on either('#count', Array),
|
97
|
+
def convert(value)
|
98
|
+
value.count
|
99
|
+
end
|
100
|
+
|
101
|
+
on full_match('#count', '#lala'),
|
102
|
+
def convert(value)
|
103
|
+
value.count + value.lala
|
104
|
+
end
|
105
|
+
|
77
106
|
default def convert(value)
|
78
107
|
[:z, value]
|
79
108
|
end
|
80
|
-
|
81
|
-
def
|
82
|
-
value.
|
109
|
+
|
110
|
+
def starts_with_cat?(value)
|
111
|
+
value.to_s.start_with?('cat')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Z
|
116
|
+
def special_convert
|
117
|
+
[:special_convert, nil]
|
83
118
|
end
|
84
119
|
end
|
85
120
|
|
86
121
|
converter = Converter.new
|
122
|
+
p Converter.instance_methods
|
87
123
|
p converter.convert(2) #[:integer, 2]
|
124
|
+
p converter.convert(Z.new) #[:special_convert, nil]
|
125
|
+
p converter.convert([4, 4]) # 2
|
88
126
|
p converter.convert({2 => 4}) #[:dict, [[[:integer, 2], [:integer, 4]]]
|
89
|
-
p converter.convert('reserved_l') #[
|
127
|
+
p converter.convert('reserved_l') #[:reserved_symbol, 'reserved_l']
|
90
128
|
p converter.convert('zaza') #[:string, 'zaza']
|
91
129
|
p converter.convert(['deleted', [2, Array]]) #['deleted', [2, Array]]
|
92
|
-
p converter.convert(:
|
130
|
+
p converter.convert(:cat_hehe) #[:fail, :cat_hehe]
|
93
131
|
p converter.convert(free: 2) #[:rofl, 2]
|
94
132
|
p converter.convert(2.2) #[:z, 2.2]
|
133
|
+
|
95
134
|
```
|
135
|
+
cbb
|
136
|
+
-----
|
137
|
+

|
96
138
|
|
97
139
|
Todo
|
98
140
|
-----
|
141
|
+
* Clean up the specs, right now they're a mess.
|
142
|
+
* Fix all kinds of edge cases
|
143
|
+
|
99
144
|
|
100
145
|
Copyright
|
101
146
|
-----
|
102
147
|
|
103
|
-
Copyright (c)
|
148
|
+
Copyright (c) 2015 Alexander Ivanov. See LICENSE for further details.
|
data/lib/matchete.rb
CHANGED
@@ -32,6 +32,14 @@ module Matchete
|
|
32
32
|
convert_to_matcher method_name
|
33
33
|
end
|
34
34
|
|
35
|
+
def either(*guards)
|
36
|
+
-> arg { guards.any? { |g| match_guard(g, arg) } }
|
37
|
+
end
|
38
|
+
|
39
|
+
def full_match(*guards)
|
40
|
+
-> arg { guards.all? { |g| match_guard(g, arg) } }
|
41
|
+
end
|
42
|
+
|
35
43
|
def supporting(*method_names)
|
36
44
|
-> object do
|
37
45
|
method_names.all? do |method_name|
|
@@ -46,22 +54,22 @@ module Matchete
|
|
46
54
|
end
|
47
55
|
end
|
48
56
|
end
|
49
|
-
|
57
|
+
|
50
58
|
def call_overloaded(method_name, args: [], kwargs: {})
|
51
59
|
handler = find_handler(method_name, args, kwargs)
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
handler.bind(self).call *args, **kwargs
|
56
|
-
else
|
60
|
+
|
61
|
+
if kwargs.empty?
|
57
62
|
handler.bind(self).call *args
|
63
|
+
else
|
64
|
+
handler.bind(self).call *args, **kwargs
|
58
65
|
end
|
59
66
|
#insane workaround, because if you have
|
60
67
|
#def z(f);end
|
61
68
|
#and you call it like that
|
62
|
-
#
|
69
|
+
#empty = {}
|
70
|
+
#z(2, **empty)
|
63
71
|
#it raises wrong number of arguments (2 for 1)
|
64
|
-
#clean later
|
72
|
+
#clean up later
|
65
73
|
end
|
66
74
|
|
67
75
|
def find_handler(method_name, args, kwargs)
|
@@ -77,12 +85,13 @@ module Matchete
|
|
77
85
|
raise NotResolvedError.new("No matching #{method_name} method for args #{args}")
|
78
86
|
end
|
79
87
|
else
|
80
|
-
guards
|
88
|
+
guards.last
|
81
89
|
end
|
82
90
|
end
|
83
91
|
|
84
92
|
def match_guards(guard_args, guard_kwargs, args, kwargs)
|
85
|
-
return false if guard_args.count != args.count ||
|
93
|
+
return false if guard_args.count != args.count ||
|
94
|
+
guard_kwargs.count != kwargs.count
|
86
95
|
guard_args.zip(args).all? do |guard, arg|
|
87
96
|
match_guard guard, arg
|
88
97
|
end and
|
@@ -92,20 +101,25 @@ module Matchete
|
|
92
101
|
end
|
93
102
|
|
94
103
|
def match_guard(guard, arg)
|
104
|
+
p
|
95
105
|
case guard
|
96
106
|
when Module
|
97
107
|
arg.is_a? guard
|
98
108
|
when Symbol
|
99
109
|
send guard, arg
|
100
110
|
when Proc
|
101
|
-
|
111
|
+
instance_exec arg, &guard
|
102
112
|
when Regexp
|
103
113
|
arg.is_a? String and guard.match arg
|
104
114
|
when Array
|
105
115
|
arg.is_a?(Array) and
|
106
116
|
guard.zip(arg).all? { |child_guard, child| match_guard child_guard, child }
|
107
117
|
else
|
108
|
-
guard ==
|
118
|
+
if guard.is_a?(String) && guard[0] == '#'
|
119
|
+
arg.respond_to? guard[1..-1]
|
120
|
+
else
|
121
|
+
guard == arg
|
122
|
+
end
|
109
123
|
end
|
110
124
|
end
|
111
125
|
end
|
data/matchete.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'matchete'
|
6
|
-
s.version = '0.0
|
6
|
+
s.version = '0.2.0'
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.authors = ["Alexander Ivanov"]
|
9
9
|
s.email = ["alehander42@gmail.com"]
|
@@ -12,10 +12,10 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.description = %q{A DSL for method overloading for Ruby based on pattern matching}
|
13
13
|
|
14
14
|
s.add_development_dependency 'rspec', '~> 0'
|
15
|
-
|
15
|
+
|
16
16
|
s.license = 'MIT'
|
17
17
|
s.files = `git ls-files`.split("\n")
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
|
-
end
|
21
|
+
end
|
data/spec/matchete_spec.rb
CHANGED
@@ -20,8 +20,8 @@ describe Matchete do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
a = A.new
|
23
|
-
a.play(2).
|
24
|
-
a.play(2.2, 4).
|
23
|
+
expect(a.play(2)).to eq :integer
|
24
|
+
expect(a.play(2.2, 4)).to eq :float
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'can use a pattern based on classes and modules' do
|
@@ -45,9 +45,9 @@ describe Matchete do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
a = A.new
|
48
|
-
a.play(2).
|
49
|
-
a.play(2.2).
|
50
|
-
a.play([2]).
|
48
|
+
expect(a.play(2)).to eq :integer
|
49
|
+
expect(a.play(2.2)).to eq :float
|
50
|
+
expect(a.play([2])).to eq :enumerable
|
51
51
|
end
|
52
52
|
|
53
53
|
it 'can use a pattern based on nested arrays with classes/modules' do
|
@@ -66,8 +66,8 @@ describe Matchete do
|
|
66
66
|
end
|
67
67
|
|
68
68
|
a = A.new
|
69
|
-
a.play([2, 2.2]).
|
70
|
-
a.play([[2], Matchete]).
|
69
|
+
expect(a.play([2, 2.2])).to eq [:integer, :float]
|
70
|
+
expect(a.play([[2], Matchete])).to eq :s
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'can use a pattern based on exact values' do
|
@@ -86,9 +86,9 @@ describe Matchete do
|
|
86
86
|
end
|
87
87
|
|
88
88
|
a = A.new
|
89
|
-
a.play(2, 4).
|
90
|
-
a.play(4, 4).
|
91
|
-
|
89
|
+
expect(a.play(2, 4)).to eq 2
|
90
|
+
expect(a.play(4, 4)).to eq 4
|
91
|
+
expect { a.play(8, 2) }.to raise_error(Matchete::NotResolvedError)
|
92
92
|
end
|
93
93
|
|
94
94
|
it 'can use a pattern based on regexes' do
|
@@ -107,8 +107,8 @@ describe Matchete do
|
|
107
107
|
end
|
108
108
|
|
109
109
|
a = A.new
|
110
|
-
a.play('zewr').
|
111
|
-
a.play('yy').
|
110
|
+
expect(a.play('zewr')).to eq 'z'
|
111
|
+
expect(a.play('yy')).to eq 'y'
|
112
112
|
end
|
113
113
|
|
114
114
|
it 'can use a default method when everything else fails' do
|
@@ -121,15 +121,15 @@ describe Matchete do
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
|
125
|
-
|
124
|
+
expect { A.new.play(2.2) }.to raise_error(Matchete::NotResolvedError)
|
125
|
+
|
126
126
|
class A
|
127
127
|
default def play(value)
|
128
128
|
:else
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
-
A.new.play(2.2).
|
132
|
+
expect(A.new.play(2.2)).to eq :else
|
133
133
|
end
|
134
134
|
|
135
135
|
it 'can use a pattern based on existing predicate methods given as symbols' do
|
@@ -140,14 +140,14 @@ describe Matchete do
|
|
140
140
|
def play(value)
|
141
141
|
value
|
142
142
|
end
|
143
|
-
|
143
|
+
|
144
144
|
def even?(value)
|
145
145
|
value.remainder(2).zero? #so gay and gay
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
149
|
-
A.new.play(2).
|
150
|
-
|
149
|
+
expect(A.new.play(2)).to eq 2
|
150
|
+
expect { A.new.play(5) }.to raise_error(Matchete::NotResolvedError)
|
151
151
|
end
|
152
152
|
|
153
153
|
it 'can use a pattern based on a lambda predicate' do
|
@@ -160,8 +160,8 @@ describe Matchete do
|
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
163
|
-
A.new.play(2).
|
164
|
-
|
163
|
+
expect(A.new.play(2)).to eq 2
|
164
|
+
expect { A.new.play(7) }.to raise_error(Matchete::NotResolvedError)
|
165
165
|
end
|
166
166
|
|
167
167
|
it 'can use a pattern based on responding to methods' do
|
@@ -174,8 +174,8 @@ describe Matchete do
|
|
174
174
|
end
|
175
175
|
end
|
176
176
|
|
177
|
-
A.new.play([]).
|
178
|
-
|
177
|
+
expect(A.new.play([])).to eq []
|
178
|
+
expect { A.new.play(4) }.to raise_error(Matchete::NotResolvedError)
|
179
179
|
end
|
180
180
|
|
181
181
|
it 'can match on different keyword arguments' do
|
@@ -188,8 +188,8 @@ describe Matchete do
|
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
191
|
-
A.new.play(e: 0, f: "y").
|
192
|
-
|
191
|
+
expect(A.new.play(e: 0, f: "y")).to eq :y
|
192
|
+
expect { A.new.play(e: "f", f: Class)}.to raise_error(Matchete::NotResolvedError)
|
193
193
|
end
|
194
194
|
|
195
195
|
it 'can match on multiple different kinds of patterns' do
|
@@ -197,7 +197,7 @@ describe Matchete do
|
|
197
197
|
include Matchete
|
198
198
|
end
|
199
199
|
|
200
|
-
A.new.match_guards([Integer, Float], {}, [8, 8.8], {}).
|
200
|
+
expect(A.new.match_guards([Integer, Float], {}, [8, 8.8], {})).to be_truthy
|
201
201
|
end
|
202
202
|
|
203
203
|
describe '#match_guard' do
|
@@ -213,33 +213,33 @@ describe Matchete do
|
|
213
213
|
end
|
214
214
|
|
215
215
|
it 'matches modules and classes' do
|
216
|
-
@a.match_guard(Integer, 2).
|
217
|
-
@a.match_guard(Class, 4).
|
216
|
+
expect(@a.match_guard(Integer, 2)).to be_truthy
|
217
|
+
expect(@a.match_guard(Class, 4)).to be_falsey
|
218
218
|
end
|
219
219
|
|
220
220
|
it 'matches methods given as symbols' do
|
221
|
-
@a.match_guard(:even?, 2).
|
222
|
-
|
221
|
+
expect(@a.match_guard(:even?, 2)).to be_truthy
|
222
|
+
expect { @a.match_guard(:odd?, 4) }.to raise_error
|
223
223
|
end
|
224
224
|
|
225
225
|
it 'matches predicates given as lambdas' do
|
226
|
-
@a.match_guard(-> x { x == {} }, {}).
|
226
|
+
expect(@a.match_guard(-> x { x == {} }, {})).to be_truthy
|
227
227
|
end
|
228
228
|
|
229
229
|
it 'matches on regex' do
|
230
|
-
@a.match_guard(/a/, 'aw').
|
231
|
-
@a.match_guard(/z/, 'lol').
|
230
|
+
expect(@a.match_guard(/a/, 'aw')).to be_truthy
|
231
|
+
expect(@a.match_guard(/z/, 'lol')).to be_falsey
|
232
232
|
end
|
233
233
|
|
234
234
|
it 'matches on nested arrays' do
|
235
|
-
@a.match_guard([Integer, [:even?]], [2, [4]]).
|
236
|
-
@a.match_guard([Float, [:even?]], [2.2, [7]]).
|
235
|
+
expect(@a.match_guard([Integer, [:even?]], [2, [4]])).to be_truthy
|
236
|
+
expect(@a.match_guard([Float, [:even?]], [2.2, [7]])).to be_falsey
|
237
237
|
end
|
238
238
|
|
239
239
|
it 'matches on exact values' do
|
240
|
-
@a.match_guard(2, 2).
|
241
|
-
@a.match_guard('d', 'f').
|
240
|
+
expect(@a.match_guard(2, 2)).to be_truthy
|
241
|
+
expect(@a.match_guard('d', 'f')).to be_falsey
|
242
242
|
end
|
243
243
|
end
|
244
244
|
end
|
245
|
-
|
245
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: matchete
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Ivanov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -60,8 +60,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
60
|
version: '0'
|
61
61
|
requirements: []
|
62
62
|
rubyforge_project:
|
63
|
-
rubygems_version: 2.
|
63
|
+
rubygems_version: 2.4.6
|
64
64
|
signing_key:
|
65
65
|
specification_version: 4
|
66
66
|
summary: Method overloading for Ruby based on pattern matching
|
67
|
-
test_files:
|
67
|
+
test_files:
|
68
|
+
- spec/matchete_spec.rb
|
69
|
+
- spec/spec_helper.rb
|