LazyEnumerable 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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