pattern-proc 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d5feb7784773d21a7a6cfd9004f771d8626d9c5
4
- data.tar.gz: 9378aa295eb70fed4f8f3421bd8adfc22a611e0d
3
+ metadata.gz: adc2ed80e8a4406782666406fd1cda3117de27ad
4
+ data.tar.gz: 14792f45f789de53c59a60cf429d1d8e86151645
5
5
  SHA512:
6
- metadata.gz: 2eb70413d2a4614ff8f9daa48071600d82a139f03fad5652b522280ed727451bb08bb2932ebe65bb1eea4d866eaf14458e4668cd56b338fa41b2427c8d997678
7
- data.tar.gz: 311757068e3e8eaf2bd414e244bf55101f548118f2403f2085d1b1178e4260b727694f130aaccee6bdba11ede13e2805a282ea60bc1f8bab67a2a992023963db
6
+ metadata.gz: f18e19c8a4ad26628d01f35179f94aad549d9ea350c15fb6880524e0406a15758e1c52de4baf5dfb9379ba766c95761b755b692c8957cac33433e95444667c05
7
+ data.tar.gz: 4fcdedf9e878868403999bdd127d216d60b4030b90a3a5cb8f4b65f6a1b409de132a3a39b59ec5529f375e4e8d268fbdbdce3dc8e9bd04b477f3468027e400a5
data/Gemfile CHANGED
@@ -2,4 +2,6 @@ source "https://rubygems.org"
2
2
 
3
3
  group :test do
4
4
  gem "rspec"
5
+ gem "pry"
6
+ gem "metaclass"
5
7
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,14 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
+ coderay (1.1.0)
4
5
  diff-lcs (1.2.5)
6
+ metaclass (0.0.4)
7
+ method_source (0.8.2)
8
+ pry (0.9.12.6)
9
+ coderay (~> 1.0)
10
+ method_source (~> 0.8)
11
+ slop (~> 3.4)
5
12
  rspec (3.0.0)
6
13
  rspec-core (~> 3.0.0)
7
14
  rspec-expectations (~> 3.0.0)
@@ -14,9 +21,12 @@ GEM
14
21
  rspec-mocks (3.0.2)
15
22
  rspec-support (~> 3.0.0)
16
23
  rspec-support (3.0.2)
24
+ slop (3.5.0)
17
25
 
18
26
  PLATFORMS
19
27
  ruby
20
28
 
21
29
  DEPENDENCIES
30
+ metaclass
31
+ pry
22
32
  rspec
data/lib/pattern_proc.rb CHANGED
@@ -1,146 +1,167 @@
1
- class PatternProc
1
+ require 'metaclass'
2
2
 
3
- def initialize(cases = [])
4
- @arity = -1
5
- @cases = []
6
- cases.each do |c| add_case c end
3
+ module PatternProc
4
+ def self.included(klass)
5
+ klass.extend ClassMethods
6
+ Object.include ObjectMethods
7
7
  end
8
8
 
9
- def with(*args, &block)
10
- case_obj = PatternProcCase.new(args, &block)
11
- add_case case_obj
12
- case_obj
13
- end
9
+ class PatternProc
14
10
 
15
- def to_proc
16
- ->(*args) { call(args) }
17
- end
11
+ def initialize(cases = [])
12
+ @arity = -1
13
+ @cases = []
14
+ cases.each do |c| add_case c end
15
+ end
18
16
 
19
- private
20
- def add_case(case_obj)
21
- if @arity == -1
22
- @arity = case_obj.arity
23
- elsif @arity != case_obj.arity
24
- raise "mismatched arity"
17
+ def with(*args, &block)
18
+ case_obj = PatternProcCase.new(args, &block)
19
+ add_case case_obj
20
+ case_obj
25
21
  end
26
- @cases << case_obj
27
- end
28
22
 
23
+ def to_proc
24
+ ->(*args) { call(args) }
25
+ end
29
26
 
30
- def call(args)
31
- if args.size == @arity
32
- best_match = find_matches(args, true).first
33
- if best_match
34
- best_match.to_proc.call(args)
35
- else
36
- raise "no match"
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"
37
33
  end
