Class: ArrayEnumerator

Inherits:
Object
  • Object
show all
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.



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

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

  # 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

#[](key) ⇒ Object

This method should only be used with ‘each_index’.



70
71
72
73
74
75
76
77
78
# File 'lib/array_enumerator.rb', line 70

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)


55
56
57
# File 'lib/array_enumerator.rb', line 55

def any?
  !empty?
end

#collectObject Also known as: map



193
194
195
196
197
198
199
200
201
# File 'lib/array_enumerator.rb', line 193

def collect
  check_corrupted

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

#each(&block) ⇒ Object

Returns each element and releases them from cache.



60
61
62
63
64
65
66
67
# File 'lib/array_enumerator.rb', line 60

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.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/array_enumerator.rb', line 81

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 is empty.

Returns:

  • (Boolean)


37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/array_enumerator.rb', line 37

def empty?
  if @empty == nil
    cache_ele if !@eles

    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.



30
31
32
33
34
# File 'lib/array_enumerator.rb', line 30

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

#lengthObject

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



131
132
133
134
# File 'lib/array_enumerator.rb', line 131

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

#none?Boolean

Returns:

  • (Boolean)


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

def none?
  empty?
end

#selectObject



136
137
138
139
140
141
142
143
144
145
# File 'lib/array_enumerator.rb', line 136

def select
  result = []

  check_corrupted
  self.each do |element|
    result << element if yield(element)
  end

  return result
end

#shift(*args) ⇒ Object

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



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/array_enumerator.rb', line 170

def shift(*args)
  check_corrupted

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

  @eles = [] unless @eles
  cache_ele(amount - @eles.length) if !@eles || @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.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/array_enumerator.rb', line 148

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 "Slice cant take negative arguments."
  else
    raise "Dont now what to do with args: '#{args}'."
  end

  @eles = [] if !@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.



206
207
208
209
210
# File 'lib/array_enumerator.rb', line 206

def to_a
  check_corrupted
  cache_all
  return @eles
end

#to_enumObject

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



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/array_enumerator.rb', line 113

def to_enum
  check_corrupted
  @array_corrupted = true

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

    yield_rest do |ele|
      yielder << ele
    end
  end
end