Class: Glimmer::DataBinding::ModelBinding
- Inherits:
-
Object
- Object
- Glimmer::DataBinding::ModelBinding
show all
- Includes:
- Observable, Observer
- Defined in:
- lib/glimmer/data_binding/model_binding.rb
Constant Summary
collapse
- ARRAY_INDEXED_PROPERTY_ARGUMENT_REGEX =
/\d+/
- HASH_SYMBOL_INDEXED_PROPERTY_ARGUMENT_REGEX =
/:[^:]+/
- HASH_SINGLE_QUOTE_INDEXED_PROPERTY_ARGUMENT_REGEX =
/'[^']+'/
- HASH_DOUBLE_QUOTE_INDEXED_PROPERTY_ARGUMENT_REGEX =
/"[^"]+"/
Instance Attribute Summary collapse
Instance Method Summary
collapse
Methods included from Observer
#add_dependent, #compact_args, #dependents, #dependents_for, #observe, proc, #registration_for, #registrations, #remove_dependent, #unobserve, #unobserve_all_observables, #unobserve_dependents_with_observable
Methods included from Observable
#inspect
Constructor Details
Returns a new instance of ModelBinding.
38
39
40
41
42
43
44
45
46
47
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 38
def initialize(*args)
binding_options = args.pop if args.size > 1 && args.last.is_a?(Hash)
@base_model, @property_name_expression = args
@binding_options = binding_options || Concurrent::Hash.new
if computed?
@computed_model_bindings = Concurrent::Array.new(computed_by.map do |computed_by_property_expression|
self.class.new(base_model, computed_by_property_expression)
end)
end
end
|
Instance Attribute Details
#binding_options ⇒ Object
Returns the value of attribute binding_options.
36
37
38
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 36
def binding_options
@binding_options
end
|
#property_name_expression ⇒ Object
Returns the value of attribute property_name_expression.
36
37
38
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 36
def property_name_expression
@property_name_expression
end
|
Instance Method Details
#add_computed_observers(observer) ⇒ Object
180
181
182
183
184
185
186
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 180
def add_computed_observers(observer)
@computed_model_bindings.each do |computed_model_binding|
observer_registration = computed_observer_for(observer).observe(computed_model_binding, observation_options)
my_registration = observer.registration_for(self)
observer.add_dependent(my_registration => observer_registration)
end
end
|
#add_nested_observers(observer) ⇒ Object
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 188
def add_nested_observers(observer)
nested_property_observers = nested_property_observers_for(observer)
Concurrent::Array.new(nested_models.zip(nested_property_names)).each_with_index do |zip, i|
model, property_name = zip
nested_property_observer = nested_property_observers[property_name]
previous_index = i - 1
if previous_index.negative?
parent_model = self
parent_property_name = nil
parent_observer = observer
else
parent_model = nested_models[previous_index]
parent_property_name = nested_property_names[previous_index]
parent_observer = nested_property_observers[parent_property_name]
end
parent_property_name = nil if parent_property_name.to_s.start_with?('[')
unless model.nil?
observer_registration = property_indexed?(property_name) ? nested_property_observer.observe(model, observation_options) : nested_property_observer.observe(model, property_name, observation_options)
parent_registration = parent_observer.registration_for(parent_model, *[parent_property_name].compact)
parent_observer.add_dependent(parent_registration => observer_registration)
end
end
end
|
#add_observer(observer, extra_options = {}) ⇒ Object
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 133
def add_observer(observer, = {})
if computed?
add_computed_observers(observer)
elsif nested_property?
add_nested_observers(observer)
else
model_binding_observer = Observer.proc do |new_value|
converted_value = evaluate_property
observer.call(converted_value).tap do
apply_processor(@binding_options[:after_read], converted_value)
end
end
observer_registration = model_binding_observer.observe(*([model] + [property_name, observation_options].compact))
my_registration = observer.registration_for(self)
observer.add_dependent(my_registration => observer_registration)
end
end
|
#base_model ⇒ Object
70
71
72
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 70
def base_model
@base_model
end
|
#call(value, *extra_args) ⇒ Object
213
214
215
216
217
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 213
def call(value, *)
return if model.nil?
converted_value = value
invoke_property_writer(model, model.is_a?(Hash) && !property_indexed?(property_name) ? property_name : "#{property_name}=", converted_value) unless converted_value == evaluate_property || property_name.nil?
end
|
#computed? ⇒ Boolean
102
103
104
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 102
def computed?
!computed_by.empty?
end
|
#computed_by ⇒ Object
106
107
108
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 106
def computed_by
Concurrent::Array.new([@binding_options[:computed_by]].flatten.compact)
end
|
#computed_observer_for(observer) ⇒ Object
167
168
169
170
171
172
173
174
175
176
177
178
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 167
def computed_observer_for(observer)
@computed_observer_collection ||= Concurrent::Hash.new
unless @computed_observer_collection.has_key?(observer)
@computed_observer_collection[observer] = Observer.proc do |new_value|
converted_value = evaluate_property
observer.call(converted_value).tap do
apply_processor(@binding_options[:after_read], converted_value)
end
end
end
@computed_observer_collection[observer]
end
|
#evaluate_options_property ⇒ Object
226
227
228
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 226
def evaluate_options_property
model.send(options_property_name) unless model.nil?
end
|
#evaluate_property ⇒ Object
219
220
221
222
223
224
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 219
def evaluate_property
value = nil
value = invoke_property_reader(model, property_name) unless model.nil?
apply_processor(@binding_options[:before_read], value)
convert_on_read(value)
end
|
#model ⇒ Object
49
50
51
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 49
def model
nested_property? ? nested_model : base_model
end
|
#model_property_names ⇒ Object
Model representing nested property names e.g. property name expression “address.state” gives [‘address’]
94
95
96
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 94
def model_property_names
Concurrent::Array.new(nested_property_names[0...-1])
end
|
#nested_model ⇒ Object
66
67
68
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 66
def nested_model
nested_models.last
end
|
#nested_models ⇒ Object
e.g. person.address.state returns [person, person.address]
54
55
56
57
58
59
60
61
62
63
64
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 54
def nested_models
@nested_models = Concurrent::Array.new([base_model])
model_property_names.reduce(base_model) do |reduced_model, nested_model_property_name|
if !reduced_model.nil?
invoke_property_reader(reduced_model, nested_model_property_name).tap do |new_reduced_model|
@nested_models << new_reduced_model
end
end
end
@nested_models
end
|
#nested_property? ⇒ Boolean
98
99
100
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 98
def nested_property?
property_name_expression.to_s.match(/[.\[]/)
end
|
#nested_property_name ⇒ Object
Final nested property name e.g. property name expression “address.state” gives :state
88
89
90
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 88
def nested_property_name
nested_property_names.last
end
|
#nested_property_names ⇒ Object
All nested property names e.g. property name expression “address.state” gives [‘address’, ‘state’] If there are any indexed property names, this returns indexes as properties. e.g. property name expression “addresses.state” gives [‘addresses’, ‘[1]’, ‘state’]
82
83
84
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 82
def nested_property_names
@nested_property_names ||= Concurrent::Array.new(property_name_expression.split(/\[|\./).map {|pne| pne.end_with?(']') ? "[#{pne}" : pne }.reject {|pne| pne.empty? })
end
|
#nested_property_observers_for(observer) ⇒ Object
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 114
def nested_property_observers_for(observer)
@nested_property_observers_collection ||= Concurrent::Hash.new
unless @nested_property_observers_collection.has_key?(observer)
@nested_property_observers_collection[observer] = nested_property_names.reduce(Concurrent::Hash.new) do |output, property_name|
output.merge(
property_name => Observer.proc do |new_value|
add_observer(observer)
converted_value = evaluate_property
observer.call(converted_value).tap do
apply_processor(@binding_options[:after_read], converted_value)
end
end
)
end
end
@nested_property_observers_collection[observer]
end
|
#observation_options ⇒ Object
110
111
112
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 110
def observation_options
@binding_options.slice(:recursive)
end
|
#options_property_name ⇒ Object
230
231
232
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 230
def options_property_name
self.property_name + "_options"
end
|
#property_indexed?(property_expression) ⇒ Boolean
234
235
236
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 234
def property_indexed?(property_expression)
property_expression.to_s.start_with?('[')
end
|
#property_name ⇒ Object
74
75
76
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 74
def property_name
nested_property? ? nested_property_name : property_name_expression
end
|
#remove_observer(observer, extra_options = {}) ⇒ Object
153
154
155
156
157
158
159
160
161
162
163
164
165
|
# File 'lib/glimmer/data_binding/model_binding.rb', line 153
def remove_observer(observer, = {})
if computed?
@computed_model_bindings.each do |computed_model_binding|
computed_observer_for(observer).unobserve(computed_model_binding)
end
@computed_observer_collection.delete(observer)
elsif nested_property?
nested_property_observers_for(observer).clear
else
observer.unobserve(model, property_name)
end
end
|