38
- elsif args.size < @arity
39
- sliced_cases = find_matches(args, false)
40
- if sliced_cases.size > 0
41
- PatternProc.new(sliced_cases).to_proc
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
42
53
  else
43
- raise "no match"
54
+ raise "too many args"
44
55
  end
45
- else
46
- raise "too many args"
47
56
  end
48
- end
49
57
 
50
- def find_matches(args, stop_at_first_match)
51
- results = []
52
- sorted_cases = @cases.sort do |case_obj_1, case_obj_2|
53
- case_obj_1.specificity <=> case_obj_2.specificity
54
- end.reverse
55
- sorted_cases.each do |case_obj|
56
- result = case_obj.make_subcase(args)
57
- if result
58
- results << result
59
- if stop_at_first_match
60
- break
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
61
70
  end
62
71
  end
72
+ results
63
73
  end
64
- results
65
74
  end
66
- end
67
75
 
68
- class PatternProcCase
76
+ class PatternProcCase
69
77
 
70
- attr_reader :proc_applied_level
71
- attr_reader :proc_arity
78
+ attr_reader :proc_applied_level
79
+ attr_reader :proc_arity
72
80
 
73
- def initialize(args, previous_specificity = 0, proc_arity = 0, &block)
74
- @expected_args = args
75
- @proc = block
76
- @proc_arity = proc_arity
77
- @previous_specificity = previous_specificity || 0
78
- if !block.nil? && block.arity > 0
79
- @proc_arity = block.arity
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
80
89
  end
81
- end
82
90
 
83
- def returns(value)
84
- @return_value = value
85
- self
86
- end
91
+ def returns(value)
92
+ @return_value = value
93
+ self
94
+ end
87
95
 
88
- # private ish
89
- def expected_arity
90
- @expected_args.size || 0
91
- end
96
+ # private ish
97
+ def expected_arity
98
+ @expected_args.size || 0
99
+ end
92
100
 
93
- def arity
94
- expected_arity + proc_arity
95
- end
101
+ def arity
102
+ expected_arity + proc_arity
103
+ end
96
104
 
97
- def specificity
98
- expected_arity + @previous_specificity
99
- end
105
+ def specificity
106
+ expected_arity + @previous_specificity
107
+ end
100
108
 
101
- def make_subcase(args)
102
- match_len = [args.size, @expected_args.size].min
103
- expected = @expected_args.take(match_len)
104
- actual = args.take(match_len)
105
- if expected == actual
106
- new_proc = to_proc.curry
107
- curry_count = args.size - @expected_args.size
108
- new_arity = proc_arity
109
- if curry_count > 0
110
- send_args = args.drop(match_len).take(curry_count)
111
- new_proc = new_proc.call(*send_args)
112
- new_arity -= curry_count
113
- if new_arity == 0
114
- return PatternProcCase.new([], specificity).returns(new_proc)
115
- elsif !(new_proc.is_a?(Proc))
116
- raise "uh oh"
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
117
126
  end
127
+ PatternProcCase.new(@expected_args.drop(match_len), specificity, new_arity, &new_proc).returns(@return_value)
128
+ else
129
+ nil
118
130
  end
119
- PatternProcCase.new(@expected_args.drop(match_len), specificity, new_arity, &new_proc).returns(@return_value)
120
- else
121
- nil
122
131
  end
123
- end
124
132
 
125
- def to_proc
126
- @proc || ->(*args) { @return_value }
133
+ def to_proc
134
+ @proc || ->(*args) { @return_value }
135
+ end
127
136
  end
128
- end
129
137
 
130
- class Object
131
- def partials(method)
132
- @__partials ||= {}
133
- unless @__partials[method]
134
- proc_obj = PatternProc.new
135
- metaclass = class << self; self; end
136
- metaclass.class_eval do
137
- define_method(method) do |*args|
138
- curried_method = proc_obj.to_proc.curry
139
- curried_method[*args]
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
140
154
  end
155
+ @__pattern[method] = proc_obj
141
156
  end
