Class: Rod::CollectionProxy

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

Overview

This class allows for lazy fetching the elements from a collection of Rod objects.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(size, database, offset, klass) ⇒ CollectionProxy

Intializes the proxy with its size, database it is connected to, the offset of join elements and the klass of stored objects. If the klass is nil, the collection holds polymorphic objects.

Raises:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/rod/collection_proxy.rb', line 16

def initialize(size,database,offset,klass)
  raise InvalidArgument.new("collection size",nil) if size.nil?
  @size = size
  @original_size = size
  raise InvalidArgument.new("collection database",nil) if database.nil?
  @database = database
  raise InvalidArgument.new("collection offset",nil) if offset.nil?
  @offset = offset
  @klass = klass
  #@commands = []
  @added = []
  @deleted = []
  @map = {}
end

Instance Attribute Details

#offsetObject (readonly)

Returns the value of attribute offset.



9
10
11
# File 'lib/rod/collection_proxy.rb', line 9

def offset
  @offset
end

#sizeObject (readonly) Also known as: count

Returns the value of attribute size.



9
10
11
# File 'lib/rod/collection_proxy.rb', line 9

def size
  @size
end

Instance Method Details

#&(other) ⇒ Object

Computes an intersection with the other collection proxy.



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
111
112
113
114
115
116
# File 'lib/rod/collection_proxy.rb', line 86

def &(other)
  # So far this optimization works only for monomorphic
  # collection proxies without added elements.
  if @klass && @added.empty?
    my_ids = self.size.times.map do |index|
      id_for(index)
    end.sort
    other_ids = other.size.times.map do |index|
      other.id_for(index)
    end.sort
    ids = []
    last_id = nil
    while(!my_ids.empty?) do
      if my_ids.first == other_ids.first
        id = my_ids.shift
        other_ids.shift
        ids << id unless last_id == id
        last_id = id
      elsif my_ids.first < other_ids.first
        my_ids.shift
      else
        other_ids.shift
      end
    end
    result = CollectionProxy.new(0,@database,0,@klass)
    ids.each{|id| result << [id,@klass]}
  else
    result = self.to_a & other.to_a
  end
  result
end

#<<(element) ⇒ Object

Appends element to the end of the collection.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/rod/collection_proxy.rb', line 119

def <<(element)
  if element.nil?
    pair = [0,NilClass]
  elsif element.is_a?(Model)
    if element.new?
      pair = [element,element.class]
    else
      pair = [element.rod_id,element.class]
    end
  else
    # Assume we have an array with direct values of rod_id and class.
    pair = element
  end
  index = @size
  @map[index] = @added.size
  @added << pair
  #@commands << [:append, pair]
  @size += 1
end

#[](index) ⇒ Object

Returns an object with given index. The index have to be positive and smaller from the collection size. Otherwise nil is returned.



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/rod/collection_proxy.rb', line 34

def [](index)
  return nil if index >= @size || index < 0
  rod_id = id_for(index)
  if rod_id.is_a?(Model)
    rod_id
  elsif rod_id == 0
    nil
  else
    class_for(index).find_by_rod_id(rod_id)
  end
end

#addedObject

Returns a collection of added items.



242
243
244
245
246
247
248
249
250
# File 'lib/rod/collection_proxy.rb', line 242

def added
  @added.map do |id_or_object,klass|
    if id_or_object.is_a?(Model)
      id_or_object
    else
      id_or_object == 0 ? nil : klass.find_by_rod_id(id_or_object)
    end
  end
end

#clearObject

Clears the contents of the collection proxy.



216
217
218
219
220
221
# File 'lib/rod/collection_proxy.rb', line 216

def clear
  @deleted = @original_size.times.to_a
  @added.clear
  @map.clear
  @size = 0
end

#delete(element) ⇒ Object

Removes the element from the collection.



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/rod/collection_proxy.rb', line 164

def delete(element)
  indices = []
  self.each.with_index{|e,i| indices << i if e == element}
  if indices.empty?
    if block_given?
      return yield
    else
      return nil
    end
  end
  #@commands << [:delete,indices]
  indices.each.with_index do |index,offset|
    self.delete_at(index-offset)
  end
  element
end

#delete_at(index) ⇒ Object

Removes the element at index from the colelction. So far the index has to be positive.



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/rod/collection_proxy.rb', line 183

def delete_at(index)
  return nil if index >= @size || index < 0
  element = self[index]
  if direct_index = @map[index]
    @added.delete_at(direct_index)
    @map.delete(index)
    @map.keys.sort.each do |key|
      if key > index
        value = @map.delete(key)
        value -= 1 if value > direct_index
        @map[key-1] = value
      else
        if (value = @map[key]) > direct_index
          @map[key] -= 1
        end
      end
    end
  else
    lazy_index = lazy_index(index)
    position = @deleted.bsearch_upper_boundary{|e| e <=> lazy_index }
    @deleted.insert(position,lazy_index)
    @map.keys.sort.each do |key|
      if key > index
        @map[key-1] = @map.delete(key)
      end
    end
  end
  #@commands << [:delete,[index]]
  @size -= 1
  element
