pattern_matching 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +33 -0
- data/LICENSE.md +27 -0
- data/Rakefile +7 -0
- data/lib/pattern_matching.rb +181 -0
- data/lib/pattern_matching/bindings.rb +59 -0
- data/lib/pattern_matching/bindings_set.rb +84 -0
- data/lib/pattern_matching/case_equality_reversal.rb +15 -0
- data/lib/pattern_matching/configuration.rb +50 -0
- data/lib/pattern_matching/methods.rb +17 -0
- data/lib/pattern_matching/methods_with_binding_helper.rb +21 -0
- data/lib/pattern_matching/pattern_match.rb +55 -0
- data/lib/pattern_matching/proc_helpers.rb +19 -0
- data/lib/pattern_matching/version.rb +3 -0
- data/test/configuration_test.rb +0 -0
- data/test/configuration_tests/no_proc_helpers_test.rb +21 -0
- data/test/configuration_tests/renamed_proc_helpers_test.rb +52 -0
- data/test/proc_helper_test.rb +47 -0
- data/test/simple_matching_test.rb +134 -0
- data/test/test_helper.rb +57 -0
- data/test/wildcard_test.rb +79 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b06b9cf226bb98d933ddf95eb0b75788ae350bd2
|
4
|
+
data.tar.gz: 2f60e9f04cf9ffe1162c6f0689a8a510ef74d93c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce9d684d17669385f62e003c7a2961490d353bc42b069699ec2bdc9f7f865ef893f54c78cd4ac3cf48bff115976a5fb983c7679c89f9a47da8b77c3d44613e30
|
7
|
+
data.tar.gz: 76c6f315f0d7c474f8762739d6b6778d105f1492e84e3c1b0d5007b31f38f591e46a1632f961a882d1c29e135869e91dd20cc209f89c1eb80ed75256df91abf9
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pattern_matching (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ansi (1.5.0)
|
10
|
+
builder (3.2.2)
|
11
|
+
minitest (5.9.1)
|
12
|
+
minitest-reporters (1.1.11)
|
13
|
+
ansi
|
14
|
+
builder
|
15
|
+
minitest (>= 5.0)
|
16
|
+
ruby-progressbar
|
17
|
+
rake (10.5.0)
|
18
|
+
ruby-progressbar (1.8.1)
|
19
|
+
turn-again-reporter (1.1.0)
|
20
|
+
minitest-reporters (~> 1.0, >= 1.0.8)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
bundler (~> 1.7)
|
27
|
+
minitest-reporters (~> 1.1)
|
28
|
+
pattern_matching!
|
29
|
+
rake (~> 10.0)
|
30
|
+
turn-again-reporter (~> 1.1, >= 1.1.0)
|
31
|
+
|
32
|
+
BUNDLED WITH
|
33
|
+
1.13.5
|
data/LICENSE.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2016, Paul Kwiatkowski
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of adalog nor the names of its
|
15
|
+
contributors may be used to endorse or promote products derived from
|
16
|
+
this software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/Rakefile
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
# Allows for crude pattern-matching like behavior. Very crude.
|
2
|
+
# Currently just a Symbol as a status tag paired with a value.
|
3
|
+
# Provides capitalized methods (bad form, perhaps?) to make
|
4
|
+
# them stand out: `#Pattern`, `#Result` and `#Match`.
|
5
|
+
#
|
6
|
+
# Example usage:
|
7
|
+
#
|
8
|
+
# def some_computation
|
9
|
+
# Result(:ok, "awesome sauce")
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# case (result = some_computation)
|
13
|
+
# when Match(:ok)
|
14
|
+
# puts "Things went okay: #{result.value}"
|
15
|
+
# when Match(:error)
|
16
|
+
# puts "Something went wrong: #{result.value}"
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # => "Things went okay: awesome sauce"
|
20
|
+
#
|
21
|
+
# Originally named `Result` but that would have a conflicting meaning
|
22
|
+
# with the typical use of the Result Monad in most languages/environments.
|
23
|
+
# So while this is not true pattern matching, it might one day grow into
|
24
|
+
# something of the sort, and so the name fits... in a limited way.
|
25
|
+
module PatternMatching
|
26
|
+
|
27
|
+
##
|
28
|
+
# Configure behavior based on existing configuration.
|
29
|
+
# This is a lot of noodley-looking nested conditional logic but that's
|
30
|
+
# kind of the point with lots of boolean-based configuration of behavior!
|
31
|
+
def self.included(base)
|
32
|
+
if PatternMatching.config.use_binding_helper
|
33
|
+
base.const_set(PatternMatching.config.binding_helper, PatternMatching::BindingsSet.new)
|
34
|
+
if PatternMatching.config.default_binding_helper?
|
35
|
+
base.send(:include, PatternMatching::MethodsWithBindingHelper)
|
36
|
+
else
|
37
|
+
base.send(:include, PatternMatching.methods_with_custom_binding_helper)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
base.send(:include, PatternMatching::Methods)
|
41
|
+
end
|
42
|
+
|
43
|
+
if PatternMatching.config.use_proc_helpers
|
44
|
+
if PatternMatching.config.default_proc_helpers?
|
45
|
+
base.send(:include, PatternMatching::ProcHelpers)
|
46
|
+
else
|
47
|
+
base.send(:include, PatternMatching.custom_proc_helpers)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Available configuration options are:
|
54
|
+
#
|
55
|
+
# - use_proc_helpers: controls whether or not helpers for sending
|
56
|
+
# messages and calling a method in the local context are included
|
57
|
+
# with this module.
|
58
|
+
#
|
59
|
+
# - use_binding_helper: controls whether or not bindings are enabled (and thus)
|
60
|
+
# whether or not helpers are included.
|
61
|
+
#
|
62
|
+
# - send_helper: the method name used as the proc helper for
|
63
|
+
# "sending a message" to the object when matching.
|
64
|
+
#
|
65
|
+
# - call_helper: the method name used as the proc helper for "calling a
|
66
|
+
# method in the current context" with the object as an argument when matching.
|
67
|
+
#
|
68
|
+
# - binding_helper: the method name used as the binding set for each match.
|
69
|
+
def self.configure(&block)
|
70
|
+
block.call(config)
|
71
|
+
|
72
|
+
unless config.default_proc_helpers?
|
73
|
+
build_custom_proc_helpers(config.send_helper, config.call_helper)
|
74
|
+
end
|
75
|
+
|
76
|
+
if config.use_binding_helper && !config.default_binding_helper?
|
77
|
+
build_custom_binding_helper(config.binding_helper)
|
78
|
+
puts "Using a custom binding helper are we?"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Simple class-instance variable to hold configuration
|
84
|
+
def self.config
|
85
|
+
@config ||= ::PatternMatching::Configuration.default
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# For of use in testing, because of how Ruby loading works when testing
|
90
|
+
# behavior that is thread-global (?) for the instance of this module.
|
91
|
+
def self.default_configuration!
|
92
|
+
@config = ::PatternMatching::Configuration.default
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Will only be set with a proper module if a call to ::configure results in
|
97
|
+
# there being proc helpers with non-default names. Is not intelligently
|
98
|
+
# assigned, nor should it be used, when called in any manner before a call
|
99
|
+
# to ::configure that specifies an alternative value to config.call_helper
|
100
|
+
# and config.binding_helper.
|
101
|
+
def self.custom_proc_helpers
|
102
|
+
@custom_proc_helpers
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Will only be set with a proper module if a call to ::configure results in
|
107
|
+
# a binding helper set to a name which is not the default. Is not intelligently
|
108
|
+
# assigned, nor should it be used, when called in any manner before a call
|
109
|
+
# to ::configure that specifies an alternative value to config.binding_helper
|
110
|
+
def self.methods_with_custom_binding_helper
|
111
|
+
@methods_with_custom_binding_helper
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Implementations internal to the blocks of the define_method calls
|
116
|
+
# should remain identical to the implementations in the PatternMatching::ProcHelpers
|
117
|
+
# module, with the only change being that the produced module has different names for
|
118
|
+
# the methods behind the call and send helpers.
|
119
|
+
def self.build_custom_proc_helpers(send_helper, call_helper)
|
120
|
+
proc_helpers = ->() {
|
121
|
+
define_method(send_helper) do |symbol|
|
122
|
+
symbol.to_proc
|
123
|
+
end
|
124
|
+
|
125
|
+
define_method(call_helper) do |symbol|
|
126
|
+
Proc.new { |obj| self.send(symbol, obj) }
|
127
|
+
end
|
128
|
+
}
|
129
|
+
|
130
|
+
@custom_proc_helpers = Module.new
|
131
|
+
@custom_proc_helpers.class_exec(&proc_helpers)
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# The implementation of the METHODS heredoc should remain identical to the contents of
|
136
|
+
# the Match and Pattern methods of PatternMatching::MethodsWithBindingHelper except that
|
137
|
+
# here we are interpolating on our
|
138
|
+
def self.build_custom_binding_helper(binding_helper)
|
139
|
+
methods_with_binding = ->() {
|
140
|
+
eval(
|
141
|
+
<<-METHODS
|
142
|
+
def Match(*pattern)
|
143
|
+
result = ::PatternMatching::CaseEqualityReversal.new(*pattern)
|
144
|
+
(self.class)::#{binding_helper}._clear_bindings!(caller_locations(1,1)[0].label) unless result
|
145
|
+
result
|
146
|
+
end
|
147
|
+
|
148
|
+
def Pattern(*pattern)
|
149
|
+
(self.class)::#{binding_helper}._clear_bindings!(caller_locations(1,1)[0].label)
|
150
|
+
::PatternMatching::PatternMatch.new(*pattern)
|
151
|
+
end
|
152
|
+
METHODS
|
153
|
+
)
|
154
|
+
}
|
155
|
+
@methods_with_custom_binding_helper = Module.new
|
156
|
+
@methods_with_custom_binding_helper.class_exec(&methods_with_binding)
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# Additional singleton constants and wildcards
|
161
|
+
Undefined = Object.new
|
162
|
+
Any = Object.new
|
163
|
+
Head = Object.new
|
164
|
+
Tail = Object.new
|
165
|
+
def Undefined.inspect ; "<Undefined>" ; end
|
166
|
+
def Undefined.to_s ; "<Undefined>" ; end
|
167
|
+
def Any.inspect ; "<Any>" ; end
|
168
|
+
def Any.to_s ; "<Any>" ; end
|
169
|
+
def Head.inspect ; "<Head>" ; end
|
170
|
+
def Head.to_s ; "<Head>" ; end
|
171
|
+
def Tail.inspect ; "<Tail>" ; end
|
172
|
+
def Tail.to_s ; "<Tail>" ; end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
##
|
178
|
+
# Include the rest of this library.
|
179
|
+
Dir[File.join(File.dirname(__FILE__), "pattern_matching", "*.rb")].each do |rb_file|
|
180
|
+
require rb_file
|
181
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module PatternMatching
|
2
|
+
##
|
3
|
+
# Taken nearly verbatim from the idea presented by Avdi Grimm in an episode of
|
4
|
+
# the fantastic Ruby Tapas series, including the use of the >> (right-shift) operator
|
5
|
+
# as a "guard" operator, as visually stands out compared to other define-able
|
6
|
+
# binary operators but it lacks idiomatic re-use the way that << (left-shift) does.
|
7
|
+
# It also feels a little bit like the \\ guards used in Elixir, which I would have
|
8
|
+
# used, if I could find a way to have Ruby treat either \\ or // as a binary operator.
|
9
|
+
class Bindings < BasicObject
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@bindings = ::Hash.new do |hash, key|
|
13
|
+
::PatternMatching::Bindings::BoundValue.new(hash, key)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Hello darkness, my old friend.
|
19
|
+
def method_missing(msg, *)
|
20
|
+
@bindings[msg]
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
##
|
25
|
+
# The interesting work of Bindings happens in this class.
|
26
|
+
#
|
27
|
+
# Allows setting up guards via the >> oprator.
|
28
|
+
#
|
29
|
+
# If guards pass, or no guards have been added to a BoundValue,
|
30
|
+
# then comparing via == or === with that BoundValue will always
|
31
|
+
# return true, and will save the compared-to value in the hash,
|
32
|
+
# presumably provided by an instance of the Bindings class.
|
33
|
+
class BoundValue
|
34
|
+
def initialize(bindings, name)
|
35
|
+
@bindings = bindings
|
36
|
+
@name = name
|
37
|
+
@guards = []
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
return false unless @guards.all? { |g| g === other }
|
42
|
+
@bindings[@name] = other
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
def ===(other)
|
47
|
+
return false unless @guards.all? { |g| g === other }
|
48
|
+
@bindings[@name] = other
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def >>(guard)
|
53
|
+
@guards << guard
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module PatternMatching
|
2
|
+
class BindingsSet
|
3
|
+
|
4
|
+
##
|
5
|
+
# For reasons I have yet to determine, implementing BindingsSet as a subclass
|
6
|
+
# of BasicObject has caused stack overflow issues. So the current workaround
|
7
|
+
# is to just remove everything we possibly can from this class at load time
|
8
|
+
# via the very hamfisted Module#undef_method.
|
9
|
+
REMOVABLE_OBJECT_METHODS = [
|
10
|
+
:nil?,
|
11
|
+
:===,
|
12
|
+
:=~,
|
13
|
+
:!~,
|
14
|
+
:eql?,
|
15
|
+
:hash,
|
16
|
+
:<=>,
|
17
|
+
:class,
|
18
|
+
:singleton_class,
|
19
|
+
:clone,
|
20
|
+
:dup,
|
21
|
+
:taint,
|
22
|
+
:tainted?,
|
23
|
+
:untaint,
|
24
|
+
:untrust,
|
25
|
+
:untrusted?,
|
26
|
+
:trust,
|
27
|
+
:freeze,
|
28
|
+
:frozen?,
|
29
|
+
:to_s,
|
30
|
+
:inspect,
|
31
|
+
:methods,
|
32
|
+
:singleton_methods,
|
33
|
+
:protected_methods,
|
34
|
+
:private_methods,
|
35
|
+
:public_methods,
|
36
|
+
:instance_variables,
|
37
|
+
:instance_variable_get,
|
38
|
+
:instance_variable_set,
|
39
|
+
:instance_variable_defined?,
|
40
|
+
:remove_instance_variable,
|
41
|
+
:instance_of?,
|
42
|
+
:kind_of?,
|
43
|
+
:is_a?,
|
44
|
+
:tap,
|
45
|
+
:send,
|
46
|
+
:public_send,
|
47
|
+
:respond_to?,
|
48
|
+
:extend,
|
49
|
+
:display,
|
50
|
+
:method,
|
51
|
+
:public_method,
|
52
|
+
:singleton_method,
|
53
|
+
:define_singleton_method,
|
54
|
+
:to_enum,
|
55
|
+
:enum_for,
|
56
|
+
]
|
57
|
+
|
58
|
+
REMOVABLE_OBJECT_METHODS.each do |msg|
|
59
|
+
undef_method(msg)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def initialize
|
64
|
+
@bindings = {}
|
65
|
+
end
|
66
|
+
|
67
|
+
###
|
68
|
+
# Based on the caller's method name (obviously not an universally-optimal choice),
|
69
|
+
# cache a Bindings object and forward all messages there.
|
70
|
+
def method_missing(msg, *)
|
71
|
+
caller_label = caller_locations(1,1)[0].label
|
72
|
+
@bindings[caller_label] ||= PatternMatching::Bindings.new
|
73
|
+
@bindings[caller_label].send(msg)
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Used internally as a hacky hook into auto-bindings with the B constant,
|
78
|
+
# when enabled. See documentation for automatic bindings.
|
79
|
+
def _clear_bindings!(caller_label)
|
80
|
+
@bindings.delete(caller_label)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PatternMatching
|
2
|
+
##
|
3
|
+
# Used by #Match to invert the call to `===` by `when` clauses
|
4
|
+
class CaseEqualityReversal < BasicObject
|
5
|
+
|
6
|
+
def initialize(*pattern)
|
7
|
+
@pattern = pattern
|
8
|
+
end
|
9
|
+
|
10
|
+
def ===(other)
|
11
|
+
other.===(*@pattern)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module PatternMatching
|
2
|
+
##
|
3
|
+
# Available configuration options are:
|
4
|
+
#
|
5
|
+
# - use_proc_helpers: controls whether or not helpers for sending
|
6
|
+
# messages and calling a method in the local context are included
|
7
|
+
# with this module.
|
8
|
+
#
|
9
|
+
# - use_binding_helper: controls whether or not bindings are enabled (and thus)
|
10
|
+
# whether or not helpers are included.
|
11
|
+
#
|
12
|
+
# - send_helper: the method name used as the proc helper for
|
13
|
+
# "sending a message" to the object when matching.
|
14
|
+
#
|
15
|
+
# - call_helper: the method name used as the proc helper for "calling a
|
16
|
+
# method in the current context" with the object as an argument when matching.
|
17
|
+
#
|
18
|
+
# - binding_helper: the method name used as the binding set for each match.
|
19
|
+
class Configuration
|
20
|
+
|
21
|
+
def self.default
|
22
|
+
new(true, true, :S, :C, :B)
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :use_proc_helpers,
|
26
|
+
:use_binding_helper,
|
27
|
+
:send_helper,
|
28
|
+
:call_helper,
|
29
|
+
:binding_helper
|
30
|
+
|
31
|
+
def initialize(use_proc_helpers, use_binding_helper, send_helper, call_helper, binding_helper)
|
32
|
+
@use_proc_helpers = use_proc_helpers
|
33
|
+
@use_binding_helper = use_binding_helper
|
34
|
+
@send_helper = send_helper
|
35
|
+
@call_helper = call_helper
|
36
|
+
@binding_helper = binding_helper
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def default_proc_helpers?
|
41
|
+
:S == send_helper && :C == call_helper
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def default_binding_helper?
|
46
|
+
:B == binding_helper
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module PatternMatching
|
2
|
+
module Methods
|
3
|
+
|
4
|
+
##
|
5
|
+
# Wraps a matchable 'pattern' in an object that inverts `===` (case-equality method).
|
6
|
+
def Match(*pattern)
|
7
|
+
::PatternMatching::CaseEqualityReversal.new(*pattern)
|
8
|
+
end
|
9
|
+
|
10
|
+
##
|
11
|
+
# Wraps an argument list as a pattern for use in a call to #Match
|
12
|
+
def Pattern(*pattern)
|
13
|
+
::PatternMatching::PatternMatch.new(*pattern)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module PatternMatching
|
2
|
+
module MethodsWithBindingHelper
|
3
|
+
|
4
|
+
##
|
5
|
+
# Wraps a matchable 'pattern' in an object that inverts `===` (case-equality method).
|
6
|
+
def Match(*pattern)
|
7
|
+
result = ::PatternMatching::CaseEqualityReversal.new(*pattern)
|
8
|
+
(self.class)::B._clear_bindings!(caller_locations(1,1)[0].label) unless result
|
9
|
+
result
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Wraps an argument list as a pattern for use in a call to #Match
|
14
|
+
def Pattern(*pattern)
|
15
|
+
(self.class)::B._clear_bindings!(caller_locations(1,1)[0].label)
|
16
|
+
::PatternMatching::PatternMatch.new(*pattern)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module PatternMatching
|
2
|
+
##
|
3
|
+
# Encapsulates pattern matching behaviors such as deep-matching of collection
|
4
|
+
# types as well as wildcard values such as `Any`.
|
5
|
+
class PatternMatch
|
6
|
+
|
7
|
+
attr_reader :pattern
|
8
|
+
alias_method :value, :pattern
|
9
|
+
|
10
|
+
def initialize(*pattern)
|
11
|
+
@pattern = pattern
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# As both self.pattern and other are slurped, they are guaranteed to begin as
|
16
|
+
# Arrays (i.e. they are enumerable). Thus all matches begin by checking a match
|
17
|
+
# as if the values are enumerable.
|
18
|
+
def ===(*other)
|
19
|
+
match_enumerable(pattern, other)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
private ######################################################################
|
24
|
+
|
25
|
+
##
|
26
|
+
# Enumerable arguments with different lengths are not equal, at least as long as
|
27
|
+
# wildcards such as Head and Tail remain un-implemented. This is used as a
|
28
|
+
# fast-reject clause. Otherwise, iteration is used to decide
|
29
|
+
#
|
30
|
+
# TODO: There might be a better way of determining which objects are legitimately
|
31
|
+
# useful as matachable collections than simply descending from Enumerable or
|
32
|
+
# responding to #length, #zip and #reduce, as is implicit here.
|
33
|
+
def match_enumerable(from_self, from_other)
|
34
|
+
return false if from_self.length < from_other.length
|
35
|
+
combined = from_self.zip(from_other)
|
36
|
+
combined.reduce(true) do |acc, (self_item, other_item)|
|
37
|
+
acc && match_item(self_item, other_item)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Handles matching for non-collection values, including the logic behind
|
43
|
+
# the wildcard Any. In the case of a collection, defers instead to #match_enumerable.
|
44
|
+
def match_item(from_self, from_other)
|
45
|
+
if Any == from_other
|
46
|
+
true
|
47
|
+
elsif Enumerable === from_other && Enumerable === from_self
|
48
|
+
match_enumerable(from_self, from_other)
|
49
|
+
else
|
50
|
+
from_other === from_self
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module PatternMatching
|
2
|
+
module ProcHelpers
|
3
|
+
|
4
|
+
##
|
5
|
+
# S for 'send', as in "send message to object".
|
6
|
+
# Allows for prettier Proc pattern-matches than simply :sym.to_proc everywhere
|
7
|
+
def S(symbol)
|
8
|
+
symbol.to_proc
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# C for 'call', as in "call method in current context".
|
13
|
+
# Allows for prettier Method pattern-matches than method(:sym)
|
14
|
+
def C(symbol)
|
15
|
+
Proc.new { |obj| self.send(symbol, obj) }
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
PatternMatching.configure do |config|
|
4
|
+
config.use_proc_helpers = false
|
5
|
+
end
|
6
|
+
|
7
|
+
module ConfigurationTests
|
8
|
+
class NoProcHelpers < Minitest::Test
|
9
|
+
include PatternMatching
|
10
|
+
|
11
|
+
test "send helper is not defined" do
|
12
|
+
refute_respond_to(self, :S)
|
13
|
+
end
|
14
|
+
|
15
|
+
test "call helper is not defined" do
|
16
|
+
refute_respond_to(self, :C)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
PatternMatching.configure do |config|
|
4
|
+
config.use_proc_helpers = true
|
5
|
+
config.send_helper = :foo
|
6
|
+
config.call_helper = :bar
|
7
|
+
end
|
8
|
+
|
9
|
+
module ConfigurationTests
|
10
|
+
class RenamedProcHelpers < Minitest::Test
|
11
|
+
include PatternMatching
|
12
|
+
|
13
|
+
test "send helper is defined as foo" do
|
14
|
+
assert_respond_to(self, :foo)
|
15
|
+
end
|
16
|
+
|
17
|
+
test "call helper is defined as bar" do
|
18
|
+
assert_respond_to(self, :bar)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
test "can use S as a shortcut for symbol-to-proc" do
|
23
|
+
result = Pattern(42, "", 555)
|
24
|
+
assert_match(result, foo(:even?), foo(:empty?), foo(:odd?))
|
25
|
+
refute_match(result, foo(:odd?), foo(:empty?), foo(:odd?))
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def contains_e?(str)
|
30
|
+
str.kind_of?(String) && str.count('e') > 0
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def forty_two?(val)
|
35
|
+
42 == val
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def triple_five?(val)
|
40
|
+
555 == val
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
test "can use C as a shortcut for methods in current context" do
|
45
|
+
result = Pattern(42, "streetlight", 555)
|
46
|
+
assert_match(result, bar(:forty_two?), bar(:contains_e?), bar(:triple_five?))
|
47
|
+
refute_match(result, bar(:forty_two?), bar(:forty_two?), bar(:triple_five?))
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
PatternMatching.default_configuration!
|
4
|
+
|
5
|
+
class ProcHelperTest < Minitest::Test
|
6
|
+
include PatternMatching
|
7
|
+
|
8
|
+
|
9
|
+
test "can use S as a shortcut for symbol-to-proc" do
|
10
|
+
result = Pattern(42, "", 555)
|
11
|
+
|
12
|
+
assert_match(result, S(:even?), S(:empty?), S(:odd?))
|
13
|
+
|
14
|
+
refute_match(result, S(:odd?), S(:empty?), S(:odd?))
|
15
|
+
refute_match(result, S(:even?), S(:empty?), S(:even?))
|
16
|
+
refute_match(result, S(:even?), S(:nil?), S(:odd?))
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def contains_e?(str)
|
21
|
+
str.kind_of?(String) && str.count('e') > 0
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def forty_two?(val)
|
26
|
+
42 == val
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def triple_five?(val)
|
31
|
+
555 == val
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
test "can use C as a shortcut for methods in current context" do
|
36
|
+
result = Pattern(42, "streetlight", 555)
|
37
|
+
|
38
|
+
assert_match(result, C(:forty_two?), C(:contains_e?), C(:triple_five?))
|
39
|
+
|
40
|
+
refute_match(result, C(:forty_two?), C(:forty_two?), C(:triple_five?))
|
41
|
+
refute_match(result, C(:forty_two?), C(:contains_e?), C(:contains_e?))
|
42
|
+
refute_match(result, C(:forty_two?), C(:contains_e?), C(:forty_two?))
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SimpleMatchingTest < Minitest::Test
|
4
|
+
include PatternMatching
|
5
|
+
|
6
|
+
|
7
|
+
test "matches status and exact value" do
|
8
|
+
result = Pattern( :ok, [1, 2, 3])
|
9
|
+
assert_match(result, :ok, [1, 2, 3])
|
10
|
+
refute_match(result, :ok, [1, 2])
|
11
|
+
refute_match(result, :ok, [3, 2, 1])
|
12
|
+
refute_match(result, :ok, [])
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
test "matches status without value" do
|
17
|
+
result = Pattern( :ok)
|
18
|
+
assert_match(result, :ok)
|
19
|
+
assert_match(result, Any)
|
20
|
+
refute_match(result, :ok, [1, 2, 3])
|
21
|
+
refute_match(result, :ok, Any)
|
22
|
+
refute_match(result, Any, Any)
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
test "matches class hierarchy" do
|
27
|
+
result = Pattern( :ok, [1, 2, 3])
|
28
|
+
assert_match(result, :ok, Array)
|
29
|
+
assert_match(result, Symbol, [1, 2, 3])
|
30
|
+
refute_match(result, :ok, [Array])
|
31
|
+
refute_match(result, :ok, [Fixnum])
|
32
|
+
refute_match(result, :ok, String)
|
33
|
+
refute_match(result, :ok, Fixnum)
|
34
|
+
refute_match(result, Symbol, [])
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
test "matches range inclusion" do
|
39
|
+
result = Pattern( 5)
|
40
|
+
assert_match(result, (1..10))
|
41
|
+
refute_match(result, (1...5))
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
test "matches by calling procs or lambdas" do
|
46
|
+
result = Pattern( 5)
|
47
|
+
|
48
|
+
assert_match(result, ->(val) { true })
|
49
|
+
assert_match(result, Proc.new { true })
|
50
|
+
assert_match(result, :odd?.to_proc)
|
51
|
+
|
52
|
+
refute_match(result, Proc.new { false })
|
53
|
+
refute_match(result, :even?.to_proc)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
test "matches arrays recursively" do
|
58
|
+
result = Pattern( :ok, [1, 2, 3])
|
59
|
+
|
60
|
+
assert_match(result, :ok, [1, Any, 3])
|
61
|
+
assert_match(result, :ok, [Any, Any, Any])
|
62
|
+
refute_match(result, :ok, [1, Any])
|
63
|
+
|
64
|
+
assert_match(result, :ok, [1, :even?.to_proc, 3])
|
65
|
+
refute_match(result, :ok, [1, :odd?.to_proc, 3])
|
66
|
+
|
67
|
+
assert_match(result, Symbol, [1, Numeric, 3])
|
68
|
+
refute_match(result, Symbol, [1, Float, 3])
|
69
|
+
|
70
|
+
assert_match(result, Symbol, [1, (0..10), 3])
|
71
|
+
refute_match(result, Symbol, [1, (5..10), 3])
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
test "matches hash strucutre" do
|
76
|
+
result = Pattern( :ok, { a: 1, b: 2, c: 3 })
|
77
|
+
assert_match(result, :ok, { a: 1, b: 2, c: 3 })
|
78
|
+
assert_match(result, :ok, { a: Any, b: Any, c: Any })
|
79
|
+
assert_match(result, :ok, { a: Any, b: Any, c: Any })
|
80
|
+
refute_match(result, :ok, {})
|
81
|
+
refute_match(result, :ok, { d: Any})
|
82
|
+
refute_match(result, :ok, { a: 1, b: 2, c: 3, d: 4 })
|
83
|
+
refute_match(result, :ok, { a: 1, b: 2, c: 3, d: Any })
|
84
|
+
refute_match(result, :ok, { a: Any, b: Any, c: Any, d: Any })
|
85
|
+
refute_match(result, :ok, { a: 1 })
|
86
|
+
refute_match(result, :ok, { a: '1', b: '2', c: '3' })
|
87
|
+
refute_match(result, :ok, { a: Any, b: '2', c: Any })
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
test "matches Any deeply nested arrays" do
|
92
|
+
deeply_nested = [
|
93
|
+
1,
|
94
|
+
"foo",
|
95
|
+
2,
|
96
|
+
[ "bar",
|
97
|
+
[3, 4, :five],
|
98
|
+
"baz"
|
99
|
+
],
|
100
|
+
]
|
101
|
+
result = Pattern(deeply_nested)
|
102
|
+
|
103
|
+
assert_match(result, [
|
104
|
+
1,
|
105
|
+
"foo",
|
106
|
+
2,
|
107
|
+
[ "bar",
|
108
|
+
[Numeric, 4, :five],
|
109
|
+
"baz"
|
110
|
+
],
|
111
|
+
])
|
112
|
+
|
113
|
+
refute_match(result, [
|
114
|
+
1,
|
115
|
+
"foo",
|
116
|
+
2,
|
117
|
+
[ "bar",
|
118
|
+
[3, 4, 5],
|
119
|
+
"baz"
|
120
|
+
],
|
121
|
+
])
|
122
|
+
|
123
|
+
assert_match(result, [
|
124
|
+
Any,
|
125
|
+
Any,
|
126
|
+
Any,
|
127
|
+
[ Any,
|
128
|
+
[Any, Any, Any],
|
129
|
+
Any
|
130
|
+
],
|
131
|
+
])
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'minitest/reporters'
|
4
|
+
require 'minitest/reporters/turn_again_reporter'
|
5
|
+
Minitest::Reporters.use!(Minitest::Reporters::TurnAgainReporter.new)
|
6
|
+
|
7
|
+
# Require ourselves.
|
8
|
+
require 'pattern_matching'
|
9
|
+
|
10
|
+
##
|
11
|
+
# Blatantly stolen from ActiveSupport::Testing::Declarative
|
12
|
+
# Allows for test files such as
|
13
|
+
# test "verify something" do
|
14
|
+
# ...
|
15
|
+
# end
|
16
|
+
# which become methods named test_verify_something, leaving a visual difference
|
17
|
+
# between tests themselves and any helper methods declared in the usual
|
18
|
+
# manner of `def some_helper_method`.
|
19
|
+
module DeclarativeTests
|
20
|
+
def test(name, &block)
|
21
|
+
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
22
|
+
defined = instance_method(test_name) rescue false
|
23
|
+
raise "#{test_name} is already defined in #{self}" if defined
|
24
|
+
if block_given?
|
25
|
+
define_method(test_name, &block)
|
26
|
+
else
|
27
|
+
define_method(test_name) do
|
28
|
+
flunk "No implementation provided for #{name}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
class Minitest::Test
|
36
|
+
extend DeclarativeTests
|
37
|
+
|
38
|
+
def assert_match(result, *pattern)
|
39
|
+
case result
|
40
|
+
when Match(*pattern)
|
41
|
+
assert(true)
|
42
|
+
else
|
43
|
+
assert(false, "Result of #{result.pattern}' should match pattern '#{pattern.join(', ')}'")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def refute_match(result, *pattern)
|
49
|
+
case result
|
50
|
+
when Match(*pattern)
|
51
|
+
assert(false, "Result of '#{result.pattern}' should not match pattern '#{pattern.join(', ')}'")
|
52
|
+
else
|
53
|
+
assert(true)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
PatternMatching.default_configuration!
|
4
|
+
|
5
|
+
class WildcardTest < Minitest::Test
|
6
|
+
include PatternMatching
|
7
|
+
|
8
|
+
|
9
|
+
test "matches with Any as items or enumerables" do
|
10
|
+
result = Pattern( :ok, [1, 2, 3])
|
11
|
+
assert_match(result, :ok, Any)
|
12
|
+
assert_match(result, Any, [1, 2, 3])
|
13
|
+
assert_match(result, Any, Any)
|
14
|
+
refute_match(result, Any, Any, Any)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
test "matches Any internal to arrays" do
|
19
|
+
result = Pattern( [1, 2, 3])
|
20
|
+
assert_match(result, [1, Any, 3])
|
21
|
+
assert_match(result, [Any, Any, Any])
|
22
|
+
refute_match(result, [1, Any])
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
test "matches Any internal to hashes" do
|
27
|
+
result = Pattern( { a: 1, b: 2, c: 3 })
|
28
|
+
|
29
|
+
assert_match(result, Any)
|
30
|
+
assert_match(result, { a: 1, b: 2, Any => Any })
|
31
|
+
assert_match(result, { a: 1, b: 2, Any => 3 })
|
32
|
+
assert_match(result, { a: Any, b: Any, c: Any })
|
33
|
+
assert_match(result, { a: Any, b: Any, c: Any })
|
34
|
+
|
35
|
+
refute_match(result, {})
|
36
|
+
refute_match(result, { d: Any})
|
37
|
+
refute_match(result, { a: 1, b: 2, Any => 4 })
|
38
|
+
refute_match(result, { a: 1, b: 2, c: 3, d: Any })
|
39
|
+
refute_match(result, { a: Any, b: Any, c: Any, d: Any })
|
40
|
+
refute_match(result, { a: 1 })
|
41
|
+
refute_match(result, { a: '1', b: '2', c: '3' })
|
42
|
+
refute_match(result, { a: Any, b: '2', c: Any })
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
test "matches Any in deeply nested arrays" do
|
47
|
+
deeply_nested = [
|
48
|
+
1,
|
49
|
+
"foo",
|
50
|
+
2,
|
51
|
+
[ "bar",
|
52
|
+
[3, 4, :five],
|
53
|
+
"baz"
|
54
|
+
],
|
55
|
+
]
|
56
|
+
result = Pattern(deeply_nested)
|
57
|
+
|
58
|
+
assert_match(result, [
|
59
|
+
1,
|
60
|
+
"foo",
|
61
|
+
2,
|
62
|
+
[ "bar",
|
63
|
+
[Numeric, Any, :five],
|
64
|
+
"baz"
|
65
|
+
],
|
66
|
+
])
|
67
|
+
|
68
|
+
assert_match(result, [
|
69
|
+
Any,
|
70
|
+
Any,
|
71
|
+
Any,
|
72
|
+
[ Any,
|
73
|
+
[Any, Any, Any],
|
74
|
+
Any
|
75
|
+
],
|
76
|
+
])
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pattern_matching
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul Kwiatkowski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-15 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: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest-reporters
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.1'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: turn-again-reporter
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.1'
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.1.0
|
65
|
+
type: :development
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - "~>"
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '1.1'
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.1.0
|
75
|
+
description: Allows for pattern matching behavior, ala many functional languages,
|
76
|
+
using Ruby's case statements. Additionally, values can be bound at match time. Provides
|
77
|
+
a few helper modules to make this even more terse and feel more flexible.
|
78
|
+
email:
|
79
|
+
- paul@groupraise.com
|
80
|
+
executables: []
|
81
|
+
extensions: []
|
82
|
+
extra_rdoc_files: []
|
83
|
+
files:
|
84
|
+
- Gemfile
|
85
|
+
- Gemfile.lock
|
86
|
+
- LICENSE.md
|
87
|
+
- Rakefile
|
88
|
+
- lib/pattern_matching.rb
|
89
|
+
- lib/pattern_matching/bindings.rb
|
90
|
+
- lib/pattern_matching/bindings_set.rb
|
91
|
+
- lib/pattern_matching/case_equality_reversal.rb
|
92
|
+
- lib/pattern_matching/configuration.rb
|
93
|
+
- lib/pattern_matching/methods.rb
|
94
|
+
- lib/pattern_matching/methods_with_binding_helper.rb
|
95
|
+
- lib/pattern_matching/pattern_match.rb
|
96
|
+
- lib/pattern_matching/proc_helpers.rb
|
97
|
+
- lib/pattern_matching/version.rb
|
98
|
+
- test/configuration_test.rb
|
99
|
+
- test/configuration_tests/no_proc_helpers_test.rb
|
100
|
+
- test/configuration_tests/renamed_proc_helpers_test.rb
|
101
|
+
- test/proc_helper_test.rb
|
102
|
+
- test/simple_matching_test.rb
|
103
|
+
- test/test_helper.rb
|
104
|
+
- test/wildcard_test.rb
|
105
|
+
homepage: https://github.com/swifthand/pattern_matching
|
106
|
+
licenses:
|
107
|
+
- Revised BSD, see LICENSE.md
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.4.8
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Allows for pattern matching behavior with value bindings and wildcards.
|
129
|
+
test_files:
|
130
|
+
- test/configuration_test.rb
|
131
|
+
- test/configuration_tests/no_proc_helpers_test.rb
|
132
|
+
- test/configuration_tests/renamed_proc_helpers_test.rb
|
133
|
+
- test/proc_helper_test.rb
|
134
|
+
- test/simple_matching_test.rb
|
135
|
+
- test/test_helper.rb
|
136
|
+
- test/wildcard_test.rb
|