LazyEnumerable 0.0.3 → 0.0.4

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.
@@ -18,7 +18,7 @@ end
18
18
  module Enumerable
19
19
 
20
20
  def to_lazy
21
- Lazy::LazyEnumerable.new(self)
21
+ Lazy::LazyEnumerable.new(self.method(:collect))
22
22
  end
23
23
 
24
24
  def to_real
@@ -1,79 +1,103 @@
1
- ### It takes a functional approach to the common Enumerable protocol of select, collect, and reject.
2
- ### By functional, I mean the collection is not changed nor is a new modified one created.
3
- ### The blocks are kept around until they are absolutely needed.
4
- ### I have been wanting this functionality for some time because it's nice for large collections.
5
- ### If you have a collection in which you are calling a lot selects, rejects, or collects on,
6
- ### then this will not create the intermediate collections.
7
- ### It will wait until you ask something of the collection where it can not delay the answer.
8
- ### This should make these chained operations must faster on large collections.
9
- ###
10
- ### This was a lot of fun to program and it's not that big. Take whatever you want from it!
11
-
12
- module Lazy
13
-
14
- class LazyEnumerable < AbstractEnumerable
15
- @@cached={}
16
-
17
- ##
18
- ## Meta-programming to provide a shorthand for extensions to let themselves
19
- ## known so that they can be found on the method_missing.
20
- ##
21
- def self.define_for(method_name)
22
- @@cached[method_name]=self
23
- end
24
-
25
- ##
26
- ## Lookup a subclass for the particular method. Basically, this is so that each
27
- ## template will provide its own functionality for each
28
- ##
29
- def self.class_for(method_name)
30
- @@cached[method_name]
31
- end
32
-
33
-
34
-
35
- def initialize(original, block=PLACEBO)
36
- @original=original
37
- @block=block
38
- _freeze
39
- end
40
-
41
- ##
42
- ## This is basically the default implementation and should be overriden in
43
- ## subclasses. It's because we can create a basic LazyEnumerable (on call to_lazy)
44
- ## that simply is a placebo implementation to keep things simple
45
- ##
46
-
47
- def each(&block)
48
- @original.each(&block)
49
- end
50
-
51
- ##
52
- ## Look up the implementation of the iterator method (for example, :select, :collect:, etc)
53
- ## I do define the missing method on this call. The reason is for ease of debugging and
54
- ## speed. We don't need to go through this logic. It's on the fly code generation without
55
- ## eval
56
- ##
57
- def method_missing(method_name, *arguments)
58
- my_class=self._class.class_for(method_name)
59
- if (my_class.nil?)
60
- super.method_missing(method_name, *arguments)
61
- end
62
-
63
- proc = lambda {|*args| my_class.new(self, *args)}
64
- self._class.send(:define_method, method_name, &proc)
65
- proc.call(*arguments)
66
- end
67
-
68
- def respond_to?(symbol,include_private = false)
69
- my_class=self._class.class_for(method_name)
70
- !my_class.nil?
71
- end
72
-
73
- def to_real
74
- _collect(&PLACEBO)
75
- end
76
-
77
- end
78
-
1
+ ###
2
+ ### LazyEnumerable makes the implementation of higher order methods easier. LazyIterator
3
+ ### is a simple implemetation of that. It basically works by "stacking" up the method
4
+ ### calls until the collection needs to be calculated (realized). It then calls the
5
+ ### "stacked up" methods.
6
+ ###
7
+ ### LazyEnumerable is immutable and I would like LazyIterator to be as well. But,
8
+ ### every attempt has made the code ugly and inelegant. I'm still looking for a good
9
+ ### solution. Immuntability gives the ability to calculate on different levels.
10
+ ###
11
+ module Lazy
12
+
13
+ class LazyEnumerable
14
+ PLACEBO=lambda {|each| each}
15
+
16
+ ##
17
+ ## Remove any unnecessary methods so that method_missing is invoked
18
+ ##
19
+ def self.wack_all_my_methods
20
+ to_wack = instance_methods.reject do |each|
21
+ ['===','method_missing'].include?(each) || each =~ /^__/
22
+ end
23
+ to_wack.each do |each|
24
+ alias_method("_#{each}", each)
25
+ undef_method(each)
26
+ end
27
+ end
28
+
29
+ def self.iterator_creator(*method_names)
30
+ method_names.each { |every| iterator_creator_for(every) }
31
+ end
32
+
33
+ def self.iterator_creator_for(method_name)
34
+ non_lazy_name = /lazy_(.+)/.match(method_name.to_s)[1].to_sym
35
+ define_method(method_name) { LazyEnumerable.new(_method(non_lazy_name)) }
36
+ end
37
+
38
+ wack_all_my_methods
39
+ include Enumerable
40
+ iterator_creator :lazy_inject, :lazy_select, :lazy_collect, :lazy_reject, :lazy_detect, :lazy_each
41
+
42
+
43
+
44
+ def initialize(proc)
45
+ @internal=proc
46
+ @sends=[]
47
+ end
48
+
49
+ ##
50
+ ## Simply make a closure around the method and capturing it's arguments as well
51
+ ## This is so it can be replayed. Notice again, I cache the method with a define_method
52
+ ##
53
+ def method_missing(method_name, *arguments)
54
+ proc=lambda do |*args|
55
+ send=lambda {|each| each.send(method_name, *args)}
56
+ @sends << send
57
+ self
58
+ end
59
+ self._class.send(:define_method, method_name, &proc)
60
+ proc.call(*arguments)
61
+ end
62
+
63
+ def respond_to?(symbol,include_private = false)
64
+ true
65
+ end
66
+
67
+ ##
68
+ ## Do a placebo collect to get it to create an array with the elements
69
+ ##
70
+ def to_real
71
+ collect(&PLACEBO)
72
+ end
73
+
74
+ ##
75
+ ## Call each member in the original collection and call each "captured" method
76
+ ## in succession. Notice the use of inject to make this really elegant and succint.
77
+ ## I do cheat a little and realize the collection and then call each. I want
78
+ ## to change this in the future. Actually, 1.9 will make this possible. We'll
79
+ ## be able to use iterators and will make below more lazy.
80
+ ##
81
+ def each(&block)
82
+ answer = @internal.call do |each|
83
+ @sends.inject(each) do |result, each_send |
84
+ each_send.call(result)
85
+ end
86
+ end
87
+ answer.each(&block)
88
+ end
89
+
90
+ ##
91
+ ## Give me the size. This can be dangerous if you ever wrap an infinite enumerable
92
+ ##
93
+ def size
94
+ inject(0) {|sum,each| sum + 1 }
95
+ end
96
+
97
+ def to_lazy
98
+ self
99
+ end
100
+
101
+ end
102
+
79
103
  end
