Module: Glimmer::DataBinding::ObservableHash

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

Defined Under Namespace

Classes: Notifier

Constant Summary collapse

OBSERVED_STORE_METHOD =
lambda do |key, value|
  if key_observer_list(key).empty?
    if all_key_observer_list.empty?
      self.send('__original__store', key, value)
    else
      old_value = self[key]
      unregister_dependent_observers(nil, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
      self.send('__original__store', key, value)
      notify_observers(key)
      ensure_array_object_observer(nil, value, old_value)
      ensure_hash_object_observer(nil, value, old_value)
    end
  else
    old_value = self[key]
    unregister_dependent_observers(key, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
    self.send('__original__store', key, value)
    notify_observers(key)
    ensure_array_object_observer(key, value, old_value)
    ensure_hash_object_observer(key, value, old_value)
  end
end

Instance Method Summary collapse

Methods included from Observable

#inspect

Instance Method Details

#add_key_writer_observer(key = nil) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/glimmer/data_binding/observable_hash.rb', line 121

def add_key_writer_observer(key = nil)
  ensure_array_object_observer(key, self[key])
  ensure_hash_object_observer(key, self[key])
  begin
    method('__original__store')
  rescue
    define_singleton_method('__original__store', store_method)
    define_singleton_method('[]=', &OBSERVED_STORE_METHOD)
  end
rescue => e
  #ignore writing if no key writer exists
  Glimmer::Config.logger.debug {"No need to observe store method: '[]='\n#{e.message}\n#{e.backtrace.join("\n")}"}
end

#add_observer(observer, key = nil) ⇒ Object



65
66
67
68
69
70
# File 'lib/glimmer/data_binding/observable_hash.rb', line 65

def add_observer(observer, key = nil)
  return observer if has_observer?(observer, key)
  key_observer_list(key) << observer
  add_key_writer_observer(key)
  observer
end

#all_key_observer_listObject



112
113
114
# File 'lib/glimmer/data_binding/observable_hash.rb', line 112

def all_key_observer_list
  key_observer_list(nil)
end

#array_object_observer_for(key) ⇒ Object



157
158
159
160
161
# File 'lib/glimmer/data_binding/observable_hash.rb', line 157

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

#ensure_array_object_observer(key, object, old_object = nil) ⇒ Object



146
147
148
149
150
151
152
153
154
155
# File 'lib/glimmer/data_binding/observable_hash.rb', line 146

def ensure_array_object_observer(key, object, old_object = nil)
  return unless object&.is_a?(Array)
  array_object_observer = array_object_observer_for(key)
  array_observer_registration = array_object_observer.observe(object)
  key_observer_list(key).each do |observer|
    my_registration = observer.registration_for(self, key) # TODO eliminate repetition
    observer.add_dependent(my_registration => array_observer_registration)
  end
  array_object_observer_for(key).unregister(old_object) if old_object.is_a?(ObservableArray)
end

#ensure_hash_object_observer(key, object, old_object = nil) ⇒ Object



163
164
165
166
167
168
169
170
171
172
# File 'lib/glimmer/data_binding/observable_hash.rb', line 163

def ensure_hash_object_observer(key, object, old_object = nil)
  return unless object&.is_a?(Hash)
  hash_object_observer = hash_object_observer_for(key)
  hash_observer_registration = hash_object_observer.observe(object)
  key_observer_list(key).each do |observer|
    my_registration = observer.registration_for(self, key) # TODO eliminate repetition
    observer.add_dependent(my_registration => hash_observer_registration)
  end
  hash_object_observer_for(key).unregister(old_object) if old_object.is_a?(ObservableHash)
end

#has_observer?(observer, key = nil) ⇒ Boolean

Returns:

  • (Boolean)


95
96
97
# File 'lib/glimmer/data_binding/observable_hash.rb', line 95

def has_observer?(observer, key = nil)
  key_observer_list(key).include?(observer)
end

#has_observer_for_any_key?(observer) ⇒ Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/glimmer/data_binding/observable_hash.rb', line 99

def has_observer_for_any_key?(observer)
  key_observer_hash.values.map(&:to_a).reduce(:+).include?(observer)
end

#hash_object_observer_for(key) ⇒ Object



174
175
176
177
178
# File 'lib/glimmer/data_binding/observable_hash.rb', line 174

def hash_object_observer_for(key)
  @hash_object_observers ||= Concurrent::Hash.new
  @hash_object_observers[key] = ObservableModel::Notifier.new(self, key) unless @hash_object_observers.has_key?(key)
  @hash_object_observers[key]
end

#key_observer_hashObject



103
104
105
# File 'lib/glimmer/data_binding/observable_hash.rb', line 103

def key_observer_hash
  @key_observers ||= Hash.new
end

#key_observer_list(key) ⇒ Object



107
108
109
110
# File 'lib/glimmer/data_binding/observable_hash.rb', line 107

def key_observer_list(key)
  key_observer_hash[key] = Concurrent::Set.new unless key_observer_hash[key]
  key_observer_hash[key]
end

#notify_observers(key) ⇒ Object



116
117
118
119
# File 'lib/glimmer/data_binding/observable_hash.rb', line 116

def notify_observers(key)
  all_key_observer_list.to_a.each { |observer| observer.call(self[key], key) }
  (key_observer_list(key).to_a - all_key_observer_list.to_a).each { |observer| observer.call(self[key]) }
end

#remove_all_observersObject



86
87
88
89
90
91
92
93
# File 'lib/glimmer/data_binding/observable_hash.rb', line 86

def remove_all_observers
  all_observers = key_observer_hash.clone
  key_observer_hash.keys.each do |key|
    remove_observers(key)
  end
  key_observer_hash.clear
  all_observers
end

#remove_observer(observer, key = nil) ⇒ Object



72
73
74
75
76
77
# File 'lib/glimmer/data_binding/observable_hash.rb', line 72

def remove_observer(observer, key = nil)
  if has_observer?(observer, key)
    key_observer_list(key).delete(observer)
    observer.unobserve(self, key)
  end
end

#remove_observers(key) ⇒ Object



79
80
81
82
83
84
# File 'lib/glimmer/data_binding/observable_hash.rb', line 79

def remove_observers(key)
  key_observer_hash[key].each do |observer|
    remove_observer(observer, key)
  end
  key_observer_hash.delete(key)
end

#store_methodObject



135
136
137
# File 'lib/glimmer/data_binding/observable_hash.rb', line 135

def store_method
  self.class.instance_method('[]=') rescue self.method('[]=')
end

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



139
140
141
142
143
# File 'lib/glimmer/data_binding/observable_hash.rb', line 139

def unregister_dependent_observers(key, old_value)
  # TODO look into optimizing this
  return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray) || old_value.is_a?(ObservableHash)
  key_observer_list(key).each { |observer| observer.unregister_dependents_with_observable(observer.registration_for(self, key), old_value) }
end