glimmer 2.3.0 → 2.5.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +234 -144
- data/VERSION +1 -1
- data/glimmer.gemspec +8 -7
- data/lib/glimmer/data_binding/model_binding.rb +30 -14
- data/lib/glimmer/data_binding/observable_array.rb +31 -22
- data/lib/glimmer/data_binding/observable_hash.rb +12 -63
- data/lib/glimmer/data_binding/observable_hashable.rb +75 -0
- data/lib/glimmer/data_binding/observable_model.rb +33 -39
- data/lib/glimmer/data_binding/observer.rb +51 -38
- metadata +11 -9
@@ -30,9 +30,9 @@ module Glimmer
|
|
30
30
|
|
31
31
|
attr_reader :binding_options, :property_name_expression
|
32
32
|
|
33
|
-
def initialize(
|
34
|
-
|
35
|
-
@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
|
-
|
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.
|
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
|
@@ -45,33 +45,41 @@ module Glimmer
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
def add_observer(observer, *
|
48
|
+
def add_observer(observer, *args)
|
49
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
50
|
+
element_properties = args
|
49
51
|
element_properties = element_properties.flatten.compact.uniq
|
50
52
|
return observer if has_observer?(observer) && has_observer_element_properties?(observer, element_properties)
|
51
|
-
property_observer_list
|
53
|
+
property_observer_list[observer] = options
|
52
54
|
observer_element_properties[observer] = element_properties_for(observer) + Concurrent::Set.new(element_properties)
|
53
|
-
|
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) }
|
54
60
|
observer
|
55
61
|
end
|
56
62
|
|
57
|
-
def add_element_observers(element)
|
58
|
-
property_observer_list.each do |observer|
|
59
|
-
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))
|
60
66
|
end
|
61
67
|
end
|
62
68
|
|
63
|
-
def add_element_observer(element, observer)
|
69
|
+
def add_element_observer(element, observer, options = {})
|
64
70
|
element_properties_for(observer).each do |property|
|
65
|
-
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)
|
66
75
|
end
|
67
|
-
ensure_array_object_observer(element) if element.is_a?(Array)
|
68
76
|
end
|
69
77
|
|
70
|
-
def ensure_array_object_observer(object)
|
78
|
+
def ensure_array_object_observer(object, options)
|
71
79
|
return unless object&.is_a?(Array)
|
72
80
|
array_object_observer = array_object_observer_for(object)
|
73
|
-
array_observer_registration = array_object_observer.observe(object)
|
74
|
-
property_observer_list.each do |observer|
|
81
|
+
array_observer_registration = array_object_observer.observe(object, options)
|
82
|
+
property_observer_list.each do |observer, options|
|
75
83
|
my_registration = observer.registration_for(self)
|
76
84
|
observer.add_dependent(my_registration => array_observer_registration)
|
77
85
|
end
|
@@ -83,7 +91,9 @@ module Glimmer
|
|
83
91
|
@array_object_observers[object]
|
84
92
|
end
|
85
93
|
|
86
|
-
def remove_observer(observer, *
|
94
|
+
def remove_observer(observer, *args)
|
95
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
96
|
+
element_properties = args
|
87
97
|
element_properties = element_properties.flatten.compact.uniq
|
88
98
|
if !element_properties.empty?
|
89
99
|
old_element_properties = element_properties_for(observer)
|
@@ -99,7 +109,7 @@ module Glimmer
|
|
99
109
|
end
|
100
110
|
|
101
111
|
def remove_element_observers(element)
|
102
|
-
property_observer_list.each do |observer|
|
112
|
+
property_observer_list.each do |observer, options|
|
103
113
|
remove_element_observer(element, observer)
|
104
114
|
end
|
105
115
|
end
|
@@ -110,17 +120,15 @@ module Glimmer
|
|
110
120
|
end
|
111
121
|
if element.is_a?(ObservableArray)
|
112
122
|
array_object_observer_for(element).unobserve(element)
|
113
|
-
element.property_observer_list.select {|
|
114
|
-
o.
|
115
|
-
registration.deregister
|
116
|
-
end
|
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)
|
117
125
|
@array_object_observers.reject! {|k, v| v == o}
|
118
126
|
end
|
119
127
|
end
|
120
128
|
end
|
121
129
|
|
122
130
|
def has_observer?(observer)
|
123
|
-
property_observer_list.include?(observer)
|
131
|
+
property_observer_list.keys.include?(observer)
|
124
132
|
end
|
125
133
|
|
126
134
|
def has_observer_element_properties?(observer, element_properties)
|
@@ -128,7 +136,7 @@ module Glimmer
|
|
128
136
|
end
|
129
137
|
|
130
138
|
def property_observer_list
|
131
|
-
@property_observer_list ||= Concurrent::
|
139
|
+
@property_observer_list ||= Concurrent::Hash.new
|
132
140
|
end
|
133
141
|
|
134
142
|
def observer_element_properties
|
@@ -140,7 +148,7 @@ module Glimmer
|
|
140
148
|
end
|
141
149
|
|
142
150
|
def notify_observers
|
143
|
-
property_observer_list.to_a.each { |
|
151
|
+
property_observer_list.to_a.each { |obs, opt| obs.call(self) }
|
144
152
|
end
|
145
153
|
|
146
154
|
def <<(element)
|
@@ -245,6 +253,7 @@ module Glimmer
|
|
245
253
|
alias map! collect!
|
246
254
|
|
247
255
|
def compact!
|
256
|
+
# TODO consider checking which exact indices changed and only notifying if there is a change
|
248
257
|
super.tap { notify_observers }
|
249
258
|
end
|
250
259
|
|
@@ -363,7 +372,7 @@ module Glimmer
|
|
363
372
|
|
364
373
|
def unregister_dependent_observers(old_value)
|
365
374
|
return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray)
|
366
|
-
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) }
|
367
376
|
end
|
368
377
|
alias deregister_dependent_observers unregister_dependent_observers
|
369
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/
|
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
|
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
|
-
|
44
|
-
if
|
45
|
-
|
46
|
-
|
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/
|
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
|
28
|
+
include ObservableHashable
|
29
29
|
|
30
30
|
class Notifier
|
31
31
|
include Observer
|
@@ -40,26 +40,27 @@ 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
|
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
|
+
open_struct_loaded = !!::OpenStruct rescue false
|
59
|
+
add_key_writer_observer(property_name, options) if is_a?(Struct) || (open_struct_loaded && is_a?(OpenStruct))
|
59
60
|
observer
|
60
61
|
end
|
61
|
-
|
62
|
-
def remove_observer(observer, property_name)
|
62
|
+
|
63
|
+
def remove_observer(observer, property_name, options = {})
|
63
64
|
if has_observer?(observer, property_name)
|
64
65
|
property_observer_list(property_name).delete(observer)
|
65
66
|
observer.unobserve(self, property_name)
|
@@ -67,10 +68,11 @@ module Glimmer
|
|
67
68
|
end
|
68
69
|
|
69
70
|
def remove_observers(property_name)
|
70
|
-
|
71
|
+
property_key = property_name&.to_sym
|
72
|
+
property_observer_hash[property_key].each do |observer|
|
71
73
|
remove_observer(observer, property_name)
|
72
74
|
end
|
73
|
-
property_observer_hash.delete(
|
75
|
+
property_observer_hash.delete(property_key)
|
74
76
|
end
|
75
77
|
|
76
78
|
def remove_all_observers
|
@@ -95,24 +97,32 @@ module Glimmer
|
|
95
97
|
end
|
96
98
|
|
97
99
|
def property_observer_list(property_name)
|
98
|
-
|
99
|
-
property_observer_hash[
|
100
|
+
property_key = property_name&.to_sym
|
101
|
+
property_observer_hash[property_key] = Concurrent::Set.new unless property_observer_hash[property_key]
|
102
|
+
property_observer_hash[property_key]
|
100
103
|
end
|
104
|
+
alias key_observer_list property_observer_list
|
105
|
+
|
106
|
+
def all_property_observer_list
|
107
|
+
property_observer_list(nil)
|
108
|
+
end
|
109
|
+
alias all_key_observer_list all_property_observer_list
|
101
110
|
|
102
111
|
def notify_observers(property_name)
|
103
112
|
property_observer_list(property_name).to_a.each { |observer| observer.call(send(property_name)) }
|
104
113
|
end
|
105
|
-
|
106
|
-
def add_property_writer_observers(property_name)
|
114
|
+
|
115
|
+
def add_property_writer_observers(property_name, options)
|
107
116
|
property_writer_name = "#{property_name}="
|
108
117
|
method(property_writer_name)
|
109
|
-
ensure_array_object_observer(property_name, send(property_name))
|
110
|
-
ensure_hash_object_observer(property_name, send(property_name))
|
118
|
+
ensure_array_object_observer(property_name, send(property_name), nil, options)
|
111
119
|
begin
|
112
120
|
method("__original__#{property_writer_name}")
|
113
121
|
rescue
|
114
122
|
define_singleton_method("__original__#{property_writer_name}", property_writer_method(property_writer_name))
|
115
|
-
|
123
|
+
# 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
|
124
|
+
# It is good enough for now. If there is a need to address this in the future, this is where to start the work
|
125
|
+
define_singleton_method(property_writer_name, &PROPERTY_WRITER_FACTORY.call(property_name, options))
|
116
126
|
end
|
117
127
|
rescue => e
|
118
128
|
#ignore writing if no property writer exists
|
@@ -130,10 +140,11 @@ module Glimmer
|
|
130
140
|
end
|
131
141
|
alias deregister_dependent_observers unregister_dependent_observers
|
132
142
|
|
133
|
-
def ensure_array_object_observer(property_name, object, old_object = nil)
|
143
|
+
def ensure_array_object_observer(property_name, object, old_object = nil, options = nil)
|
144
|
+
options ||= {}
|
134
145
|
return unless object&.is_a?(Array)
|
135
146
|
array_object_observer = array_object_observer_for(property_name)
|
136
|
-
array_observer_registration = array_object_observer.observe(object)
|
147
|
+
array_observer_registration = array_object_observer.observe(object, options)
|
137
148
|
property_observer_list(property_name).each do |observer|
|
138
149
|
my_registration = observer.registration_for(self, property_name) # TODO eliminate repetition
|
139
150
|
observer.add_dependent(my_registration => array_observer_registration)
|
@@ -146,23 +157,6 @@ module Glimmer
|
|
146
157
|
@array_object_observers[property_name] = Notifier.new(self, property_name) unless @array_object_observers.has_key?(property_name)
|
147
158
|
@array_object_observers[property_name]
|
148
159
|
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] = Notifier.new(self, property_name) unless @hash_object_observers.has_key?(property_name)
|
164
|
-
@hash_object_observers[property_name]
|
165
|
-
end
|
166
160
|
end
|
167
161
|
end
|
168
162
|
end
|