glimmer 2.2.2 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,9 +30,9 @@ module Glimmer
30
30
 
31
31
  attr_reader :binding_options, :property_name_expression
32
32
 
33
- def initialize(base_model, property_name_expression, binding_options = nil)
34
- @base_model = base_model
35
- @property_name_expression = property_name_expression
33
+ def initialize(*args)
34
+ binding_options = args.pop if args.size > 1 && args.last.is_a?(Hash)
35
+ @base_model, @property_name_expression = args
36
36
  @binding_options = binding_options || Concurrent::Hash.new
37
37
  if computed?
38
38
  @computed_model_bindings = Concurrent::Array.new(computed_by.map do |computed_by_property_expression|
@@ -91,7 +91,7 @@ module Glimmer
91
91
  end
92
92
 
93
93
  def nested_property?
94
- property_name_expression.match(/[.\[]/)
94
+ property_name_expression.to_s.match(/[.\[]/)
95
95
  end
96
96
 
97
97
  def computed?
@@ -101,6 +101,10 @@ module Glimmer
101
101
  def computed_by
102
102
  Concurrent::Array.new([@binding_options[:computed_by]].flatten.compact)
103
103
  end
104
+
105
+ def observation_options
106
+ @binding_options.slice(:recursive)
107
+ end
104
108
 
105
109
  def nested_property_observers_for(observer)
106
110
  @nested_property_observers_collection ||= Concurrent::Hash.new
@@ -121,7 +125,7 @@ module Glimmer
121
125
  @nested_property_observers_collection[observer]
122
126
  end
123
127
 
124
- def add_observer(observer)
128
+ def add_observer(observer, extra_options = {})
125
129
  if computed?
126
130
  add_computed_observers(observer)
127
131
  elsif nested_property?
@@ -133,19 +137,20 @@ module Glimmer
133
137
  apply_processor(@binding_options[:after_read], converted_value)
134
138
  end
135
139
  end
136
- observer_registration = model_binding_observer.observe(model, property_name)
140
+ observer_registration = model_binding_observer.observe(*[model, property_name, observation_options].compact)
137
141
  my_registration = observer.registration_for(self)
138
142
  observer.add_dependent(my_registration => observer_registration)
139
143
  end
140
144
  end
141
145
 
142
- def remove_observer(observer)
146
+ def remove_observer(observer, extra_options = {})
143
147
  if computed?
144
148
  @computed_model_bindings.each do |computed_model_binding|
145
149
  computed_observer_for(observer).unobserve(computed_model_binding)
146
150
  end
147
151
  @computed_observer_collection.delete(observer)
148
152
  elsif nested_property?
153
+ # No need to call remove_nested_observers(observer) (cleanup happens automatically indirectly when invoked through observer.unobserve(model_binding))
149
154
  nested_property_observers_for(observer).clear
150
155
  else
151
156
  observer.unobserve(model, property_name)
@@ -167,7 +172,7 @@ module Glimmer
167
172
 
168
173
  def add_computed_observers(observer)
169
174
  @computed_model_bindings.each do |computed_model_binding|
170
- observer_registration = computed_observer_for(observer).observe(computed_model_binding)
175
+ observer_registration = computed_observer_for(observer).observe(computed_model_binding, observation_options)
171
176
  my_registration = observer.registration_for(self)
172
177
  observer.add_dependent(my_registration => observer_registration)
173
178
  end
@@ -191,17 +196,17 @@ module Glimmer
191
196
  parent_property_name = nil if parent_property_name.to_s.start_with?('[')
192
197
  unless model.nil?
193
198
  # TODO figure out a way to deal with this more uniformly
194
- observer_registration = property_indexed?(property_name) ? nested_property_observer.observe(model) : nested_property_observer.observe(model, property_name)
195
- parent_registration = parent_observer.registration_for(parent_model, parent_property_name)
199
+ observer_registration = property_indexed?(property_name) ? nested_property_observer.observe(model, observation_options) : nested_property_observer.observe(model, property_name, observation_options)
200
+ parent_registration = parent_observer.registration_for(parent_model, *[parent_property_name].compact)
196
201
  parent_observer.add_dependent(parent_registration => observer_registration)
197
202
  end
198
203
  end
199
204
  end
200
-
205
+
201
206
  def call(value, *extra_args)
202
207
  return if model.nil?
203
208
  converted_value = value
204
- invoke_property_writer(model, "#{property_name}=", converted_value) unless converted_value == evaluate_property
209
+ invoke_property_writer(model, model.is_a?(Hash) ? property_name : "#{property_name}=", converted_value) unless converted_value == evaluate_property || property_name.nil?
205
210
  end
206
211
 
207
212
  def evaluate_property
@@ -258,11 +263,18 @@ module Glimmer
258
263
  property_argument = property_argument.to_i if property_argument.match(/\d+/)
259
264
  object.send(property_method, property_argument)
260
265
  else
261
- object.send(property_expression)
266
+ if property_expression.nil?
267
+ object
268
+ elsif object.is_a?(Hash)
269
+ object[property_expression]
270
+ else
271
+ object.send(property_expression)
272
+ end
262
273
  end
263
274
  end
264
275
 
265
276
  def invoke_property_writer(object, property_expression, value)
277
+ return if property_expression.nil?
266
278
  raise "Cannot invoke `#{property_expression}` because ModelBinding#binding_options[:read_only]=true" if @binding_options[:read_only]
267
279
  apply_processor(@binding_options[:before_write], value)
268
280
  converted_value = convert_on_write(value)
@@ -272,7 +284,11 @@ module Glimmer
272
284
  property_argument = property_argument.to_i if property_argument.match(/\d+/)
273
285
  object.send(property_method, property_argument, converted_value)
274
286
  else
275
- object.send(property_expression, converted_value)
287
+ if object.is_a?(Hash)
288
+ object[property_expression] = converted_value
289
+ else
290
+ object.send(property_expression, converted_value)
291
+ end
276
292
  end
277
293
  apply_processor(@binding_options[:after_write], converted_value)
278
294
  end
@@ -21,6 +21,7 @@
21
21
 
22
22
  require 'set'
23
23
  require 'glimmer/data_binding/observable'
24
+ require 'glimmer/data_binding/observer'
24
25
  require 'array_include_methods'
25
26
 
26
27
  using ArrayIncludeMethods
@@ -29,29 +30,70 @@ module Glimmer
29
30
  module DataBinding
30
31
  module ObservableArray
31
32
  include Observable
33
+
34
+ class Notifier
35
+ include Observer
36
+
37
+ attr_reader :observable_array
38
+
39
+ def initialize(observable_array)
40
+ @observable_array = observable_array
41
+ end
42
+
43
+ def call(new_value=nil, *extra_args)
44
+ @observable_array.notify_observers
45
+ end
46
+ end
32
47
 
33
- def add_observer(observer, *element_properties)
48
+ def add_observer(observer, *args)
49
+ options = args.last.is_a?(Hash) ? args.pop : {}
50
+ element_properties = args
34
51
  element_properties = element_properties.flatten.compact.uniq
35
52
  return observer if has_observer?(observer) && has_observer_element_properties?(observer, element_properties)
36
- property_observer_list << observer
53
+ property_observer_list[observer] = options
37
54
  observer_element_properties[observer] = element_properties_for(observer) + Concurrent::Set.new(element_properties)
38
- each { |element| add_element_observer(element, observer) }
55
+ if !options.empty? && options[:recursive].is_a?(Integer)
56
+ options = options.clone
57
+ options[:recursive] = options[:recursive] - 1
58
+ end
59
+ each { |element| add_element_observer(element, observer, options) }
39
60
  observer
40
61
  end
41
62
 
42
- def add_element_observers(element)
43
- property_observer_list.each do |observer|
44
- add_element_observer(element, observer)
63
+ def add_element_observers(element, general_options = {})
64
+ property_observer_list.each do |observer, options|
65
+ add_element_observer(element, observer, options.merge(general_options))
45
66
  end
46
67
  end
47
68
 
48
- def add_element_observer(element, observer)
69
+ def add_element_observer(element, observer, options = {})
49
70
  element_properties_for(observer).each do |property|
50
- observer.observe(element, property)
71
+ observer.observe(element, property, options)
72
+ end
73
+ if element.is_a?(Array) && (options[:recursive] == true || (options[:recursive].is_a?(Integer) && options[:recursive] >= 0))
74
+ ensure_array_object_observer(element, options)
51
75
  end
52
76
  end
77
+
78
+ def ensure_array_object_observer(object, options)
79
+ return unless object&.is_a?(Array)
80
+ array_object_observer = array_object_observer_for(object)
81
+ array_observer_registration = array_object_observer.observe(object, options)
82
+ property_observer_list.each do |observer, options|
83
+ my_registration = observer.registration_for(self)
84
+ observer.add_dependent(my_registration => array_observer_registration)
85
+ end
86
+ end
87
+
88
+ def array_object_observer_for(object)
89
+ @array_object_observers ||= Concurrent::Hash.new
90
+ @array_object_observers[object] = Notifier.new(self) unless @array_object_observers.has_key?(object)
91
+ @array_object_observers[object]
92
+ end
53
93
 
54
- def remove_observer(observer, *element_properties)
94
+ def remove_observer(observer, *args)
95
+ options = args.last.is_a?(Hash) ? args.pop : {}
96
+ element_properties = args
55
97
  element_properties = element_properties.flatten.compact.uniq
56
98
  if !element_properties.empty?
57
99
  old_element_properties = element_properties_for(observer)
@@ -67,7 +109,7 @@ module Glimmer
67
109
  end
68
110
 
69
111
  def remove_element_observers(element)
70
- property_observer_list.each do |observer|
112
+ property_observer_list.each do |observer, options|
71
113
  remove_element_observer(element, observer)
72
114
  end
73
115
  end
@@ -76,10 +118,17 @@ module Glimmer
76
118
  element_properties_for(observer).each do |property|
77
119
  observer.unobserve(element, property)
78
120
  end
121
+ if element.is_a?(ObservableArray)
122
+ array_object_observer_for(element).unobserve(element)
123
+ element.property_observer_list.select {|obs, opt| obs.respond_to?(:observable_array) && obs.observable_array == self}.each do |o|
124
+ o.deregister_all_observables if o.respond_to?(:deregister_all_observables)
125
+ @array_object_observers.reject! {|k, v| v == o}
126
+ end
127
+ end
79
128
  end
80
129
 
81
130
  def has_observer?(observer)
82
- property_observer_list.include?(observer)
131
+ property_observer_list.keys.include?(observer)
83
132
  end
84
133
 
85
134
  def has_observer_element_properties?(observer, element_properties)
@@ -87,7 +136,7 @@ module Glimmer
87
136
  end
88
137
 
89
138
  def property_observer_list
90
- @property_observer_list ||= Concurrent::Set.new
139
+ @property_observer_list ||= Concurrent::Hash.new
91
140
  end
92
141
 
93
142
  def observer_element_properties
@@ -99,7 +148,7 @@ module Glimmer
99
148
  end
100
149
 
101
150
  def notify_observers
102
- property_observer_list.to_a.each { |o| o.call(self) }
151
+ property_observer_list.to_a.each { |obs, opt| obs.call(self) }
103
152
  end
104
153
 
105
154
  def <<(element)
@@ -204,6 +253,7 @@ module Glimmer
204
253
  alias map! collect!
205
254
 
206
255
  def compact!
256
+ # TODO consider checking which exact indices changed and only notifying if there is a change
207
257
  super.tap { notify_observers }
208
258
  end
209
259
 
@@ -322,7 +372,7 @@ module Glimmer
322
372
 
323
373
  def unregister_dependent_observers(old_value)
324
374
  return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray)
325
- property_observer_list.each { |observer| observer.unregister_dependents_with_observable(observer.registration_for(self), old_value) }
375
+ property_observer_list.each { |observer, options| observer.unregister_dependents_with_observable(observer.registration_for(self), old_value) }
326
376
  end
327
377
  alias deregister_dependent_observers unregister_dependent_observers
328
378
  end
@@ -19,13 +19,13 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
- require 'glimmer/data_binding/observable'
22
+ require 'glimmer/data_binding/observable_hashable'
23
23
  require 'glimmer/data_binding/observer'
24
24
 
25
25
  module Glimmer
26
26
  module DataBinding
27
27
  module ObservableHash
28
- include Observable
28
+ include ObservableHashable
29
29
 
30
30
  class Notifier
31
31
  include Observer
@@ -40,36 +40,19 @@ module Glimmer
40
40
  end
41
41
  end
42
42
 
43
- OBSERVED_STORE_METHOD = lambda do |key, value|
44
- if key_observer_list(key).empty?
45
- if all_key_observer_list.empty?
46
- self.send('__original__store', key, value)
47
- else
48
- old_value = self[key]
49
- unregister_dependent_observers(nil, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
50
- self.send('__original__store', key, value)
51
- notify_observers(key)
52
- ensure_array_object_observer(nil, value, old_value)
53
- ensure_hash_object_observer(nil, value, old_value)
54
- end
55
- else
56
- old_value = self[key]
57
- unregister_dependent_observers(key, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
58
- self.send('__original__store', key, value)
59
- notify_observers(key)
60
- ensure_array_object_observer(key, value, old_value)
61
- ensure_hash_object_observer(key, value, old_value)
43
+ def add_observer(observer, key = nil, options = {})
44
+ if key.is_a?(Hash)
45
+ options = key
46
+ key = nil
62
47
  end
63
- end
64
-
65
- def add_observer(observer, key = nil)
66
48
  return observer if has_observer?(observer, key)
67
49
  key_observer_list(key) << observer
68
- add_key_writer_observer(key)
50
+ add_key_writer_observer(key, options)
69
51
  observer
70
52
  end
71
53
 
72
- def remove_observer(observer, key = nil)
54
+ def remove_observer(observer, key = nil, options = {})
55
+ old_value = self[key]
73
56
  if has_observer?(observer, key)
74
57
  key_observer_list(key).delete(observer)
75
58
  observer.unobserve(self, key)
@@ -118,24 +101,6 @@ module Glimmer
118
101
  (key_observer_list(key).to_a - all_key_observer_list.to_a).each { |observer| observer.call(self[key], key) }
119
102
  end
120
103
 
121
- def add_key_writer_observer(key = nil)
122
- ensure_array_object_observer(key, self[key])
123
- ensure_hash_object_observer(key, self[key])
124
- begin
125
- method('__original__store')
126
- rescue
127
- define_singleton_method('__original__store', store_method)
128
- define_singleton_method('[]=', &OBSERVED_STORE_METHOD)
129
- end
130
- rescue => e
131
- #ignore writing if no key writer exists
132
- Glimmer::Config.logger.debug {"No need to observe store method: '[]='\n#{e.message}\n#{e.backtrace.join("\n")}"}
133
- end
134
-
135
- def store_method
136
- self.class.instance_method('[]=') rescue self.method('[]=')
137
- end
138
-
139
104
  def unregister_dependent_observers(key, old_value)
140
105
  # TODO look into optimizing this
141
106
  return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray) || old_value.is_a?(ObservableHash)
@@ -143,10 +108,11 @@ module Glimmer
143
108
  end
144
109
  alias deregister_dependent_observers unregister_dependent_observers
145
110
 
146
- def ensure_array_object_observer(key, object, old_object = nil)
111
+ def ensure_array_object_observer(key, object, old_object = nil, options = {})
112
+ options ||= {}
147
113
  return unless object&.is_a?(Array)
148
114
  array_object_observer = array_object_observer_for(key)
149
- array_observer_registration = array_object_observer.observe(object)
115
+ array_observer_registration = array_object_observer.observe(object, options)
150
116
  key_observer_list(key).each do |observer|
151
117
  my_registration = observer.registration_for(self, key) # TODO eliminate repetition
152
118
  observer.add_dependent(my_registration => array_observer_registration)
@@ -160,23 +126,6 @@ module Glimmer
160
126
  @array_object_observers[key]
161
127
  end
162
128
 
163
- def ensure_hash_object_observer(key, object, old_object = nil)
164
- return unless object&.is_a?(Hash)
165
- hash_object_observer = hash_object_observer_for(key)
166
- hash_observer_registration = hash_object_observer.observe(object)
167
- key_observer_list(key).each do |observer|
168
- my_registration = observer.registration_for(self, key) # TODO eliminate repetition
169
- observer.add_dependent(my_registration => hash_observer_registration)
170
- end
171
- hash_object_observer_for(key).unregister(old_object) if old_object.is_a?(ObservableHash)
172
- end
173
-
174
- def hash_object_observer_for(key)
175
- @hash_object_observers ||= Concurrent::Hash.new
176
- @hash_object_observers[key] = ObservableModel::Notifier.new(self, key) unless @hash_object_observers.has_key?(key)
177
- @hash_object_observers[key]
178
- end
179
-
180
129
  def delete(key, &block)
181
130
  old_value = self[key]
182
131
  unless old_value.nil?
@@ -0,0 +1,75 @@
1
+ # Copyright (c) 2007-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/data_binding/observable'
23
+
24
+ module Glimmer
25
+ module DataBinding
26
+ # Represents a Hash-like object with attributes writable via :[]= store method like Hash, Struct, and OpenStruct
27
+ # Expects including class to have the following methods:
28
+ # - key_observer_list
29
+ # - all_key_observer_list
30
+ # - unregister_dependent_observer
31
+ # - ensure_array_object_observer
32
+ module ObservableHashable
33
+ include Observable
34
+
35
+ OBSERVED_STORE_METHOD = lambda do |options|
36
+ lambda do |key, value|
37
+ if key_observer_list(key).empty?
38
+ if all_key_observer_list.empty?
39
+ self.send('__original__store', key, value)
40
+ else
41
+ old_value = self[key]
42
+ unregister_dependent_observers(nil, old_value) # remove dependent observers previously installed in ensure_array_object_observer
43
+ self.send('__original__store', key, value)
44
+ notify_observers(key)
45
+ ensure_array_object_observer(nil, value, old_value, options)
46
+ end
47
+ else
48
+ old_value = self[key]
49
+ unregister_dependent_observers(key, old_value) # remove dependent observers previously installed in ensure_array_object_observer
50
+ self.send('__original__store', key, value)
51
+ notify_observers(key)
52
+ ensure_array_object_observer(key, value, old_value, options)
53
+ end
54
+ end
55
+ end
56
+
57
+ def add_key_writer_observer(key = nil, options)
58
+ ensure_array_object_observer(key, self[key], nil, options)
59
+ begin
60
+ method('__original__store')
61
+ rescue
62
+ define_singleton_method('__original__store', store_method)
63
+ define_singleton_method('[]=', &OBSERVED_STORE_METHOD.call(options))
64
+ end
65
+ rescue => e
66
+ #ignore writing if no key writer exists
67
+ Glimmer::Config.logger.debug {"No need to observe store method: '[]='\n#{e.message}\n#{e.backtrace.join("\n")}"}
68
+ end
69
+
70
+ def store_method
71
+ self.class.instance_method('[]=') rescue self.method('[]=')
72
+ end
73
+ end
74
+ end
75
+ end
@@ -19,13 +19,13 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
- require 'glimmer/data_binding/observable'
22
+ require 'glimmer/data_binding/observable_hashable'
23
23
  require 'glimmer/data_binding/observer'
24
24
 
25
25
  module Glimmer
26
26
  module DataBinding
27
27
  module ObservableModel
28
- include Observable
28
+ include ObservableHashable
29
29
 
30
30
  class Notifier
31
31
  include Observer
@@ -40,26 +40,26 @@ module Glimmer
40
40
  end
41
41
  end
42
42
 
43
- PROPERTY_WRITER_FACTORY = lambda do |property_name|
43
+ PROPERTY_WRITER_FACTORY = lambda do |property_name, options|
44
44
  property_writer_name = "#{property_name}="
45
45
  lambda do |value|
46
46
  old_value = self.send(property_name)
47
- unregister_dependent_observers(property_name, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
47
+ unregister_dependent_observers(property_name, old_value) # remove dependent observers previously installed in ensure_array_object_observer
48
48
  self.send("__original__#{property_writer_name}", value)
49
49
  notify_observers(property_name)
50
- ensure_array_object_observer(property_name, value, old_value)
51
- ensure_hash_object_observer(property_name, value, old_value)
50
+ ensure_array_object_observer(property_name, value, old_value, options)
52
51
  end
53
52
  end
54
-
55
- def add_observer(observer, property_name)
53
+
54
+ def add_observer(observer, property_name, options = {})
56
55
  return observer if has_observer?(observer, property_name)
57
56
  property_observer_list(property_name) << observer
58
- add_property_writer_observers(property_name)
57
+ add_property_writer_observers(property_name, options)
58
+ add_key_writer_observer(property_name, options) if is_a?(Struct) || is_a?(OpenStruct)
59
59
  observer
60
60
  end
61
-
62
- def remove_observer(observer, property_name)
61
+
62
+ def remove_observer(observer, property_name, options = {})
63
63
  if has_observer?(observer, property_name)
64
64
  property_observer_list(property_name).delete(observer)
65
65
  observer.unobserve(self, property_name)
@@ -67,10 +67,11 @@ module Glimmer
67
67
  end
68
68
 
69
69
  def remove_observers(property_name)
70
- property_observer_hash[property_name.to_sym].each do |observer|
70
+ property_key = property_name&.to_sym
71
+ property_observer_hash[property_key].each do |observer|
71
72
  remove_observer(observer, property_name)
72
73
  end
73
- property_observer_hash.delete(property_name.to_sym)
74
+ property_observer_hash.delete(property_key)
74
75
  end
75
76
 
76
77
  def remove_all_observers
@@ -95,24 +96,32 @@ module Glimmer
95
96
  end
96
97
 
97
98
  def property_observer_list(property_name)
98
- property_observer_hash[property_name.to_sym] = Concurrent::Set.new unless property_observer_hash[property_name.to_sym]
99
- property_observer_hash[property_name.to_sym]
99
+ property_key = property_name&.to_sym
100
+ property_observer_hash[property_key] = Concurrent::Set.new unless property_observer_hash[property_key]
101
+ property_observer_hash[property_key]
100
102
  end
103
+ alias key_observer_list property_observer_list
104
+
105
+ def all_property_observer_list
106
+ property_observer_list(nil)
107
+ end
108
+ alias all_key_observer_list all_property_observer_list
101
109
 
102
110
  def notify_observers(property_name)
103
111
  property_observer_list(property_name).to_a.each { |observer| observer.call(send(property_name)) }
104
112
  end
105
-
106
- def add_property_writer_observers(property_name)
113
+
114
+ def add_property_writer_observers(property_name, options)
107
115
  property_writer_name = "#{property_name}="
108
116
  method(property_writer_name)
109
- ensure_array_object_observer(property_name, send(property_name))
110
- ensure_hash_object_observer(property_name, send(property_name))
117
+ ensure_array_object_observer(property_name, send(property_name), nil, options)
111
118
  begin
112
119
  method("__original__#{property_writer_name}")
113
120
  rescue
114
121
  define_singleton_method("__original__#{property_writer_name}", property_writer_method(property_writer_name))
115
- define_singleton_method(property_writer_name, &PROPERTY_WRITER_FACTORY.call(property_name))
122
+ # Note the limitation that the first observe call options apply to all subsequent observations meaning even if unobserve was called, options do not change from initial ones
123
+ # It is good enough for now. If there is a need to address this in the future, this is where to start the work
124
+ define_singleton_method(property_writer_name, &PROPERTY_WRITER_FACTORY.call(property_name, options))
116
125
  end
117
126
  rescue => e
118
127
  #ignore writing if no property writer exists
@@ -130,10 +139,11 @@ module Glimmer
130
139
  end
131
140
  alias deregister_dependent_observers unregister_dependent_observers
132
141
 
133
- def ensure_array_object_observer(property_name, object, old_object = nil)
142
+ def ensure_array_object_observer(property_name, object, old_object = nil, options = nil)
143
+ options ||= {}
134
144
  return unless object&.is_a?(Array)
135
145
  array_object_observer = array_object_observer_for(property_name)
136
- array_observer_registration = array_object_observer.observe(object)
146
+ array_observer_registration = array_object_observer.observe(object, options)
137
147
  property_observer_list(property_name).each do |observer|
138
148
  my_registration = observer.registration_for(self, property_name) # TODO eliminate repetition
139
149
  observer.add_dependent(my_registration => array_observer_registration)
@@ -143,26 +153,9 @@ module Glimmer
143
153
 
144
154
  def array_object_observer_for(property_name)
145
155
  @array_object_observers ||= Concurrent::Hash.new
146
- @array_object_observers[property_name] = ObservableModel::Notifier.new(self, property_name) unless @array_object_observers.has_key?(property_name)
156
+ @array_object_observers[property_name] = Notifier.new(self, property_name) unless @array_object_observers.has_key?(property_name)
147
157
  @array_object_observers[property_name]
148
158
  end
149
-
150
- def ensure_hash_object_observer(property_name, object, old_object = nil)
151
- return unless object&.is_a?(Hash)
152
- hash_object_observer = hash_object_observer_for(property_name)
153
- hash_observer_registration = hash_object_observer.observe(object)
154
- property_observer_list(property_name).each do |observer|
155
- my_registration = observer.registration_for(self, property_name) # TODO eliminate repetition
156
- observer.add_dependent(my_registration => hash_observer_registration)
157
- end
158
- hash_object_observer_for(property_name).unregister(old_object) if old_object.is_a?(ObservableHash)
159
- end
160
-
161
- def hash_object_observer_for(property_name)
162
- @hash_object_observers ||= Concurrent::Hash.new
163
- @hash_object_observers[property_name] = ObservableModel::Notifier.new(self, property_name) unless @hash_object_observers.has_key?(property_name)
164
- @hash_object_observers[property_name]
165
- end
166
159
  end
167
160
  end
168
161
  end