rfunc 0.0.1

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.
data/lib/rfunc/seq.rb ADDED
@@ -0,0 +1,181 @@
1
+ require "rfunc/option"
2
+
3
+ module RFunc
4
+ class Seq
5
+ extend Forwardable
6
+
7
+ def_delegators :@array, :[], :to_s, :to_h, :empty?, :last, :join, :count, :size, :each, :inspect, :all?, :to_a, :to_ary
8
+
9
+ def initialize(seq=[])
10
+ raise "RFunc::Seq must be initialized with an Array. #{seq.class} is not an Array" if seq.class != Array
11
+
12
+ @array = seq
13
+ end
14
+
15
+ def all
16
+ Seq.new(@array)
17
+ end
18
+
19
+ def <<(el)
20
+ Seq.new(@array << el)
21
+ end
22
+
23
+ def +(seq2)
24
+ Seq.new(@array + seq2)
25
+ end
26
+
27
+ def ==(object)
28
+ object.class == self.class && object.members == @array
29
+ end
30
+
31
+ def <=>(seq_or_array)
32
+ if seq_or_array.is_a?(Seq)
33
+ @array <=> seq_or_array.members
34
+ else
35
+ @array <=> seq_or_array
36
+ end
37
+ end
38
+
39
+ def members
40
+ @array
41
+ end
42
+
43
+ def head
44
+ raise "RFunc::Seq #{@array} has no head" if @array.size == 0
45
+ raw_head
46
+ end
47
+
48
+ alias_method :first, :head
49
+
50
+ def head_option
51
+ (h_e = raw_head) ? Some.new(h_e) : None.new
52
+ end
53
+
54
+ alias_method :first_option, :head_option
55
+
56
+ def tail_option
57
+ (tail[0]) ? Some.new(tail) : None.new
58
+ end
59
+
60
+ def tail
61
+ @tail ||= (t_s = @array[1..-1]) ? Seq.new(t_s) : Seq.new
62
+ end
63
+
64
+ def last_option
65
+ l = last
66
+ l ? Some.new(l) : None.new
67
+ end
68
+
69
+ def map(&block)
70
+ Seq.new(@array.map{|v| yield(v) })
71
+ end
72
+
73
+ def fold(accum, &block)
74
+ @array.inject(accum) {|a, el| yield(a, el) }
75
+ end
76
+
77
+ def foldr(accum, &block)
78
+ @array.reverse.inject(accum) {|a, el| yield(a, el) }
79
+ end
80
+
81
+ alias_method :foldl, :fold
82
+
83
+ def slice(from, to)
84
+ Seq.new(@array.slice(from, to))
85
+ end
86
+
87
+ def prepend(el)
88
+ Seq.new(@array.unshift(el))
89
+ end
90
+
91
+ def append(el)
92
+ Seq.new(@array.push(el))
93
+ end
94
+
95
+ def reverse
96
+ Seq.new(@array.reverse)
97
+ end
98
+
99
+ def filter(&block)
100
+ fold(Seq.new) {|accum, el|
101
+ if yield(el)
102
+ accum.append(el)
103
+ else
104
+ accum
105
+ end
106
+ }
107
+ end
108
+
109
+ def find(&block)
110
+ RFunc::Option.new(@array.find {|el| yield(el) })
111
+ end
112
+
113
+ def collect(&block)
114
+ fold(Seq.new([])) {|accum, el| (res = yield(el)) ? accum.append(res) : accum }
115
+ end
116
+
117
+ def collect_first(&block)
118
+ # more performant than it's prettier version
119
+ result = nil
120
+ find {|el| result = yield(el) }
121
+ result ? RFunc::Some.new(result) : RFunc::None.new
122
+ end
123
+
124
+ def concat(seq_or_array)
125
+ if seq_or_array.is_a?(Seq)
126
+ Seq.new(@array.concat(seq_or_array.members))
127
+ else
128
+ Seq.new(@array.concat(seq_or_array))
129
+ end
130
+ end
131
+
132
+ def flat_map(&block)
133
+ fold(Seq.new) {|accum, el|
134
+ accum.concat(yield(el))
135
+ }
136
+ end
137
+
138
+ def flatten
139
+ Seq.new(@array.flatten)
140
+ end
141
+
142
+ alias_method :for_all?, :all?
143
+
144
+ alias_method :for_each, :each
145
+
146
+ def slice(start_index, end_index=count)
147
+ Seq.new(@array.slice(start_index, end_index) || [])
148
+ end
149
+
150
+ def intersect(seq_or_array)
151
+ if seq_or_array.is_a?(Seq)
152
+ Seq.new(@array & seq_or_array.members)
153
+ else
154
+ Seq.new(@array & seq_or_array)
155
+ end
156
+ end
157
+
158
+ def take_while(&block)
159
+ Seq.new(@array.take_while {|r| yield(r) })
160
+ end
161
+
162
+ def take(n)
163
+ Seq.new(@array.take(n))
164
+ end
165
+
166
+ def sort_by(&block)
167
+ Seq.new(@array.sort_by {|obj| yield(obj) })
168
+ end
169
+
170
+ def sort(&block)
171
+ if block_given?
172
+ Seq.new(@array.sort {|x,y| yield(x,y) })
173
+ else
174
+ Seq.new(@array.sort)
175
+ end
176
+ end
177
+
178
+ private
179
+ def raw_head; @array[0] end
180
+ end
181
+ end
@@ -0,0 +1,3 @@
1
+ module RFunc
2
+ VERSION = "0.0.1"
3
+ end
data/rfunc.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rfunc/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rfunc"
8
+ spec.version = RFunc::VERSION
9
+ spec.authors = ["Paul De Goes"]
10
+ spec.email = ["pauldegoes@hotmail.com"]
11
+ spec.summary = %q{This gem provides a more functional collection library to Ruby}
12
+ spec.description = %q{nada}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "guard-rspec"
25
+ spec.add_development_dependency "yard"
26
+ end
@@ -0,0 +1,243 @@
1
+ require 'spec_helper'
2
+
3
+ describe RFunc::Option do
4
+ describe "#new" do
5
+ it "returns a Some when a value is supplied" do
6
+ RFunc::Option.new(1).should == RFunc::Some.new(1)
7
+ end
8
+
9
+ it "returns a None when a value is not supplied" do
10
+ RFunc::Option.new(nil).should == RFunc::None.new
11
+ end
12
+ end
13
+
14
+ describe "#map" do
15
+ context "when Option is Some" do
16
+ let(:option) { RFunc::Option.new(1) }
17
+ it "extracts the value from an Option and returns an Option" do
18
+ option.map {|v| v.should == 1; 2 }.should == RFunc::Some.new(2)
19
+ end
20
+ end
21
+ context "when Option is None" do
22
+ let(:option) { RFunc::Option.new(nil) }
23
+ it "doesn't extract the value and returns a None" do
24
+ option.map {|v| throw "this should not be executed" }.should == RFunc::None.new
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "#empty?" do
30
+ context "for an Option initialized with nil" do
31
+ it "returns true" do
32
+ RFunc::Option.new(nil).empty?.should be_true
33
+ end
34
+ end
35
+
36
+ context "for an None" do
37
+ it "returns true" do
38
+ RFunc::None.new.empty?.should be_true
39
+ end
40
+ end
41
+
42
+ context "for an Some" do
43
+ it "returns false" do
44
+ RFunc::Some.new(1).empty?.should be_false
45
+ end
46
+ end
47
+
48
+ context "for an Option initialized with a value" do
49
+ it "returns false" do
50
+ RFunc::Option.new(1).empty?.should be_false
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "#flat_map" do
56
+ context "when the option is a None" do
57
+ it "does not perform the operation" do
58
+ expect {
59
+ RFunc::None.new.flat_map{|v| throw 'should not throw anything' }
60
+ }.not_to raise_error
61
+ end
62
+ end
63
+ context "when the option is a Some and return type is not a Some of None" do
64
+ it "raises an error" do
65
+ expect {
66
+ RFunc::Some.new(1).flat_map{|v| v + v }
67
+ }.to raise_error(RFunc::Errors::InvalidReturnType)
68
+ end
69
+ end
70
+ context "when the option is a Some" do
71
+ it "returns the result of the yield" do
72
+ RFunc::Some.new(1).flat_map{|v| RFunc::Some.new(v + v) }.should == RFunc::Some.new(2)
73
+
74
+ RFunc::Some.new(1).flat_map{|v| RFunc::None.new }.should == RFunc::None.new
75
+ end
76
+ end
77
+
78
+ describe "#or_else" do
79
+ context "when the option is a Some" do
80
+ it "returns the option" do
81
+ RFunc::Some.new(1).or_else(RFunc::Some.new(2)).get.should == 1
82
+ end
83
+ end
84
+ context "when the option is a None" do
85
+ context "when the return type of the supplied alternative is valid" do
86
+ it "returns the option" do
87
+ RFunc::None.new.or_else(RFunc::Some.new(2)).get.should == 2
88
+ end
89
+ end
90
+ context "when the return type of the supplied alternative is invalid" do
91
+ it "raises an exception" do
92
+ expect {
93
+ RFunc::None.new.or_else(2).get.should == 2
94
+ }.to raise_error(RFunc::Errors::InvalidReturnType)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ describe "#filter" do
102
+ context "when filter matches" do
103
+ it "returns a Some" do
104
+ RFunc::Some.new(1).filter {|el| el == 1}.should == RFunc::Some.new(1)
105
+ end
106
+ end
107
+ context "when filter does not match" do
108
+ it "returns a None" do
109
+ RFunc::Some.new(1).filter {|el| el == 2}.should == RFunc::None.new
110
+ end
111
+ end
112
+ context "when None is supplied" do
113
+ it "returns a None" do
114
+ RFunc::None.new.filter {|el| el == "foo" }.should == RFunc::None.new
115
+ end
116
+ end
117
+ end
118
+
119
+ describe "#filter_not" do
120
+ context "when filter matches" do
121
+ it "returns a None" do
122
+ RFunc::Some.new(1).filter_not {|el| el == 1}.should == RFunc::None.new
123
+ end
124
+ end
125
+ context "when filter does not match" do
126
+ it "returns a Some" do
127
+ RFunc::Some.new(1).filter_not {|el| el == 2}.should == RFunc::Some.new(1)
128
+ end
129
+ end
130
+ context "when None is supplied" do
131
+ it "returns a None" do
132
+ RFunc::None.new.filter_not {|el| el == "foo" }.should == RFunc::None.new
133
+ end
134
+ end
135
+ end
136
+
137
+ describe "#fold" do
138
+ context "when the option is a None" do
139
+ it "returns the alternate" do
140
+ RFunc::None.new.fold(2) {|el|
141
+ el * el
142
+ }.should == 2
143
+ end
144
+ end
145
+ context "when the option is a Some" do
146
+ it "returns the alternate" do
147
+ RFunc::Some.new(10).fold(2) {|el|
148
+ el * el
149
+ }.should == 100
150
+ end
151
+ end
152
+ end
153
+
154
+ describe "#flatten" do
155
+ context "when the option is a None" do
156
+ it "returns a None" do
157
+ RFunc::None.new.flatten.should == RFunc::None.new
158
+ end
159
+ end
160
+ context "when the option is a Some[Some]" do
161
+ it "returns a flattened Some" do
162
+ RFunc::Some.new(RFunc::Some.new(1)).flatten.should == RFunc::Some.new(1)
163
+ end
164
+ end
165
+ context "when the option is a Some[None]" do
166
+ it "returns a None" do
167
+ RFunc::Some.new(RFunc::None.new).flatten.should == RFunc::None.new
168
+ end
169
+ end
170
+ end
171
+
172
+ describe "#collect" do
173
+ context "when the option is a None" do
174
+ it "returns a None" do
175
+ RFunc::None.new.collect {|el|
176
+ case el > 2
177
+ when true; el * 100
178
+ else nil
179
+ end
180
+ }.should be_a(RFunc::None)
181
+ end
182
+ end
183
+ context "when the option is a Some with on which the provided block returns nil" do
184
+ it "returns a None" do
185
+ RFunc::Some.new(2).collect {|el|
186
+ case el > 2
187
+ when true; el * 100
188
+ else nil
189
+ end
190
+ }.should be_a(RFunc::None)
191
+ end
192
+ end
193
+ context "when the option is a Some with on which the provided block returns a value" do
194
+ it "returns a None" do
195
+ RFunc::Some.new(3).collect {|el|
196
+ case el > 2
197
+ when true; el * 100
198
+ else 100
199
+ end
200
+ }.should == RFunc::Some.new(300)
201
+ end
202
+ end
203
+ end
204
+
205
+ describe "#for_all" do
206
+ context "when the option is a None" do
207
+ it "returns true" do
208
+ RFunc::None.new.for_all {|el| el == 2}.should == true
209
+ end
210
+ end
211
+ context "when the option is a Some and the result of the provided block is true" do
212
+ it "returns true" do
213
+ RFunc::Some.new(2).for_all {|el| el == 2}.should == true
214
+ end
215
+ end
216
+ context "when the option is a Some and the result of the provided block is false" do
217
+ it "returns false" do
218
+ RFunc::Some.new(1).for_all {|el| el == 2}.should == false
219
+ end
220
+ end
221
+ end
222
+
223
+ describe "#for_each" do
224
+ context "when the option is a None" do
225
+ it "does not execute the yield" do
226
+ count = 0
227
+
228
+ RFunc::None.new.for_each {|el| count = 10 }
229
+
230
+ count.should == 0
231
+ end
232
+ end
233
+ context "when the option is a Some" do
234
+ it "not execute the yield with the Option value" do
235
+ count = 0
236
+
237
+ RFunc::Some.new(10).for_each {|el| count = el }
238
+
239
+ count.should == 10
240
+ end
241
+ end
242
+ end
243
+ end