glimmer 2.2.2 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +64 -14
- 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 -40
- data/lib/glimmer/data_binding/observer.rb +51 -38
- metadata +13 -10
@@ -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
|
@@ -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, *
|
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
|
53
|
+
property_observer_list[observer] = options
|
37
54
|
observer_element_properties[observer] = element_properties_for(observer) + Concurrent::Set.new(element_properties)
|
38
|
-
|
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, *
|
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::
|
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 { |
|
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/
|
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,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
|
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
|
-
|
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(
|
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
|
-
|
99
|
-
property_observer_hash[
|
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
|
-
|
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] =
|
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
|