Class: ArrayEnumerator

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/array_enumerator.rb

Overview

This class is ment as an enumerator but with a cache that enables it to emulate array-functionality (first, empty and so on). If elements goes out of memory, then the array becomes corrupted and methods like 'first' and 'slice' will no longer work (raise errors).

Defined Under Namespace

Classes: ArrayCorruptedError, CannotCallBeforeEnd

Instance Method Summary collapse

Constructor Details

#initialize(enum = nil, &blk) ⇒ ArrayEnumerator

Takes an enumerator to work with as argument.


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/array_enumerator.rb', line 9

def initialize(enum = nil, &blk)
  if enum
    #The enumerator being used.
    @enum = enum
  elsif blk
    @enum = Enumerator.new do |yielder|
      blk.call(yielder)
    end
  else
    raise "No enum or block was given."
  end

  @eles = []
  @end_eles = []

  # Used to calculate length without depending corruption.
  @length_cache = 0

  # If the virtual array has become corrupted because of forgotten elements (by calling each and enumerating through elements).
  @array_corrupted = false

  # To allow the object to be thread-safe.
  @mutex = Mutex.new
end

Instance Method Details

#<<(object) ⇒ Object


39
40
41
# File 'lib/array_enumerator.rb', line 39

def <<(object)
  push(object)
end

#[](key) ⇒ Object

This method should only be used with 'each_index'.


89
90
91
92
93
94
95
96
97
# File 'lib/array_enumerator.rb', line 89

def [](key)
  if @each_index && @each_index.key?(key)
    ret = @each_index[key]
    @each_index.delete(key)
    return ret
  end

  raise "This only works when also using 'each_index'. Invalid key: '#{key}'."
end

#any?Boolean

Returns:

  • (Boolean)

74
75
76
# File 'lib/array_enumerator.rb', line 74

def any?
  !empty?
end

#collectObject Also known as: map


224
225
226
227
228
229
230
231
232
# File 'lib/array_enumerator.rb', line 224

def collect
  check_corrupted

  return ArrayEnumerator.new do |y|
    each do |element|
      y << yield(element)
    end
  end
end

#compactObject


174
175
176
# File 'lib/array_enumerator.rb', line 174

def compact
  reject { |element| element == nil }
end

#each(&block) ⇒ Object

Returns each element and releases them from cache.


79
80
81
82
83
84
85
86
# File 'lib/array_enumerator.rb', line 79

def each(&block)
  if block
    to_enum.each(&block)
    return nil
  else
    return to_enum
  end
end

#each_index(&block) ⇒ Object

Yields each count-key and caches element for returning it by using the []-method.


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/array_enumerator.rb', line 100

def each_index(&block)
  enum = Enumerator.new do |yielder|
    begin
      @each_index = {}

      count = 0
      self.each do |ele|
        # Remove previous element to not take up memory.
        count_before = count - 1
        @each_index.delete(count_before) if @each_index.key?(count_before)

        # Add current element to cache.
        @each_index[count] = ele
        yield(count)

        # Increase count for next run.
        count += 1
      end
    ensure
      @each_index = nil
    end
  end

  if block
    enum.each(&block)
    return nil
  else
    return enum
  end
end

#empty?Boolean

Returns true if the array-enumerator is empty.

Returns:

  • (Boolean)

56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/array_enumerator.rb', line 56

def empty?
  if @empty == nil
    cache_ele if @length_cache == 0

    if @length_cache > 0
      @empty = false
    else
      @empty = true
    end
  end

  return @empty
end

#firstObject

Cache the first elements (if not cached already) and returns it.


49
50
51
52
53
# File 'lib/array_enumerator.rb', line 49

def first
  check_corrupted
  cache_ele if @eles.empty?
  return @eles.first
end

#lengthObject

Returns the counted length. Can only be called after the end of the enumerator has been reached.


152
153
154
155
# File 'lib/array_enumerator.rb', line 152

def length
  raise CannotCallBeforeEnd, "Cant call length before the end has been reached." unless @end
  return @length_cache
end

#none?Boolean

Returns:

  • (Boolean)

70
71
72
# File 'lib/array_enumerator.rb', line 70

def none?
  empty?
end

#push(object) ⇒ Object


34
35
36
37
# File 'lib/array_enumerator.rb', line 34

def push(object)
  raise ArrayCorruptedError if @end
  @end_eles << object
end

#rejectObject Also known as: delete_if


168
169
170
# File 'lib/array_enumerator.rb', line 168

def reject
  select { |element| !yield(element) }
end

#selectObject Also known as: keep_if


157
158
159
160
161
162
163
164
# File 'lib/array_enumerator.rb', line 157

def select
  return ArrayEnumerator.new do |y|
    check_corrupted
    each do |element|
      y << element if yield(element)
    end
  end
end

#shift(*args) ⇒ Object

Caches necessary needed elements and then returns the result as on a normal array.


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/array_enumerator.rb', line 201

def shift(*args)
  check_corrupted

  if args[0]
    amount = args[0]
  else
    amount = 1
  end

  @eles ||= []
  cache_ele(amount - @eles.length) if @eles.length < amount
  res = @eles.shift(*args)

  # Since we are removing an element, the length should go down with the amount of elements captured.
  if args[0]
    @length_cache -= res.length
  else
    @length_cache -= 1
  end

  return res
end

#slice(*args) ⇒ Object

Giving slice negaive arguments will force it to cache all elements and crush the memory for big results.


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/array_enumerator.rb', line 179

def slice(*args)
  check_corrupted

  if args[0].is_a?(Range) && !args[1]
    need_eles = args[0].begin + args[0].end
  elsif args[0] && !args[1]
    need_eles = args[0]
  elsif args[0] && args[1] && args[0] > 0 && args[1] > 0
    need_eles = args[0] + args[1]
  elsif args[0] < 0 || args[1] < 0
    raise ArgumentError, "Slice cant take negative arguments."
  else
    raise ArgumentError, "Dont now what to do with args: '#{args}'."
  end

  @eles ||= []
  cache_eles = need_eles - @eles.length if need_eles
  cache_ele(cache_eles) if need_eles && cache_eles > 0
  return @eles.slice(*args)
end

#to_aObject Also known as: to_ary

Returns a normal array with all elements. Can also raise corrupted error if elements have been thrown out.


237
238
239
240
241
# File 'lib/array_enumerator.rb', line 237

def to_a
  check_corrupted
  cache_all
  return @eles
end

#to_enumObject

Returns a enumerator that can yield all the elements (both cached and future un-cached ones).


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/array_enumerator.rb', line 132

def to_enum
  check_corrupted
  @array_corrupted = true

  return Enumerator.new do |yielder|
    while ele = @eles.shift
      yielder << ele
    end

    yield_rest do |ele|
      yielder << ele
    end

    while ele = @end_eles.shift
      yielder << ele
    end
  end
end

#to_sObject Also known as: inspect


245
246
247
# File 'lib/array_enumerator.rb', line 245

def to_s
  "<ArrayEnumerator array_corrupted=\"#{@array_corrupted}\" length_cache=\"#{@length_cache}\">"
end

#unshift(object) ⇒ Object


43
44
45
46
# File 'lib/array_enumerator.rb', line 43

def unshift(object)
  check_corrupted
  @eles << object
end