Module: Glimmer::DataBinding::ObservableArray

Includes:
Observable
Defined in:
lib/glimmer/data_binding/observable_array.rb

Defined Under Namespace

Classes: Notifier

Instance Method Summary collapse

Methods included from Observable

#inspect

Instance Method Details

#<<(element) ⇒ Object Also known as: push, append



153
154
155
156
157
158
# File 'lib/glimmer/data_binding/observable_array.rb', line 153

def <<(element)
  super(element).tap do
    add_element_observers(element)
    notify_observers
  end
end

#[]=(index, value) ⇒ Object



162
163
164
165
166
167
168
169
170
# File 'lib/glimmer/data_binding/observable_array.rb', line 162

def []=(index, value)
  old_value = self[index]
  unregister_dependent_observers(old_value)
  remove_element_observers(old_value)
  add_element_observers(value)
  super(index, value).tap do
    notify_observers
  end
end

#add_element_observer(element, observer, options = {}) ⇒ Object



68
69
70
71
72
73
74
75
# File 'lib/glimmer/data_binding/observable_array.rb', line 68

def add_element_observer(element, observer, options = {})
  element_properties_for(observer).each do |property|
    observer.observe(element, property, options)
  end
  if element.is_a?(Array) && (options[:recursive] == true || (options[:recursive].is_a?(Integer) && options[:recursive] >= 0))
    ensure_array_object_observer(element, options)
  end
end

#add_element_observers(element, general_options = {}) ⇒ Object



62
63
64
65
66
# File 'lib/glimmer/data_binding/observable_array.rb', line 62

def add_element_observers(element, general_options = {})
  property_observer_list.each do |observer, options|
    add_element_observer(element, observer, options.merge(general_options))
  end
end

#add_observer(observer, *args) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/glimmer/data_binding/observable_array.rb', line 48

