pattern_matching 0.1.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 +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
|