end

#deletedObject

Returns a collection of deleted items.



253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/rod/collection_proxy.rb', line 253

def deleted
  @deleted.map do |index|
    if polymorphic?
      rod_id = @database.polymorphic_join_index(@offset,index)
      if rod_id != 0
        klass = Model.get_class(@database.polymorphic_join_class(@offset,index))
      end
    else
      klass = @klass
      rod_id = @database.join_index(@offset,index)
    end
    rod_id == 0 ? nil : klass.find_by_rod_id(rod_id)
  end
end

#eachObject

Iterator implementation. It raises an exception when the collection is modified during iteration. WARNING: This is not compliant with an Array class!



226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/rod/collection_proxy.rb', line 226

def each
  if block_given?
    @size.times do |index|
      added_size = @added.size
      deleted_size = @deleted.size
      yield self[index]
      if added_size != @added.size || deleted_size != @deleted.size
        raise "Can't modify collection during iteration!"
      end
    end
  else
    enum_for(:each)
  end
end

#empty?Boolean

Returns true if the collection is empty.

Returns:

  • (Boolean)


275
276
277
# File 'lib/rod/collection_proxy.rb', line 275

def empty?
  self.count == 0
end

#insert(index, element) ⇒ Object

Inserts the element at given index. So far the index has to be positive, smaller or equal to size and only one pair of values is accepted. If these assumptions are not met, nil is returned.



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/rod/collection_proxy.rb', line 143

def insert(index,element)
  return nil if index < 0 || index > @size
  if element.new?
    pair = [element,element.class]
  else
    pair = [element.rod_id,element.class]
  end
  @map.keys.sort.reverse.each do |key|
    if key >= index
      value = @map.delete(key)
      @map[key+1] = value
    end
  end
  @map[index] = @added.size
  @added << pair
  #@commands << [:insert,pair]
  @size += 1
  self
end

#intersection_size(other) ⇒ Object

Returns the size of intersection with the other collection proxy. XXX this method assumes that the elements are sorted according to rod_id, the collection is not polymorphic and no elements were added nor deleted.



49
50
51
# File 'lib/rod/collection_proxy.rb', line 49

def intersection_size(other)
  @database.fast_intersection_size(self.offset,self.size,other.offset,other.size)
end

#saveObject

Saves to collection proxy into disk and returns the collection proxy’s offset. If no element was added nor deleted, nothing happes.



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/rod/collection_proxy.rb', line 282

def save
  unless @added.empty? && @deleted.empty?
    # We cannot reuse the allocated space, since the data
    # that is copied would be destroyed.
    if polymorphic?
      offset = @database.allocate_polymorphic_join_elements(@size)
    else
      offset = @database.allocate_join_elements(@size)
    end
    pairs =
      @size.times.map do |index|
        rod_id = id_for(index)
        if rod_id.is_a?(Model)
          object = rod_id
          if object.new?
            if polymorphic?
              object.reference_updaters <<
                ReferenceUpdater.for_plural(self,index,@database)
            else
              object.reference_updaters <<
                ReferenceUpdater.for_plural(self,index,@database)
            end
            next
          else
            rod_id = object.rod_id
          end
        end
        [rod_id,index]
      end.compact
    if polymorphic?
      pairs.each do |rod_id,index|
        class_id = (rod_id == 0 ? 0 : class_for(index).name_hash)
        @database.set_polymorphic_join_element_id(offset,index,rod_id,class_id)
      end
    else
      pairs.each do |rod_id,index|
        @database.set_join_element_id(offset,index,rod_id)
      end
    end
    @offset = offset
    @added.clear
    @deleted.clear
    @map.clear
    @original_size = @size
  end
  @offset
end

#to_sObject

String representation of the collection proxy. Displays only the actual and the original size.



270
271
272
# File 'lib/rod/collection_proxy.rb', line 270

def to_s
  "Collection:[#{@size}][#{@original_size}]"
end

#|(other) ⇒ Object

Computes a union with the other collection proxy.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rod/collection_proxy.rb', line 54

def |(other)
  # So far this optimization works only for monomorphic
  # collection proxies without added elements.
  if @klass && @added.empty?
    my_ids = self.size.times.map do |index|
      id_for(index)
    end.sort
    other_ids = other.size.times.map do |index|
      other.id_for(index)
    end.sort
    ids = []
    last_id = nil
    while(!my_ids.empty?) do
      id = my_ids.shift
      other_ids.shift if other_ids.first == id
      ids << id unless last_id == id
      last_id = id
    end
    while(!other_ids.empty?) do
      id = other_ids.shift
      ids << id unless last_id == id
      last_id = id
    end
    result = CollectionProxy.new(0,@database,0,@klass)
    ids.each{|id| result << [id,@klass]}
  else
    result = self.to_a | other.to_a
  end
  result
end