Class: ModelBinding
- Inherits:
-
Object
show all
- Includes:
- Observer
- Defined in:
- lib/command_handlers/models/model_binding.rb
Constant Summary
collapse
- @@property_type_converters =
{
:undefined => lambda { |value| value },
:fixnum => lambda { |value| value.to_i },
:array => lambda { |value| value.to_a }
}
Instance Attribute Summary collapse
Instance Method Summary
collapse
Methods included from Observer
#add_dependent, #dependents, #dependents_for, #register, #registrations, #registrations_for, #remove_dependent, #unregister, #unregister_all_observables, #unregister_dependents_with_observable
Constructor Details
#initialize(base_model, property_name_expression, property_type = :undefined, binding_options = nil) ⇒ ModelBinding
Returns a new instance of ModelBinding.
13
14
15
16
17
18
19
20
21
22
23
24
|
# File 'lib/command_handlers/models/model_binding.rb', line 13
def initialize(base_model, property_name_expression, property_type = :undefined, binding_options = nil)
property_type = :undefined if property_type.nil?
@base_model = base_model
@property_name_expression = property_name_expression
@property_type = property_type
@binding_options = binding_options || {}
if computed?
@computed_model_bindings = computed_by.map do |computed_by_property_expression|
self.class.new(base_model, computed_by_property_expression, :undefined, computed_binding_options)
end
end
end
|
Instance Attribute Details
#binding_options ⇒ Object
Returns the value of attribute binding_options.
7
8
9
|
# File 'lib/command_handlers/models/model_binding.rb', line 7
def binding_options
@binding_options
end
|
#property_type ⇒ Object
Returns the value of attribute property_type.
7
8
9
|
# File 'lib/command_handlers/models/model_binding.rb', line 7
def property_type
@property_type
end
|
Instance Method Details
#add_computed_observers(observer) ⇒ Object
140
141
142
143
144
145
|
# File 'lib/command_handlers/models/model_binding.rb', line 140
def add_computed_observers(observer)
@computed_model_bindings.each do |computed_model_binding|
computed_observer_for(observer).observe(computed_model_binding)
observer.add_dependent([self, nil] => [computed_observer_for(observer), computed_model_binding, nil])
end
end
|
#add_nested_observers(observer) ⇒ Object
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
# File 'lib/command_handlers/models/model_binding.rb', line 146
def add_nested_observers(observer)
nested_property_observers = nested_property_observers_for(observer)
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
parent_model = previous_index.negative? ? self : nested_models[previous_index]
parent_property_name = previous_index.negative? ? nil : nested_property_names[previous_index]
parent_observer = previous_index.negative? ? observer : nested_property_observers[parent_property_name]
parent_property_name = nil if parent_property_name.to_s.start_with?('[')
unless model.nil?
if property_indexed?(property_name)
model.extend(ObservableArray) unless model.is_a?(ObservableArray)
nested_property_observer.observe(model) unless model.has_observer?(nested_property_observer)
parent_observer.add_dependent([parent_model, parent_property_name] => [nested_property_observer, model, nil])
else
model.extend(ObservableModel) unless model.is_a?(ObservableModel)
nested_property_observer.observe(model, property_name) unless model.has_observer?(nested_property_observer, property_name)
parent_observer.add_dependent([parent_model, parent_property_name] => [nested_property_observer, model, property_name])
end
end
end
end
|
#add_observer(observer) ⇒ Object
107
108
109
110
111
112
113
114
115
116
117
|
# File 'lib/command_handlers/models/model_binding.rb', line 107
def add_observer(observer)
if computed?
add_computed_observers(observer)
elsif nested_property?
add_nested_observers(observer)
else
model.extend(ObservableModel) unless model.is_a?(ObservableModel)
observer.observe(model, property_name)
observer.add_dependent([self, nil] => [observer, model, property_name])
end
end
|
#base_model ⇒ Object
45
46
47
|
# File 'lib/command_handlers/models/model_binding.rb', line 45
def base_model
@base_model
end
|
#computed? ⇒ Boolean
74
75
76
|
# File 'lib/command_handlers/models/model_binding.rb', line 74
def computed?
!!computed_by
end
|
#computed_binding_options ⇒ Object
80
81
82
|
# File 'lib/command_handlers/models/model_binding.rb', line 80
def computed_binding_options
@binding_options.reject {|k,v| k == :computed_by}
end
|
#computed_by ⇒ Object
77
78
79
|
# File 'lib/command_handlers/models/model_binding.rb', line 77
def computed_by
@binding_options[:computed_by]
end
|
#computed_observer_for(observer) ⇒ Object
131
132
133
134
135
136
137
138
139
|
# File 'lib/command_handlers/models/model_binding.rb', line 131
def computed_observer_for(observer)
@computed_observer_collection ||= {}
unless @computed_observer_collection.has_key?(observer)
@computed_observer_collection[observer] = BlockObserver.new do |changed_value|
observer.update(evaluate_property)
end
end
@computed_observer_collection[observer]
end
|
#evaluate_options_property ⇒ Object
177
178
179
|
# File 'lib/command_handlers/models/model_binding.rb', line 177
def evaluate_options_property
model.send(options_property_name) unless model.nil?
end
|
#evaluate_property ⇒ Object
174
175
176
|
# File 'lib/command_handlers/models/model_binding.rb', line 174
def evaluate_property
invoke_property_reader(model, property_name) unless model.nil?
end
|
#invoke_property_reader(object, property_expression) ⇒ Object
186
187
188
189
190
191
192
193
194
195
|
# File 'lib/command_handlers/models/model_binding.rb', line 186
def invoke_property_reader(object, property_expression)
if property_indexed?(property_expression)
property_method = '[]'
property_argument = property_expression[1...-1]
property_argument = property_argument.to_i if property_argument.match(/\d+/)
object.send(property_method, property_argument)
else
object.send(property_expression)
end
end
|
#invoke_property_writer(object, property_expression, value) ⇒ Object
196
197
198
199
200
201
202
203
204
205
|
# File 'lib/command_handlers/models/model_binding.rb', line 196
def invoke_property_writer(object, property_expression, value)
if property_indexed?(property_expression)
property_method = '[]='
property_argument = property_expression[1...-2]
property_argument = property_argument.to_i if property_argument.match(/\d+/)
object.send(property_method, property_argument, value)
else
object.send(property_expression, value)
end
end
|
#model ⇒ Object
25
26
27
|
# File 'lib/command_handlers/models/model_binding.rb', line 25
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]
65
66
67
|
# File 'lib/command_handlers/models/model_binding.rb', line 65
def model_property_names
nested_property_names[0...-1]
end
|
#nested_model ⇒ Object
42
43
44
|
# File 'lib/command_handlers/models/model_binding.rb', line 42
def nested_model
nested_models.last
end
|
#nested_models ⇒ Object
e.g. person.address.state returns [person, person.address]
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# File 'lib/command_handlers/models/model_binding.rb', line 29
def nested_models
@nested_models = [base_model]
model_property_names.reduce(base_model) do |reduced_model, nested_model_property_name|
if reduced_model.nil?
nil
else
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
68
69
70
|
# File 'lib/command_handlers/models/model_binding.rb', line 68
def nested_property?
property_name_expression.match(/[.\[]/)
end
|
#nested_property_name ⇒ Object
Final nested property name e.g. property name expression “address.state” gives :state
60
61
62
|
# File 'lib/command_handlers/models/model_binding.rb', line 60
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’]
55
56
57
|
# File 'lib/command_handlers/models/model_binding.rb', line 55
def nested_property_names
@nested_property_names ||= property_name_expression.split(".").map {|pne| pne.match(/([^\[]+)(\[[^\]]+\])?/).to_a.drop(1)}.flatten.compact
end
|
#nested_property_observers_for(observer) ⇒ Object
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
# File 'lib/command_handlers/models/model_binding.rb', line 83
def nested_property_observers_for(observer)
@nested_property_observers_collection ||= {}
unless @nested_property_observers_collection.has_key?(observer)
@nested_property_observers_collection[observer] = nested_property_names.reduce({}) do |output, property_name|
output.merge(
property_name => BlockObserver.new do |changed_value|
add_observer(observer)
observer.update(evaluate_property)
end
)
end
end
@nested_property_observers_collection[observer]
end
|
#options_property_name ⇒ Object
180
181
182
|
# File 'lib/command_handlers/models/model_binding.rb', line 180
def options_property_name
self.property_name + "_options"
end
|
#property_indexed?(property_expression) ⇒ Boolean
183
184
185
|
# File 'lib/command_handlers/models/model_binding.rb', line 183
def property_indexed?(property_expression)
property_expression.start_with?('[')
end
|
#property_name ⇒ Object
48
49
50
|
# File 'lib/command_handlers/models/model_binding.rb', line 48
def property_name
nested_property? ? nested_property_name : property_name_expression
end
|
#property_name_expression ⇒ Object
71
72
73
|
# File 'lib/command_handlers/models/model_binding.rb', line 71
def property_name_expression
@property_name_expression
end
|
#remove_observer(observer) ⇒ Object
118
119
120
121
122
123
124
125
126
127
128
129
130
|
# File 'lib/command_handlers/models/model_binding.rb', line 118
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[observer] = nil
elsif nested_property?
nested_property_observers_for(observer).clear
else
model.extend(ObservableModel) unless model.is_a?(ObservableModel)
observer.unobserve(model, property_name)
end
end
|
#update(value) ⇒ Object
169
170
171
172
173
|
# File 'lib/command_handlers/models/model_binding.rb', line 169
def update(value)
return if model.nil?
converted_value = @@property_type_converters[@property_type].call(value)
invoke_property_writer(model, "#{property_name}=", converted_value) unless evaluate_property == converted_value
end
|