def add_observer(observer, *args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  element_properties = args.flatten.compact.uniq
  return observer if has_observer?(observer) && has_observer_element_properties?(observer, element_properties)
  property_observer_list[observer] = options
  observer_element_properties[observer] = Concurrent::Set.new(Concurrent::Array.new(element_properties_for(observer).to_a) + Concurrent::Array.new(element_properties)) # converting to Array as a workaround to jruby-9.3.2.0 issue TODO remove this workaround when no longer needed
  if !options.empty? && options[:recursive].is_a?(Integer)
    options = options.clone
    options[:recursive] = options[:recursive] - 1
  end
  each { |element| add_element_observer(element, observer, options) }
  observer
end

#array_object_observer_for(object) ⇒ Object



87
88
89
90
91
# File 'lib/glimmer/data_binding/observable_array.rb', line 87

def array_object_observer_for(object)
  @array_object_observers ||= Concurrent::Hash.new
  @array_object_observers[object] = Notifier.new(self) unless @array_object_observers.has_key?(object)
  @array_object_observers[object]
end

#clearObject



222
223
224
225
226
227
228
229
230
# File 'lib/glimmer/data_binding/observable_array.rb', line 222

def clear
  each do |old_value|
    unregister_dependent_observers(old_value)
    remove_element_observers(old_value)
  end
  super.tap do
    notify_observers
  end
end

#collect!(&block) ⇒ Object Also known as: map!



238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/glimmer/data_binding/observable_array.rb', line 238

def collect!(&block)
  if block_given?
    each do |old_value|
      unregister_dependent_observers(old_value)
      remove_element_observers(old_value)
    end
    super(&block).tap do
      each { |element| add_element_observers(element) }
      notify_observers
    end
  else
    super
  end
end

#compact!Object



254
255
256
257
# File 'lib/glimmer/data_binding/observable_array.rb', line 254

def compact!
  # TODO consider checking which exact indices changed and only notifying if there is a change
  super.tap { notify_observers }
end

#delete(element) ⇒ Object



190
191
192
193
194
195
196
# File 'lib/glimmer/data_binding/observable_array.rb', line 190

def delete(element)
  unregister_dependent_observers(element)
  remove_element_observers(element)
  super(element).tap do
    notify_observers
  end
end

#delete_at(index) ⇒ Object



198
199
200
201
202
203
204
205
# File 'lib/glimmer/data_binding/observable_array.rb', line 198

def delete_at(index)
  old_value = self[index]
  unregister_dependent_observers(old_value)
  remove_element_observers(old_value)
  super(index).tap do
    notify_observers
  end
end

#delete_if(&block) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/glimmer/data_binding/observable_array.rb', line 207

def delete_if(&block)
  if block_given?
    old_array = Array.new(self)
    super(&block).tap do |new_array|
      (old_array - new_array).each do |element|
        unregister_dependent_observers(element)
        remove_element_observers(element)
      end
      notify_observers
    end
  else
    super
  end
end

#element_properties_for(observer) ⇒ Object



145
146
147
# File 'lib/glimmer/data_binding/observable_array.rb', line 145

def element_properties_for(observer)
  observer_element_properties[observer] ||= Concurrent::Set.new
end

#ensure_array_object_observer(object, options) ⇒ Object



77
78
79
80
81
82
83
84
85
# File 'lib/glimmer/data_binding/observable_array.rb', line 77

def ensure_array_object_observer(object, options)
  return unless object&.is_a?(Array)
  array_object_observer = array_object_observer_for(object)
  array_observer_registration = array_object_observer.observe(object, options)
  property_observer_list.each do |observer, options|
    my_registration = observer.registration_for(self)
    observer.add_dependent(my_registration => array_observer_registration)
  end
end

#filter!(&block) ⇒ Object



289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/glimmer/data_binding/observable_array.rb', line 289

def filter!(&block)
  if block_given?
    old_array = Array.new(self)
    super(&block).tap do
      (old_array - self).each do |old_value|
        unregister_dependent_observers(old_value)
        remove_element_observers(old_value)
      end
      notify_observers
    end
  else
    super
  end
end

#flatten!(level = nil) ⇒ Object



259
260
261
262
263
264
265
266
267
268
# File 'lib/glimmer/data_binding/observable_array.rb', line 259

def flatten!(level=nil)
  each do |old_value|
    unregister_dependent_observers(old_value)
    remove_element_observers(old_value)
  end
  (level.nil? ? super() : super(level)).tap do
    each { |element| add_element_observers(element) }
    notify_observers
  end
end

#has_observer?(observer) ⇒ Boolean

Returns:

  • (Boolean)


129
130
131
# File 'lib/glimmer/data_binding/observable_array.rb', line 129

def has_observer?(observer)
  property_observer_list.keys.include?(observer)
end

#has_observer_element_properties?(observer, element_properties) ⇒ Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/glimmer/data_binding/observable_array.rb', line 133

def has_observer_element_properties?(observer, element_properties)
  element_properties_for(observer).to_a.include_all?(*element_properties)
end

#notify_observersObject



149
150
151
# File 'lib/glimmer/data_binding/observable_array.rb', line 149

def notify_observers
  property_observer_list.to_a.each { |obs, opt| obs.call(self) }
end

#observer_element_propertiesObject



141
142
143
# File 'lib/glimmer/data_binding/observable_array.rb', line 141

def observer_element_properties
  @observer_element_properties ||= Concurrent::Hash.new
end

#popObject



172
173
174
175
176
177
178
179
# File 'lib/glimmer/data_binding/observable_array.rb', line 172

def pop
  popped_element = last
  unregister_dependent_observers(popped_element)
  remove_element_observers(popped_element)
  super.tap do
    notify_observers
  end
end

#property_observer_listObject



137
138
139
# File 'lib/glimmer/data_binding/observable_array.rb', line 137

def property_observer_list
  @property_observer_list ||= Concurrent::Hash.new
end

#reject!(&block) ⇒ Object



346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/glimmer/data_binding/observable_array.rb', line 346

def reject!(&block)
  if block.nil?
    super
  else
    old_array = Array.new(self)
    super(&block).tap do
      (old_array - self).each do |old_value|
        unregister_dependent_observers(old_value)
        remove_element_observers(old_value)
      end
      notify_observers
    end
  end
end

#remove_element_observer(element, observer) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/glimmer/data_binding/observable_array.rb', line 116

def remove_element_observer(element, observer)
  element_properties_for(observer).each do |property|
    observer.unobserve(element, property)
  end
  if element.is_a?(ObservableArray)
    array_object_observer_for(element).unobserve(element)
    element.property_observer_list.select {|obs, opt| obs.respond_to?(:observable_array) && obs.observable_array == self}.each do |o|
      o.deregister_all_observables if o.respond_to?(:deregister_all_observables)
      @array_object_observers.reject! {|k, v| v == o}
    end
  end
end

#remove_element_observers(element) ⇒ Object



110
111
112
113
114
# File 'lib/glimmer/data_binding/observable_array.rb', line 110

def remove_element_observers(element)
  property_observer_list.each do |observer, options|
    remove_element_observer(element, observer)
  end
end

#remove_observer(observer, *args) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/glimmer/data_binding/observable_array.rb', line 93

def remove_observer(observer, *args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  element_properties = args
  element_properties = element_properties.flatten.compact.uniq
  if !element_properties.empty?
    old_element_properties = element_properties_for(observer)
    observer_element_properties[observer] = Concurrent::Set.new(Concurrent::Array.new(element_properties_for(observer).to_a) - Concurrent::Array.new(element_properties)) # TODO remove this workaround when no longer needed (it is for jruby-9.3.2.0 issue)
    each { |element| element_properties.each { |property| observer.unobserve(element, property) } }
  end
  if element_properties_for(observer).empty?
    property_observer_list.delete(observer)
    observer_element_properties.delete(observer)
    each { |element| remove_element_observer(element, observer) }
  end
  observer
end

#replace(other_array) ⇒ Object



361
362
363
364
365
366
367
368
369
370
# File 'lib/glimmer/data_binding/observable_array.rb', line 361

def replace(other_array)
  old_array = Array.new(self)
  super(other_array).tap do
    (old_array - self).each do |old_value|
      unregister_dependent_observers(old_value)
      remove_element_observers(old_value)
    end
    notify_observers
  end
end

#reverse!Object



232
233
234
235
236
# File 'lib/glimmer/data_binding/observable_array.rb', line 232

def reverse!
  super.tap do
    notify_observers
  end
end

#rotate!(count = 1) ⇒ Object



270
271
272
# File 'lib/glimmer/data_binding/observable_array.rb', line 270

def rotate!(count=1)
  super(count).tap { notify_observers }
end

#select!(&block) ⇒ Object



274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/glimmer/data_binding/observable_array.rb', line 274

def select!(&block)
  if block_given?
    old_array = Array.new(self)
    super(&block).tap do
      (old_array - self).each do |old_value|
        unregister_dependent_observers(old_value)
        remove_element_observers(old_value)
      end
      notify_observers
    end
  else
    super
  end
end

#shiftObject



181
182
183
184
185
186
187
188
# File 'lib/glimmer/data_binding/observable_array.rb', line 181

def shift
  shifted_element = first
  unregister_dependent_observers(shifted_element)
  remove_element_observers(shifted_element)
  super.tap do
    notify_observers
  end
end

#shuffle!(hash = nil) ⇒ Object



304
305
306
# File 'lib/glimmer/data_binding/observable_array.rb', line 304

def shuffle!(hash = nil)
  (hash.nil? ? super() : super(random: hash[:random])).tap { notify_observers }
end

#slice!(arg1, arg2 = nil) ⇒ Object



308
309
310
311
312
313
314
315
316
317
# File 'lib/glimmer/data_binding/observable_array.rb', line 308

def slice!(arg1, arg2=nil)
  old_array = Array.new(self)
  (arg2.nil? ? super(arg1) : super(arg1, arg2)).tap do
    (old_array - self).each do |old_value|
      unregister_dependent_observers(old_value)
      remove_element_observers(old_value)
    end
    notify_observers
  end
end

#sort!(&block) ⇒ Object



319
320
321
# File 'lib/glimmer/data_binding/observable_array.rb', line 319

def sort!(&block)
  (block.nil? ? super() : super(&block)).tap { notify_observers }
end

#sort_by!(&block) ⇒ Object



323
324
325
# File 'lib/glimmer/data_binding/observable_array.rb', line 323

def sort_by!(&block)
  (block.nil? ? super() : super(&block)).tap { notify_observers }
end

#uniq!(&block) ⇒ Object



327
328
329
330
331
332
333
334
335
336
# File 'lib/glimmer/data_binding/observable_array.rb', line 327

def uniq!(&block)
  each do |old_value|
    unregister_dependent_observers(old_value)
    remove_element_observers(old_value)
  end
  (block.nil? ? super() : super(&block)).tap do
    each { |element| add_element_observers(element) }
    notify_observers
  end
end

#unregister_dependent_observers(old_value) ⇒ Object Also known as: deregister_dependent_observers



372
373
374
375
# File 'lib/glimmer/data_binding/observable_array.rb', line 372

def unregister_dependent_observers(old_value)
  return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray)
  property_observer_list.each { |observer, options| observer.unregister_dependents_with_observable(observer.registration_for(self), old_value) }
end

#unshift(element) ⇒ Object Also known as: prepend



338
339
340
341
342
343
# File 'lib/glimmer/data_binding/observable_array.rb', line 338

def unshift(element)
  super(element).tap do
    add_element_observers(element)
    notify_observers
  end
end