@@ -1,7 +1,2 @@
1
1
  require 'lazy/extensions'
2
- require 'lazy/abstract_enumerable'
3
- require 'lazy/lazy_enumerable'
4
- require 'lazy/collect_enumerable'
5
- require 'lazy/reject_enumerable'
6
- require 'lazy/select_enumerable'
7
- require 'lazy/message_collector'
2
+ require 'lazy/lazy_enumerable'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: LazyEnumerable
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
  - Blaine Buxton
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-10-05 00:00:00 -05:00
12
+ date: 2008-10-06 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -26,13 +26,8 @@ files:
26
26
  - tests/lazy_enumerable_test.rb
27
27
  - tests/lazy_enumerable_test_suite.rb
28
28
  - lib/lazy
29
- - lib/lazy/abstract_enumerable.rb
30
- - lib/lazy/collect_enumerable.rb
31
29
  - lib/lazy/extensions.rb
32
30
  - lib/lazy/lazy_enumerable.rb
33
- - lib/lazy/message_collector.rb
34
- - lib/lazy/reject_enumerable.rb
35
- - lib/lazy/select_enumerable.rb
36
31
  - lib/lazy_enumerable.rb
37
32
  - README
38
33
  - LICENSE
@@ -1,50 +0,0 @@
1
- module Lazy
2
- class AbstractEnumerable
3
- PLACEBO=lambda {|each| each}
4
-
5
- ##
6
- ## Remove any unnecessary methods so that method_missing is invoked
7
- ##
8
- def self.wack_all_my_methods
9
- to_wack = instance_methods.reject do |each|
10
- ['===','method_missing'].include?(each) || each =~ /^__/
11
- end
12
- to_wack.each do |each|
13
- alias_method("_#{each}", each)
14
- undef_method(each)
15
- end
16
- end
17
-
18
- def self.iterator_creator(*method_names)
19
- method_names.each { |every| iterator_creator_for(every) }
20
- end
21
-
22
- def self.iterator_creator_for(method_name)
23
- non_lazy_name = /lazy_(.+)/.match(method_name.to_s)[1].to_sym
24
- define_method(method_name) { iterator_class.new(_method(non_lazy_name)) }
25
- end
26
-
27
- wack_all_my_methods
28
- include Enumerable
29
- iterator_creator :lazy_select, :lazy_collect, :lazy_reject, :lazy_detect, :lazy_each
30
-
31
- ##
32
- ## Give me the size. This can be dangerous if you ever wrap an infinite enumerable
33
- ##
34
- def size
35
- inject(0) {|sum,each| sum + 1 }
36
- end
37
-
38
- def to_lazy
39
- self
40
- end
41
-
42
- ##
43
- ## This should be added with definition of the iterator class
44
- ##
45
- def iterator_class
46
- raise(NotImplementedError, "iterator_class", caller);
47
- end
48
-
49
- end
50
- end
@@ -1,14 +0,0 @@
1
- module Lazy
2
-
3
- class CollectEnumerable < LazyEnumerable
4
- define_for(:lazy_collect)
5
-
6
- def each(&each_block)
7
- @original.each do |every|
8
- each_block.call(@block.call(every))
9
- end
10
- end
11
-
12
- end
13
-
14
- end
@@ -1,68 +0,0 @@
1
- ###
2
- ### LazyEnumerable makes the implementation of higher order methods easier. LazyIterator
3
- ### is a simple implemetation of that. It basically works by "stacking" up the method
4
- ### calls until the collection needs to be calculated (realized). It then calls the
5
- ### "stacked up" methods.
6
- ###
7
- ### LazyEnumerable is immutable and I would like LazyIterator to be as well. But,
8
- ### every attempt has made the code ugly and inelegant. I'm still looking for a good
9
- ### solution. Immuntability gives the ability to calculate on different levels.
10
- ###
11
- module Lazy
12
-
13
- class MessageCollector < AbstractEnumerable
14
-
15
- def initialize(proc)
16
- @internal=proc
17
- @sends=[]
18
- end
19
-
20
- ##
21
- ## Simply make a closure around the method and capturing it's arguments as well
22
- ## This is so it can be replayed. Notice again, I cache the method with a define_method
23
- ##
24
- def method_missing(method_name, *arguments)
25
- proc=lambda do |*args|
26
- send=lambda {|each| each.send(method_name, *args)}
27
- @sends << send
28
- self
29
- end
30
- self._class.send(:define_method, method_name, &proc)
31
- proc.call(*arguments)
32
- end
33
-
34
- def respond_to?(symbol,include_private = false)
35
- true
36
- end
37
-
38
- ##
39
- ## Do a placebo collect to get it to create an array with the elements
40
- ##
41
- def to_real
42
- collect(&PLACEBO)
43
- end
44
-
45
- ##
46
- ## Call each member in the original collection and call each "captured" method
47
- ## in succession. Notice the use of inject to make this really elegant and succint.
48
- ## I do cheat a little and realize the collection and then call each. I want
49
- ## to change this in the future.
50
- ##
51
- def each(&block)
52
- answer = @internal.call do |each|
53
- @sends.inject(each) do |result, each_send |
54
- each_send.call(result)
55
- end
56
- end
57
- answer.each(&block)
58
- end
59
-
60
- end
61
-
62
- class AbstractEnumerable
63
- def iterator_class
64
- MessageCollector
65
- end
66
- end
67
-
68
- end
@@ -1,14 +0,0 @@
1
- module Lazy
2
-
3
- class RejectEnumerable < LazyEnumerable
4
- define_for(:lazy_reject)
5
-
6
- def each(&each_block)
7
- @original.each do |every|
8
- each_block.call(every) unless @block.call(every)
9
- end
10
- end
11
-
12
- end
13
-
14
- end
@@ -1,14 +0,0 @@
1
- module Lazy
2
-
3
- class SelectEnumerable < LazyEnumerable
4
- define_for(:lazy_select)
5
-
6
- def each(&each_block)
7
- @original.each do |every|
8
- each_block.call(every) if @block.call(every)
9
- end
10
- end
11
-
12
- end
13
-
14
- end