pattern-proc 0.0.4 → 0.0.6
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/lib/pattern-proc/class_methods.rb +14 -0
- data/lib/pattern-proc/object_methods.rb +5 -0
- data/lib/pattern-proc/pattern_proc.rb +70 -0
- data/lib/pattern-proc/pattern_proc_case.rb +63 -0
- data/lib/pattern-proc/pattern_proc_patterns.rb +21 -0
- data/lib/pattern-proc/test_methods.rb +10 -0
- data/lib/pattern_proc.rb +5 -166
- data/pattern-proc.gemspec +1 -1
- data/spec/lib/object_method_spec.rb +24 -4
- metadata +7 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e6e5c92ff2acd52f48ad6a3499622f6b6605ce3
|
4
|
+
data.tar.gz: 48d17c98cf20548f02d4fb5a317c412235c22c3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 331e688e706805d3e03687107141f45b2652f8c0e568c02c79bdd3de0de7e7c0c78b4764708da4968d8215cdb288563f5a5d6ab13438e0315f82b3d59ce6bb4e
|
7
|
+
data.tar.gz: acbdae026983fe1db56d77a1b88fd0aeca837980eb9637a5ccf421dcf03f2c1cf47b4ca26b92804959909838da7018f34bb60162f2fce614e3166d28da40aa4e
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module PatternProc
|
2
|
+
module ClassMethods
|
3
|
+
def self.included(klass)
|
4
|
+
klass.extend JustClassMethods
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module JustClassMethods
|
9
|
+
def pattern(method)
|
10
|
+
@__patterns ||= {}
|
11
|
+
PatternProcPatterns.find_or_create_pattern @__patterns, self, method
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'pattern-proc/pattern_proc_case'
|
2
|
+
|
3
|
+
module PatternProc
|
4
|
+
class PatternProc
|
5
|
+
|
6
|
+
def initialize(cases = [])
|
7
|
+
@arity = -1
|
8
|
+
@cases = []
|
9
|
+
cases.each do |c| add_case c end
|
10
|
+
end
|
11
|
+
|
12
|
+
def with(*args, &block)
|
13
|
+
case_obj = PatternProcCase.new(args, &block)
|
14
|
+
add_case case_obj
|
15
|
+
case_obj
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_proc
|
19
|
+
->(*args) { call(args) }
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def add_case(case_obj)
|
24
|
+
if @arity == -1
|
25
|
+
@arity = case_obj.arity
|
26
|
+
elsif @arity != case_obj.arity
|
27
|
+
raise "mismatched arity"
|
28
|
+
end
|
29
|
+
@cases << case_obj
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def call(args)
|
34
|
+
if args.size == @arity
|
35
|
+
best_match = find_matches(args, true).first
|
36
|
+
if best_match
|
37
|
+
best_match.to_proc.call(args)
|
38
|
+
else
|
39
|
+
raise "no match"
|
40
|
+
end
|
41
|
+
elsif args.size < @arity
|
42
|
+
sliced_cases = find_matches(args, false)
|
43
|
+
if sliced_cases.size > 0
|
44
|
+
PatternProc.new(sliced_cases).to_proc
|
45
|
+
else
|
46
|
+
raise "no match"
|
47
|
+
end
|
48
|
+
else
|
49
|
+
raise "too many args"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_matches(args, stop_at_first_match)
|
54
|
+
results = []
|
55
|
+
sorted_cases = @cases.sort do |case_obj_1, case_obj_2|
|
56
|
+
case_obj_1.specificity <=> case_obj_2.specificity
|
57
|
+
end.reverse
|
58
|
+
sorted_cases.each do |case_obj|
|
59
|
+
result = case_obj.make_subcase(args)
|
60
|
+
if result
|
61
|
+
results << result
|
62
|
+
if stop_at_first_match
|
63
|
+
break
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
results
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module PatternProc
|
2
|
+
class PatternProcCase
|
3
|
+
|
4
|
+
attr_reader :proc_applied_level
|
5
|
+
attr_reader :proc_arity
|
6
|
+
|
7
|
+
def initialize(args, previous_specificity = 0, proc_arity = 0, &block)
|
8
|
+
@expected_args = args
|
9
|
+
@proc = block
|
10
|
+
@proc_arity = proc_arity
|
11
|
+
@previous_specificity = previous_specificity || 0
|
12
|
+
if !block.nil? && block.arity > 0
|
13
|
+
@proc_arity = block.arity
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def returns(value)
|
18
|
+
@return_value = value
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
# private ish
|
23
|
+
def expected_arity
|
24
|
+
@expected_args.size || 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def arity
|
28
|
+
expected_arity + proc_arity
|
29
|
+
end
|
30
|
+
|
31
|
+
def specificity
|
32
|
+
expected_arity + @previous_specificity
|
33
|
+
end
|
34
|
+
|
35
|
+
def make_subcase(args)
|
36
|
+
match_len = [args.size, @expected_args.size].min
|
37
|
+
expected = @expected_args.take(match_len)
|
38
|
+
actual = args.take(match_len)
|
39
|
+
if expected == actual
|
40
|
+
new_proc = to_proc.curry
|
41
|
+
curry_count = args.size - @expected_args.size
|
42
|
+
new_arity = proc_arity
|
43
|
+
if curry_count > 0
|
44
|
+
send_args = args.drop(match_len).take(curry_count)
|
45
|
+
new_proc = new_proc.call(*send_args)
|
46
|
+
new_arity -= curry_count
|
47
|
+
if new_arity == 0
|
48
|
+
return PatternProcCase.new([], specificity).returns(new_proc)
|
49
|
+
elsif !(new_proc.is_a?(Proc))
|
50
|
+
raise "uh oh"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
PatternProcCase.new(@expected_args.drop(match_len), specificity, new_arity, &new_proc).returns(@return_value)
|
54
|
+
else
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_proc
|
60
|
+
@proc || ->(*args) { @return_value }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'pattern-proc/pattern_proc'
|
2
|
+
|
3
|
+
module PatternProc
|
4
|
+
class PatternProcPatterns
|
5
|
+
class << self
|
6
|
+
def find_or_create_pattern(patterns, receiver, method)
|
7
|
+
unless patterns[method]
|
8
|
+
proc_obj = PatternProc.new
|
9
|
+
receiver.class_eval do
|
10
|
+
define_method(method) do |*args|
|
11
|
+
curried_method = proc_obj.to_proc.curry
|
12
|
+
curried_method[*args]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
patterns[method] = proc_obj
|
16
|
+
end
|
17
|
+
patterns[method]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/pattern_proc.rb
CHANGED
@@ -1,167 +1,6 @@
|
|
1
|
-
require '
|
1
|
+
require 'pattern-proc/pattern_proc'
|
2
|
+
require 'pattern-proc/pattern_proc_patterns'
|
3
|
+
require 'pattern-proc/object_methods'
|
4
|
+
require 'pattern-proc/class_methods'
|
5
|
+
require 'pattern-proc/test_methods'
|
2
6
|
|
3
|
-
module PatternProc
|
4
|
-
def self.included(klass)
|
5
|
-
klass.extend ClassMethods
|
6
|
-
Object.include ObjectMethods
|
7
|
-
end
|
8
|
-
|
9
|
-
class PatternProc
|
10
|
-
|
11
|
-
def initialize(cases = [])
|
12
|
-
@arity = -1
|
13
|
-
@cases = []
|
14
|
-
cases.each do |c| add_case c end
|
15
|
-
end
|
16
|
-
|
17
|
-
def with(*args, &block)
|
18
|
-
case_obj = PatternProcCase.new(args, &block)
|
19
|
-
add_case case_obj
|
20
|
-
case_obj
|
21
|
-
end
|
22
|
-
|
23
|
-
def to_proc
|
24
|
-
->(*args) { call(args) }
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
def add_case(case_obj)
|
29
|
-
if @arity == -1
|
30
|
-
@arity = case_obj.arity
|
31
|
-
elsif @arity != case_obj.arity
|
32
|
-
raise "mismatched arity"
|
33
|
-
end
|
34
|
-
@cases << case_obj
|
35
|
-
end
|
36
|
-
|
37
|
-
|
38
|
-
def call(args)
|
39
|
-
if args.size == @arity
|
40
|
-
best_match = find_matches(args, true).first
|
41
|
-
if best_match
|
42
|
-
best_match.to_proc.call(args)
|
43
|
-
else
|
44
|
-
raise "no match"
|
45
|
-
end
|
46
|
-
elsif args.size < @arity
|
47
|
-
sliced_cases = find_matches(args, false)
|
48
|
-
if sliced_cases.size > 0
|
49
|
-
PatternProc.new(sliced_cases).to_proc
|
50
|
-
else
|
51
|
-
raise "no match"
|
52
|
-
end
|
53
|
-
else
|
54
|
-
raise "too many args"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def find_matches(args, stop_at_first_match)
|
59
|
-
results = []
|
60
|
-
sorted_cases = @cases.sort do |case_obj_1, case_obj_2|
|
61
|
-
case_obj_1.specificity <=> case_obj_2.specificity
|
62
|
-
end.reverse
|
63
|
-
sorted_cases.each do |case_obj|
|
64
|
-
result = case_obj.make_subcase(args)
|
65
|
-
if result
|
66
|
-
results << result
|
67
|
-
if stop_at_first_match
|
68
|
-
break
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
results
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
class PatternProcCase
|
77
|
-
|
78
|
-
attr_reader :proc_applied_level
|
79
|
-
attr_reader :proc_arity
|
80
|
-
|
81
|
-
def initialize(args, previous_specificity = 0, proc_arity = 0, &block)
|
82
|
-
@expected_args = args
|
83
|
-
@proc = block
|
84
|
-
@proc_arity = proc_arity
|
85
|
-
@previous_specificity = previous_specificity || 0
|
86
|
-
if !block.nil? && block.arity > 0
|
87
|
-
@proc_arity = block.arity
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def returns(value)
|
92
|
-
@return_value = value
|
93
|
-
self
|
94
|
-
end
|
95
|
-
|
96
|
-
# private ish
|
97
|
-
def expected_arity
|
98
|
-
@expected_args.size || 0
|
99
|
-
end
|
100
|
-
|
101
|
-
def arity
|
102
|
-
expected_arity + proc_arity
|
103
|
-
end
|
104
|
-
|
105
|
-
def specificity
|
106
|
-
expected_arity + @previous_specificity
|
107
|
-
end
|
108
|
-
|
109
|
-
def make_subcase(args)
|
110
|
-
match_len = [args.size, @expected_args.size].min
|
111
|
-
expected = @expected_args.take(match_len)
|
112
|
-
actual = args.take(match_len)
|
113
|
-
if expected == actual
|
114
|
-
new_proc = to_proc.curry
|
115
|
-
curry_count = args.size - @expected_args.size
|
116
|
-
new_arity = proc_arity
|
117
|
-
if curry_count > 0
|
118
|
-
send_args = args.drop(match_len).take(curry_count)
|
119
|
-
new_proc = new_proc.call(*send_args)
|
120
|
-
new_arity -= curry_count
|
121
|
-
if new_arity == 0
|
122
|
-
return PatternProcCase.new([], specificity).returns(new_proc)
|
123
|
-
elsif !(new_proc.is_a?(Proc))
|
124
|
-
raise "uh oh"
|
125
|
-
end
|
126
|
-
end
|
127
|
-
PatternProcCase.new(@expected_args.drop(match_len), specificity, new_arity, &new_proc).returns(@return_value)
|
128
|
-
else
|
129
|
-
nil
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def to_proc
|
134
|
-
@proc || ->(*args) { @return_value }
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
module ObjectMethods
|
139
|
-
|
140
|
-
def pattern(method)
|
141
|
-
find_or_create_pattern self.__metaclass__, method
|
142
|
-
end
|
143
|
-
|
144
|
-
private
|
145
|
-
def find_or_create_pattern(receiver, method)
|
146
|
-
@__pattern ||= {}
|
147
|
-
unless @__pattern[method]
|
148
|
-
proc_obj = PatternProc.new
|
149
|
-
receiver.class_eval do
|
150
|
-
define_method(method) do |*args|
|
151
|
-
curried_method = proc_obj.to_proc.curry
|
152
|
-
curried_method[*args]
|
153
|
-
end
|
154
|
-
end
|
155
|
-
@__pattern[method] = proc_obj
|
156
|
-
end
|
157
|
-
@__pattern[method]
|
158
|
-
end
|
159
|
-
|
160
|
-
end
|
161
|
-
|
162
|
-
module ClassMethods
|
163
|
-
def pattern(method)
|
164
|
-
find_or_create_pattern self, method
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
data/pattern-proc.gemspec
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'pattern_proc'
|
2
|
+
include PatternProc::TestMethods
|
2
3
|
|
3
4
|
class TestObjectClass
|
4
|
-
include PatternProc
|
5
|
+
include PatternProc::ClassMethods
|
5
6
|
def my_func(a,b,c)
|
6
7
|
raise "don't call me"
|
7
8
|
end
|
@@ -12,7 +13,7 @@ end
|
|
12
13
|
|
13
14
|
class TestSingletonClass
|
14
15
|
class << self
|
15
|
-
include PatternProc
|
16
|
+
include PatternProc::ClassMethods
|
16
17
|
def class_func(a,b,c)
|
17
18
|
raise "don't call me"
|
18
19
|
end
|
@@ -22,8 +23,16 @@ class TestSingletonClass
|
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
26
|
+
class TestSingletonNoInclude
|
27
|
+
class << self
|
28
|
+
def a_func(a,b,c)
|
29
|
+
raise "don't call me"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
25
34
|
describe "pattern object methods" do
|
26
|
-
context "
|
35
|
+
context "object" do
|
27
36
|
let (:obj) { TestObjectClass.new }
|
28
37
|
context "in declaration" do
|
29
38
|
it "allows declaration of pattern methods in a class" do
|
@@ -50,7 +59,7 @@ describe "pattern object methods" do
|
|
50
59
|
end
|
51
60
|
end
|
52
61
|
|
53
|
-
context "
|
62
|
+
context "class" do
|
54
63
|
let (:klass) { TestSingletonClass }
|
55
64
|
context "in declaration" do
|
56
65
|
it "allows declaration of pattern class methods in a class" do
|
@@ -77,4 +86,15 @@ describe "pattern object methods" do
|
|
77
86
|
end
|
78
87
|
end
|
79
88
|
end
|
89
|
+
|
90
|
+
context "for testing" do
|
91
|
+
let (:klass) { TestSingletonNoInclude }
|
92
|
+
it "allows replacing methods on a class" do
|
93
|
+
klass.pattern(:a_func).with(1,2,3).returns(7)
|
94
|
+
klass.pattern(:a_func).with(2,3,4).returns(9)
|
95
|
+
|
96
|
+
klass.a_func(1,2).(3).should == 7
|
97
|
+
klass.a_func(2).(3,4).should == 9
|
98
|
+
end
|
99
|
+
end
|
80
100
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pattern-proc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Pleasant-Ryan
|
@@ -21,6 +21,12 @@ files:
|
|
21
21
|
- Gemfile.lock
|
22
22
|
- LICENSE
|
23
23
|
- README.md
|
24
|
+
- lib/pattern-proc/class_methods.rb
|
25
|
+
- lib/pattern-proc/object_methods.rb
|
26
|
+
- lib/pattern-proc/pattern_proc.rb
|
27
|
+
- lib/pattern-proc/pattern_proc_case.rb
|
28
|
+
- lib/pattern-proc/pattern_proc_patterns.rb
|
29
|
+
- lib/pattern-proc/test_methods.rb
|
24
30
|
- lib/pattern_proc.rb
|
25
31
|
- pattern-proc.gemspec
|
26
32
|
- spec/lib/object_method_spec.rb
|