142
- @__partials[method] = proc_obj
157
+ @__pattern[method]
158
+ end
159
+
160
+ end
161
+
162
+ module ClassMethods
163
+ def pattern(method)
164
+ find_or_create_pattern self, method
143
165
  end
144
- @__partials[method]
145
166
  end
146
167
  end
data/pattern-proc.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'pattern-proc'
3
- s.version = '0.0.3'
3
+ s.version = '0.0.4'
4
4
  s.date = '2014-07-01'
5
5
  s.summary = 'Pattern Proc'
6
6
  s.description = 'define a Proc with pattern matching "re-declaration" ala Haskell'
@@ -0,0 +1,80 @@
1
+ require 'pattern_proc'
2
+
3
+ class TestObjectClass
4
+ include PatternProc
5
+ def my_func(a,b,c)
6
+ raise "don't call me"
7
+ end
8
+
9
+ pattern(:p_func).with(3,2).returns(5)
10
+ pattern(:p_func).with do |x,y| x * y end
11
+ end
12
+
13
+ class TestSingletonClass
14
+ class << self
15
+ include PatternProc
16
+ def class_func(a,b,c)
17
+ raise "don't call me"
18
+ end
19
+
20
+ pattern(:p_cls_func).with(3) do |y| y * y * y end
21
+ pattern(:p_cls_func).with do |x, y| x + y end
22
+ end
23
+ end
24
+
25
+ describe "pattern object methods" do
26
+ context "class" do
27
+ let (:obj) { TestObjectClass.new }
28
+ context "in declaration" do
29
+ it "allows declaration of pattern methods in a class" do
30
+ obj.p_func(3,2).should == 5
31
+ obj.p_func(3,3).should == 9
32
+ end
33
+ end
34
+ context "after the fact" do
35
+ it "allows replacing methods on an instance" do
36
+ obj.pattern(:my_func).with(1,2,3).returns(7)
37
+ obj.pattern(:my_func).with(2,3,4).returns(9)
38
+
39
+ obj.my_func(1,2).(3).should == 7
40
+ obj.my_func(2).(3,4).should == 9
41
+ end
42
+
43
+ it "allows declaring new methods on an instance" do
44
+ obj.pattern(:new_func).with(:a, :b).returns(:c)
45
+ obj.pattern(:new_func).with(:d, :e).returns(:f)
46
+
47
+ obj.new_func(:a).(:b).should == :c
48
+ obj.new_func(:d,:e).should == :f
49
+ end
50
+ end
51
+ end
52
+
53
+ context "object" do
54
+ let (:klass) { TestSingletonClass }
55
+ context "in declaration" do
56
+ it "allows declaration of pattern class methods in a class" do
57
+ klass.p_cls_func(3,2).should == 8
58
+ klass.p_cls_func(9,3).should == 12
59
+ end
60
+ end
61
+
62
+ context "after the fact" do
63
+ it "allows replacing methods on a class" do
64
+ klass.pattern(:class_func).with(1,2,3).returns(7)
65
+ klass.pattern(:class_func).with(2,3,4).returns(9)
66
+
67
+ klass.class_func(1,2).(3).should == 7
68
+ klass.class_func(2).(3,4).should == 9
69
+ end
70
+
71
+ it "allows declaring new methods on an instance" do
72
+ klass.pattern(:new_func).with(:a, :b).returns(:c)
73
+ klass.pattern(:new_func).with(:d, :e).returns(:f)
74
+
75
+ klass.new_func(:a).(:b).should == :c
76
+ klass.new_func(:d,:e).should == :f
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,6 +1,6 @@
1
1
  require 'pattern_proc'
2
2
 
3
- describe PatternProc do
3
+ describe PatternProc::PatternProc do
4
4
  subject { described_class.new }
5
5
 
6
6
  it "simple" do
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.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Pleasant-Ryan
@@ -23,6 +23,7 @@ files:
23
23
  - README.md
24
24
  - lib/pattern_proc.rb
25
25
  - pattern-proc.gemspec
26
+ - spec/lib/object_method_spec.rb
26
27
  - spec/lib/pattern_proc_spec.rb
27
28
  homepage: http://rubygems.org/gems/pattern-proc
28
29
  licenses: