totally_lazy 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2bd08beb128d57daef7cb8259b9dd391d77d3f6
4
- data.tar.gz: 0436a18ccd3dc6f192af42e80fbfd8467e0c068b
3
+ metadata.gz: 580d257ae0a5c6db629420f25fc435870cbd09d7
4
+ data.tar.gz: 428a631231457eb51abd105a07a91b8ad5517b26
5
5
  SHA512:
6
- metadata.gz: 562656c8ed06737fedb4dd348d788e484786873855c0ecd5fb951c651ed2383a90a738c2cd64f444155589b07df9c08881964e0fafbaeebdffd11d4200d47bce
7
- data.tar.gz: 3c77e9a13c67a68b76797583a83179259ffca73e5a754902b31e99c51c8c2874711d07a3943836669dea4e7025d2147090e0f69672dc3f46d8589850f339ad4b
6
+ metadata.gz: 831ab147b15383e7fc2ff86c4d85636cc3a93ae14c64efa2b0a4a2cf06c0cb89ff6f43ffe747273252f595213f4c0e13c5943646e4b2e786c2095272a428de4d
7
+ data.tar.gz: aeff00bdcac329c9554adca869f9c022d7c9e11eef72958ae61e434a0a590dcff45b14a9aad03ce0af3ed15d685b2718c0ce10b992420e9886ed747a6e0bc6d5
data/Gemfile CHANGED
@@ -6,4 +6,5 @@ group :development do
6
6
  gem 'bundler', '~> 1.0'
7
7
  gem 'jeweler', '~> 2.0.1'
8
8
  gem 'rspec_html_formatter', '~> 0.3.0'
9
+ gem 'rake', '~> 10.3.2'
9
10
  end
data/README.md CHANGED
@@ -13,7 +13,7 @@ This is a port of the java functional library [Totally Lazy](https://code.google
13
13
  In your bundler Gemfile
14
14
 
15
15
  ```ruby
16
- gem totally_lazy, '~>0.0.2'
16
+ gem totally_lazy, '~>0.0.3'
17
17
  ```
18
18
 
19
19
  Or with rubygems
@@ -27,8 +27,10 @@ Or with rubygems
27
27
  The following are some simple examples of the currently implemented functionality.
28
28
 
29
29
  ```ruby
30
- sequence(1,2,3,4).filter{|i| i.even? } # lazily returns 2,4
31
- sequence(1,2).map{|i| i.to_s} # lazily returns "1","2"
30
+ require 'totally_lazy'
31
+
32
+ sequence(1,2,3,4).filter(even) # lazily returns 2,4
33
+ sequence(1,2).map(to_string) # lazily returns "1","2"
32
34
  sequence(1,2,3).take(2) # lazily returns 1,2
33
35
  sequence(1,2,3).drop(2) # lazily returns 3
34
36
  sequence(1,2,3).tail # lazily returns 2,3
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
data/lib/functor.rb ADDED
@@ -0,0 +1,92 @@
1
+ # Functor is Ruby's implementation of a Higher-Order-Message. Essentally,
2
+ # a Functor can vary its behavior accorrding to the operation applied to it.
3
+ #
4
+ # Example
5
+ #
6
+ # f = Functor.new { |op, x| x.send(op, x) }
7
+ # (f + 1) #=> 2
8
+ # (f + 2) #=> 4
9
+ # (f + 3) #=> 6
10
+ # (f * 1) #=> 1
11
+ # (f * 2) #=> 4
12
+ # (f * 3) #=> 9
13
+ #
14
+ class Functor #< BasicObject
15
+
16
+ # Functors can be somewhat inefficient if a new Functor
17
+ # is frequently recreated for the same use. So this cache
18
+ # can be used to speed things up.
19
+ #
20
+ # The +key+ will always be an array, wich makes it easier
21
+ # to cache Functor for multiple factors.
22
+ #
23
+ def self.cache(*key, &function)
24
+ @cache ||= {}
25
+ if function
26
+ @cache[key] = new(&function)
27
+ else
28
+ @cache[key]
29
+ end
30
+ end
31
+
32
+ EXCEPTIONS = [:binding, :inspect, :object_id]
33
+ if defined?(::BasicObject)
34
+ EXCEPTIONS.concat(::BasicObject.instance_methods)
35
+ EXCEPTIONS.uniq!
36
+ EXCEPTIONS.map!{ |m| m.to_sym }
37
+ end
38
+
39
+ #
40
+ alias :__class__ :class
41
+
42
+ ## If Functor were built-in to Ruby this would not be
43
+ ## needed since exceptions could just be added directly.
44
+ ##$FUNCTOR_EXCEPTIONS ||= [:binding, :undefine_method]
45
+
46
+ ## TODO: This will not work when BasicObject is utilized. How to fix?
47
+ ##def self.honor_exceptions
48
+ ## $FUNCTOR_EXCEPTIONS.each{ |name|
49
+ ## next if method_defined?(name)
50
+ ## eval %{
51
+ ## def #{name}(*a,&b)
52
+ ## super(*a, &b)
53
+ ## end
54
+ ## }
55
+ ## }
56
+ ##end
57
+
58
+ # Privatize all methods except vital methods and #binding.
59
+ instance_methods(true).each do |m|
60
+ next if m.to_s =~ /^__/
61
+ next if EXCEPTIONS.include?(m.to_sym)
62
+ undef_method(m)
63
+ end
64
+
65
+ #
66
+ def initialize(&function)
67
+ @function = function
68
+ end
69
+
70
+ #
71
+ def to_proc
72
+ @function
73
+ end
74
+
75
+ ##def inspect
76
+ ## #"#<#{__class__}:#{object_id} #{method_missing(:inspect)}>" # hex id ?
77
+ ## "#{method_missing(:inspect)}"
78
+ ##end
79
+
80
+ ## Needed?
81
+ ##def send(op, *a, &b)
82
+ ## method_missing(op, *a, &b)
83
+ ##end
84
+
85
+ private
86
+
87
+ # Any action against the Functor is processesd by the function.
88
+ def method_missing(op, *args, &blk)
89
+ @function.call(op, *args, &blk)
90
+ end
91
+
92
+ end
data/lib/predicates.rb ADDED
@@ -0,0 +1,35 @@
1
+ module Predicates
2
+
3
+ module Numbers
4
+
5
+ def even
6
+ -> (v) { Type.responds(v, :even?); v if v.even? }
7
+ end
8
+
9
+ def odd
10
+ -> (v) { Type.responds(v, :odd?); v if v.odd? }
11
+ end
12
+
13
+ end
14
+
15
+ module Conversions
16
+
17
+ def to_string
18
+ -> (v) { v.to_s }
19
+ end
20
+
21
+ def to_int
22
+ -> (v) { Type.responds(v, :to_i); v.to_i }
23
+ end
24
+
25
+ def to_float
26
+ -> (v) { Type.responds(v, :to_i); v.to_f }
27
+ end
28
+
29
+ def to_array
30
+ -> (v) { [v] }
31
+ end
32
+
33
+ end
34
+
35
+ end
data/lib/sequence.rb CHANGED
@@ -1,22 +1,17 @@
1
1
  class NoSuchElementException < RuntimeError
2
2
  end
3
3
 
4
- class Empty
5
-
6
- def each
7
- yield self
8
- end
9
-
4
+ class UnsupportedTypeException < RuntimeError
10
5
  end
11
6
 
12
7
  module Sequences
13
8
 
14
9
  def sequence(*items)
15
10
  if items.size == 1
16
- if items.first.kind_of?(Range)
17
- Sequence.new(items.first)
18
- elsif items.first.kind_of?(Hash)
11
+ if [Range, Hash].include?(items.first.class)
19
12
  Sequence.new(items.first)
13
+ elsif items.first.nil?
14
+ empty
20
15
  else
21
16
  Sequence.new(items)
22
17
  end
@@ -26,7 +21,7 @@ module Sequences
26
21
  end
27
22
 
28
23
  def empty
29
- sequence(Empty.new)
24
+ Empty.new
30
25
  end
31
26
 
32
27
  class Sequence < Enumerator
@@ -49,23 +44,37 @@ module Sequences
49
44
  end
50
45
 
51
46
  def <=>(object)
52
- self.class <=> object.class
47
+ self.entries <=> object.entries
53
48
  end
54
49
 
55
- def map(&block)
56
- Sequence.new(self) { |yielder, val|
57
- yielder << block.call(val)
58
- }
50
+ def map(predicate=nil, &block)
51
+ if predicate
52
+ Sequence.new(self) { |yielder, val|
53
+ v = predicate.call(val)
54
+ yielder << v unless v.nil?
55
+ }
56
+ else
57
+ Sequence.new(self) { |yielder, val|
58
+ yielder << block.call(val)
59
+ }
60
+ end
59
61
  end
60
62
 
61
63
  alias collect map
62
64
 
63
- def select(&block)
64
- Sequence.new(self) { |yielder, val|
65
- if block.call(val)
66
- yielder << val
67
- end
68
- }
65
+ def select(predicate=nil, &block)
66
+ if predicate
67
+ Sequence.new(self) { |yielder, val|
68
+ v = predicate.call(val)
69
+ yielder << v unless v.nil?
70
+ }
71
+ else
72
+ Sequence.new(self) { |yielder, val|
73
+ if block.call(val)
74
+ yielder << val
75
+ end
76
+ }
77
+ end
69
78
  end
70
79
 
71
80
  alias find_all select
@@ -165,7 +174,11 @@ module Sequences
165
174
  alias get []
166
175
 
167
176
  def empty?
168
- sequence.next.kind_of?(Empty)
177
+ begin
178
+ sequence.peek_values.empty?
179
+ rescue StopIteration
180
+ true
181
+ end
169
182
  end
170
183
 
171
184
  def head
@@ -206,30 +219,115 @@ module Sequences
206
219
  def transpose
207
220
  Sequence.new(Sequence::Generator.new do |g|
208
221
  result = []
209
- max_size = self.entries.max { |a, b| a.size <=> b.size }.size
222
+ max_size = self.to_a.max { |a, b| a.size <=> b.size }.size
210
223
  max_size.times do |i|
211
- result[i] = [self.entries.first.size]
212
- self.entries.each_with_index { |r, j| result[i][j] = r[i] }
224
+ result[i] = [self.to_a.first.size]
225
+ self.to_a.each_with_index { |r, j| result[i][j] = r[i] }
213
226
  end
214
227
  result
215
- self.empty? ? raise(NoSuchElementException.new, 'The sequence was empty') : result.each { |i| g.yield i }
228
+ if self.empty?
229
+ raise(NoSuchElementException.new, 'The sequence was empty')
230
+ else
231
+ raise(Exception.new, 'The subject of transposition must be multidimensional') unless self.to_a.first.is_a?(Array)
232
+ end
233
+ result.each { |i| g.yield i }
216
234
  end)
217
235
  end
218
236
 
219
237
  def join(target_sequence)
220
238
  Sequence.new(Sequence::Generator.new do |g|
221
239
  raise(Exception.new, 'The target (right side) must be a sequence') unless target_sequence.kind_of?(Sequences::Sequence)
222
- (self.entries << target_sequence.entries).flatten.each{|i| g.yield i}
240
+ elements = self.entries
241
+ elements = elements.reject { |i| i.is_a?(Empty) }
242
+ (elements << target_sequence.entries).flatten.each { |i| g.yield i }
223
243
  end)
224
244
  end
225
- alias + join
245
+
226
246
  alias << join
227
247
 
248
+ def add(target_sequence)
249
+ Sequence.new(Sequence::Generator.new do |g|
250
+ (self.entries + target_sequence.entries).each { |i| g.yield i }
251
+ end)
252
+ end
253
+
254
+ alias + add
255
+
256
+ def append(item)
257
+ Sequence.new(Sequence::Generator.new do |g|
258
+ elements = self.entries
259
+ elements = elements.reject { |i| i.is_a?(Empty) }
260
+ (elements << item).each { |i| g.yield i }
261
+ end)
262
+ end
263
+
264
+ def to_seq
265
+ Sequence.new(Sequence::Generator.new do |g|
266
+ self.entries.map { |e| Type.check(e, Sequences::Sequence); e.entries }.flatten.each { |i| g.yield i }
267
+ end)
268
+ end
269
+
270
+ def from_pairs
271
+ Sequence.new(Sequence::Generator.new do |g|
272
+ self.entries.map { |e| Type.check(e, Pair::Pair); [e.key, e.value] }.flatten.each { |i| g.yield i }
273
+ end)
274
+ end
275
+
276
+ def to_a
277
+ execution = {
278
+ Sequences::Sequence => -> { self.entries.map { |s| s.entries } },
279
+ Pair::Pair => -> { self.entries.map { |pair| pair.to_map } }
280
+ }
281
+ if self.empty?
282
+ self.entries
283
+ else
284
+
285
+ first_item = self.peek_values.first.class
286
+ execution[first_item].nil? ? self.entries : execution[first_item].call
287
+ end
288
+ end
289
+
290
+ def all
291
+ to_a.flatten
292
+ end
293
+
294
+ def get_or_else(index, or_else)
295
+ blank?(sequence[index]) ? or_else : sequence[index]
296
+ end
297
+
298
+ def get_option(index)
299
+ blank?(sequence[index]) ? none : some(sequence[index])
300
+ end
301
+
302
+
228
303
  private
229
304
  def sequence
230
305
  Sequence.new(self)
231
306
  end
232
307
 
308
+ def blank?(item)
309
+ item.respond_to?(:empty?) ? item.empty? : !item
310
+ end
311
+
312
+ end
313
+
314
+ class Empty < Sequence
315
+
316
+ def initialize(obj=[], &block)
317
+ super(obj) { |yielder|
318
+ begin
319
+ obj.each { |x|
320
+ if block
321
+ block.call(yielder, x)
322
+ else
323
+ yielder << x
324
+ end
325
+ }
326
+ rescue StopIteration
327
+ end
328
+ }
329
+ end
330
+
233
331
  end
234
332
 
235
333
  end
@@ -246,3 +344,5 @@ end
246
344
 
247
345
 
248
346
 
347
+
348
+
data/lib/totally_lazy.rb CHANGED
@@ -1,8 +1,13 @@
1
+ require_relative 'type_check'
1
2
  require_relative 'sequence'
2
3
  require_relative 'pair'
3
4
  require_relative 'option'
5
+ require_relative 'functor'
6
+ require_relative 'predicates'
4
7
 
5
8
  include Sequences
6
9
  include Option
7
10
  include Pair
11
+ include Predicates::Numbers
12
+ include Predicates::Conversions
8
13
 
data/lib/type_check.rb ADDED
@@ -0,0 +1,11 @@
1
+ class Type
2
+
3
+ def self.check(actual, expected)
4
+ raise(UnsupportedTypeException.new, "Target must be of type: #{expected} but was: #{actual.class}") unless actual.kind_of?(expected)
5
+ end
6
+
7
+ def self.responds(value,meth)
8
+ raise(UnsupportedTypeException.new, "Target must respond to: #{meth} - but did not with: #{value.class} of: #{value}") unless value.respond_to?(meth)
9
+ end
10
+
11
+ end
data/spec/option_spec.rb CHANGED
@@ -4,7 +4,7 @@ require_relative '../lib/totally_lazy'
4
4
  describe 'Option' do
5
5
 
6
6
  it 'should support contains' do
7
- expect(option(1).contains(1)).to be(true)
7
+ expect(option(1).contains(1)).to eq(true)
8
8
  end
9
9
 
10
10
  it 'should support is alias' do
@@ -26,7 +26,8 @@ describe 'Option' do
26
26
  end
27
27
 
28
28
  it 'should not get value of none' do
29
-
29
+ pending('not get none')
30
+ pass
30
31
  end
31
32
 
32
33
  it 'should get or else' do
data/spec/pair_spec.rb CHANGED
@@ -22,7 +22,7 @@ describe 'Pair' do
22
22
  end
23
23
 
24
24
  it 'should convert to a sequence of pairs from a map' do
25
- expect(Pair.from_map({apples:'10'})).to eq(sequence(pair(:apples,'10')))
25
+ expect(Pair.from_map({apples:'10'}).to_a).to eq(sequence(pair(:apples,'10')).to_a)
26
26
  end
27
27
 
28
28
  it 'should convert a pair to a map' do
@@ -8,7 +8,7 @@ describe 'Sequence' do
8
8
  end
9
9
 
10
10
  it 'should support transpose' do
11
- expect(sequence(sequence(1, 2), sequence(3, 4), sequence(5, 6))).to include(sequence(1, 3, 5), sequence(2, 4, 6))
11
+ expect(sequence(sequence(1, 2), sequence(3, 4), sequence(5, 6)).transpose).to include(sequence(1, 3, 5), sequence(2, 4, 6))
12
12
  end
13
13
 
14
14
  it 'should eagerly return the first element of a sequence, returns NoSuchElementException if empty' do
@@ -46,5 +46,10 @@ describe 'Sequence' do
46
46
  expect { empty.shuffle.first }.to raise_error(NoSuchElementException)
47
47
  end
48
48
 
49
+ it 'should lazily join sequences together' do
50
+ expect(sequence(1,2,3).join(sequence(4,5,6))).to eq(sequence(1,2,3,4,5,6))
51
+ expect(empty.join(sequence(1,2))).to eq(sequence(1,2))
52
+ end
53
+
49
54
 
50
55
  end
data/totally_lazy.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: totally_lazy 0.0.2 ruby lib
5
+ # stub: totally_lazy 0.0.3 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "totally_lazy"
9
- s.version = "0.0.2"
9
+ s.version = "0.0.3"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Kingsley Hendrickse"]
14
- s.date = "2014-08-15"
14
+ s.date = "2014-08-16"
15
15
  s.description = "Port of java functional library totally lazy to ruby"
16
16
  s.email = "kingsleyhendrickse@me.com"
17
17
  s.extra_rdoc_files = [
@@ -26,11 +26,14 @@ Gem::Specification.new do |s|
26
26
  "README.md",
27
27
  "Rakefile",
28
28
  "VERSION",
29
+ "lib/functor.rb",
29
30
  "lib/option.rb",
30
31
  "lib/pair.rb",
32
+ "lib/predicates.rb",
31
33
  "lib/sequence.rb",
32
34
  "lib/totally_lazy.rb",
33
35
  "lib/tuple.rb",
36
+ "lib/type_check.rb",
34
37
  "spec/option_spec.rb",
35
38
  "spec/pair_spec.rb",
36
39
  "spec/sequence_spec.rb",
@@ -51,12 +54,14 @@ Gem::Specification.new do |s|
51
54
  s.add_development_dependency(%q<bundler>, ["~> 1.0"])
52
55
  s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
53
56
  s.add_development_dependency(%q<rspec_html_formatter>, ["~> 0.3.0"])
57
+ s.add_development_dependency(%q<rake>, ["~> 10.3.2"])
54
58
  else
55
59
  s.add_dependency(%q<rspec>, ["~> 3.0.0"])
56
60
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
57
61
  s.add_dependency(%q<bundler>, ["~> 1.0"])
58
62
  s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
59
63
  s.add_dependency(%q<rspec_html_formatter>, ["~> 0.3.0"])
64
+ s.add_dependency(%q<rake>, ["~> 10.3.2"])
60
65
  end
61
66
  else
62
67
  s.add_dependency(%q<rspec>, ["~> 3.0.0"])
@@ -64,6 +69,7 @@ Gem::Specification.new do |s|
64
69
  s.add_dependency(%q<bundler>, ["~> 1.0"])
65
70
  s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
66
71
  s.add_dependency(%q<rspec_html_formatter>, ["~> 0.3.0"])
72
+ s.add_dependency(%q<rake>, ["~> 10.3.2"])
67
73
  end
68
74
  end
69
75
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: totally_lazy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kingsley Hendrickse
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-15 00:00:00.000000000 Z
11
+ date: 2014-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.3.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 10.3.2
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 10.3.2
83
97
  description: Port of java functional library totally lazy to ruby
84
98
  email: kingsleyhendrickse@me.com
85
99
  executables: []
@@ -95,11 +109,14 @@ files:
95
109
  - README.md
96
110
  - Rakefile
97
111
  - VERSION
112
+ - lib/functor.rb
98
113
  - lib/option.rb
99
114
  - lib/pair.rb
115
+ - lib/predicates.rb
100
116
  - lib/sequence.rb
101
117
  - lib/totally_lazy.rb
102
118
  - lib/tuple.rb
119
+ - lib/type_check.rb
103
120
  - spec/option_spec.rb
104
121
  - spec/pair_spec.rb
105
122
  - spec/sequence_spec.rb