glimmer 0.3.5 → 0.4.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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/README.markdown +94 -32
  3. data/lib/glimmer.rb +4 -6
  4. data/lib/glimmer/command_handler.rb +10 -0
  5. data/lib/glimmer/command_handler_chain_factory.rb +32 -0
  6. data/lib/glimmer/command_handler_chain_link.rb +21 -0
  7. data/lib/{command_handlers.rb → glimmer/command_handlers.rb} +21 -17
  8. data/lib/glimmer/command_handlers/bind_command_handler.rb +51 -0
  9. data/lib/glimmer/command_handlers/color_command_handler.rb +26 -0
  10. data/lib/glimmer/command_handlers/combo_selection_data_binding_command_handler.rb +40 -0
  11. data/lib/glimmer/command_handlers/data_binding_command_handler.rb +69 -0
  12. data/lib/glimmer/command_handlers/display_command_handler.rb +16 -0
  13. data/lib/glimmer/command_handlers/list_selection_data_binding_command_handler.rb +45 -0
  14. data/lib/glimmer/command_handlers/models/g_color.rb +34 -0
  15. data/lib/glimmer/command_handlers/models/g_display.rb +26 -0
  16. data/lib/glimmer/command_handlers/models/g_font.rb +62 -0
  17. data/lib/glimmer/command_handlers/models/g_runnable.rb +13 -0
  18. data/lib/glimmer/command_handlers/models/g_shell.rb +27 -0
  19. data/lib/glimmer/command_handlers/models/g_swt.rb +22 -0
  20. data/lib/glimmer/command_handlers/models/g_tab_item_composite.rb +33 -0
  21. data/lib/glimmer/command_handlers/models/g_widget.rb +199 -0
  22. data/lib/glimmer/command_handlers/models/g_widget_listener.rb +11 -0
  23. data/lib/glimmer/command_handlers/models/list_selection_binding.rb +47 -0
  24. data/lib/glimmer/command_handlers/models/model_binding.rb +206 -0
  25. data/lib/glimmer/command_handlers/models/observable.rb +11 -0
  26. data/lib/glimmer/command_handlers/models/observable_array.rb +104 -0
  27. data/lib/glimmer/command_handlers/models/observable_model.rb +105 -0
  28. data/lib/glimmer/command_handlers/models/observer.rb +115 -0
  29. data/lib/glimmer/command_handlers/models/table_items_binding.rb +45 -0
  30. data/lib/glimmer/command_handlers/models/tree_items_binding.rb +49 -0
  31. data/lib/glimmer/command_handlers/models/widget_binding.rb +29 -0
  32. data/lib/glimmer/command_handlers/shell_command_handler.rb +17 -0
  33. data/lib/glimmer/command_handlers/tab_item_command_handler.rb +21 -0
  34. data/lib/glimmer/command_handlers/table_column_properties_data_binding_command_handler.rb +25 -0
  35. data/lib/glimmer/command_handlers/table_items_data_binding_command_handler.rb +30 -0
  36. data/lib/glimmer/command_handlers/tree_items_data_binding_command_handler.rb +29 -0
  37. data/lib/glimmer/command_handlers/tree_properties_data_binding_command_handler.rb +25 -0
  38. data/lib/glimmer/command_handlers/widget_command_handler.rb +22 -0
  39. data/lib/glimmer/command_handlers/widget_listener_command_handler.rb +39 -0
  40. data/lib/glimmer/command_handlers/widget_method_command_handler.rb +21 -0
  41. data/lib/glimmer/parent.rb +7 -0
  42. data/lib/{shine.rb → glimmer/shine.rb} +1 -1
  43. data/lib/glimmer/swt_packages.rb +13 -0
  44. data/lib/{xml_command_handlers.rb → glimmer/xml_command_handlers.rb} +10 -8
  45. data/lib/glimmer/xml_command_handlers/html_command_handler.rb +47 -0
  46. data/lib/glimmer/xml_command_handlers/models/depth_first_search_iterator.rb +19 -0
  47. data/lib/glimmer/xml_command_handlers/models/name_space_visitor.rb +20 -0
  48. data/lib/glimmer/xml_command_handlers/models/node.rb +82 -0
  49. data/lib/glimmer/xml_command_handlers/models/node_visitor.rb +11 -0
  50. data/lib/glimmer/xml_command_handlers/models/xml_visitor.rb +61 -0
  51. data/lib/glimmer/xml_command_handlers/xml_command_handler.rb +20 -0
  52. data/lib/glimmer/xml_command_handlers/xml_name_space_command_handler.rb +33 -0
  53. data/lib/glimmer/xml_command_handlers/xml_tag_command_handler.rb +25 -0
  54. data/lib/glimmer/xml_command_handlers/xml_text_command_handler.rb +21 -0
  55. metadata +53 -54
  56. data/lib/command_handler.rb +0 -8
  57. data/lib/command_handler_chain_factory.rb +0 -30
  58. data/lib/command_handler_chain_link.rb +0 -19
  59. data/lib/command_handlers/bind_command_handler.rb +0 -49
  60. data/lib/command_handlers/color_command_handler.rb +0 -24
  61. data/lib/command_handlers/combo_selection_data_binding_command_handler.rb +0 -38
  62. data/lib/command_handlers/data_binding_command_handler.rb +0 -67
  63. data/lib/command_handlers/list_selection_data_binding_command_handler.rb +0 -43
  64. data/lib/command_handlers/models/block_observer.rb +0 -14
  65. data/lib/command_handlers/models/list_selection_binding.rb +0 -45
  66. data/lib/command_handlers/models/model_binding.rb +0 -205
  67. data/lib/command_handlers/models/observable.rb +0 -9
  68. data/lib/command_handlers/models/observable_array.rb +0 -102
  69. data/lib/command_handlers/models/observable_model.rb +0 -103
  70. data/lib/command_handlers/models/observer.rb +0 -88
  71. data/lib/command_handlers/models/r_color.rb +0 -32
  72. data/lib/command_handlers/models/r_font.rb +0 -60
  73. data/lib/command_handlers/models/r_runnable.rb +0 -11
  74. data/lib/command_handlers/models/r_shell.rb +0 -24
  75. data/lib/command_handlers/models/r_swt.rb +0 -18
  76. data/lib/command_handlers/models/r_tab_item_composite.rb +0 -31
  77. data/lib/command_handlers/models/r_widget.rb +0 -183
  78. data/lib/command_handlers/models/r_widget_listener.rb +0 -9
  79. data/lib/command_handlers/models/table_items_binding.rb +0 -43
  80. data/lib/command_handlers/models/tree_items_binding.rb +0 -47
  81. data/lib/command_handlers/models/widget_binding.rb +0 -27
  82. data/lib/command_handlers/shell_command_handler.rb +0 -15
  83. data/lib/command_handlers/tab_item_command_handler.rb +0 -19
  84. data/lib/command_handlers/table_column_properties_data_binding_command_handler.rb +0 -23
  85. data/lib/command_handlers/table_items_data_binding_command_handler.rb +0 -28
  86. data/lib/command_handlers/tree_items_data_binding_command_handler.rb +0 -27
  87. data/lib/command_handlers/tree_properties_data_binding_command_handler.rb +0 -23
  88. data/lib/command_handlers/widget_command_handler.rb +0 -20
  89. data/lib/command_handlers/widget_listener_command_handler.rb +0 -37
  90. data/lib/command_handlers/widget_method_command_handler.rb +0 -19
  91. data/lib/parent.rb +0 -5
  92. data/lib/string.rb +0 -8
  93. data/lib/swt_packages.rb +0 -11
  94. data/lib/symbol.rb +0 -10
  95. data/lib/xml_command_handlers/html_command_handler.rb +0 -45
  96. data/lib/xml_command_handlers/models/depth_first_search_iterator.rb +0 -17
  97. data/lib/xml_command_handlers/models/name_space_visitor.rb +0 -18
  98. data/lib/xml_command_handlers/models/node.rb +0 -80
  99. data/lib/xml_command_handlers/models/node_visitor.rb +0 -9
  100. data/lib/xml_command_handlers/models/xml_visitor.rb +0 -59
  101. data/lib/xml_command_handlers/xml_command_handler.rb +0 -18
  102. data/lib/xml_command_handlers/xml_name_space_command_handler.rb +0 -31
  103. data/lib/xml_command_handlers/xml_tag_command_handler.rb +0 -23
  104. data/lib/xml_command_handlers/xml_text_command_handler.rb +0 -19
@@ -0,0 +1,11 @@
1
+ module Glimmer
2
+ class GWidgetListener
3
+
4
+ attr_reader :listener
5
+
6
+ def initialize(listener)
7
+ @listener = listener
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ require_relative 'observable'
2
+ require_relative 'observer'
3
+
4
+ module Glimmer
5
+ # SWT List widget selection binding
6
+ class ListSelectionBinding
7
+ include Glimmer
8
+ include Observable
9
+ include Observer
10
+
11
+ attr_reader :widget
12
+ @@property_type_updaters = {
13
+ :string => lambda { |widget, value| widget.widget.select(widget.widget.index_of(value.to_s)) },
14
+ :array => lambda { |widget, value| widget.widget.selection=((value or []).to_java :string) }
15
+ }
16
+ @@property_evaluators = {
17
+ :string => lambda do |selection_array|
18
+ return nil if selection_array.empty?
19
+ selection_array[0]
20
+ end,
21
+ :array => lambda do |selection_array|
22
+ selection_array
23
+ end
24
+ }
25
+ # Initialize with list widget and property_type
26
+ # property_type :string represents default list single selection
27
+ # property_type :array represents list multi selection
28
+ def initialize(widget, property_type)
29
+ property_type = :string if property_type.nil? or property_type == :undefined
30
+ @widget = widget
31
+ @property_type = property_type
32
+ add_contents(@widget) {
33
+ on_widget_disposed { |dispose_event|
34
+ unregister_all_observables
35
+ }
36
+ }
37
+ end
38
+ def call(value)
39
+ @@property_type_updaters[@property_type].call(@widget, value) unless evaluate_property == value
40
+ end
41
+ def evaluate_property
42
+ selection_array = @widget.widget.send("selection").to_a
43
+ property_value = @@property_evaluators[@property_type].call(selection_array)
44
+ return property_value
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,206 @@
1
+ require_relative 'observable'
2
+ require_relative 'observer'
3
+
4
+ module Glimmer
5
+ class ModelBinding
6
+ include Observable
7
+ include Observer
8
+
9
+ attr_reader :property_type, :binding_options
10
+ @@property_type_converters = {
11
+ :undefined => lambda { |value| value },
12
+ :fixnum => lambda { |value| value.to_i },
13
+ :array => lambda { |value| value.to_a }
14
+ }
15
+ def initialize(base_model, property_name_expression, property_type = :undefined, binding_options = nil)
16
+ property_type = :undefined if property_type.nil?
17
+ @base_model = base_model
18
+ @property_name_expression = property_name_expression
19
+ @property_type = property_type
20
+ @binding_options = binding_options || {}
21
+ if computed?
22
+ @computed_model_bindings = computed_by.map do |computed_by_property_expression|
23
+ self.class.new(base_model, computed_by_property_expression, :undefined, computed_binding_options)
24
+ end
25
+ end
26
+ end
27
+ def model
28
+ nested_property? ? nested_model : base_model
29
+ end
30
+ # e.g. person.address.state returns [person, person.address]
31
+ def nested_models
32
+ @nested_models = [base_model]
33
+ model_property_names.reduce(base_model) do |reduced_model, nested_model_property_name|
34
+ if reduced_model.nil?
35
+ nil
36
+ else
37
+ invoke_property_reader(reduced_model, nested_model_property_name).tap do |new_reduced_model|
38
+ @nested_models << new_reduced_model
39
+ end
40
+ end
41
+ end
42
+ @nested_models
43
+ end
44
+ def nested_model
45
+ nested_models.last
46
+ end
47
+ def base_model
48
+ @base_model
49
+ end
50
+ def property_name
51
+ nested_property? ? nested_property_name : property_name_expression
52
+ end
53
+ # All nested property names
54
+ # e.g. property name expression "address.state" gives ['address', 'state']
55
+ # If there are any indexed property names, this returns indexes as properties.
56
+ # e.g. property name expression "addresses[1].state" gives ['addresses', '[1]', 'state']
57
+ def nested_property_names
58
+ @nested_property_names ||= property_name_expression.split(".").map {|pne| pne.match(/([^\[]+)(\[[^\]]+\])?/).to_a.drop(1)}.flatten.compact
59
+ end
60
+ # Final nested property name
61
+ # e.g. property name expression "address.state" gives :state
62
+ def nested_property_name
63
+ nested_property_names.last
64
+ end
65
+ # Model representing nested property names
66
+ # e.g. property name expression "address.state" gives [:address]
67
+ def model_property_names
68
+ nested_property_names[0...-1]
69
+ end
70
+ def nested_property?
71
+ property_name_expression.match(/[.\[]/)
72
+ end
73
+ def property_name_expression
74
+ @property_name_expression
75
+ end
76
+ def computed?
77
+ !!computed_by
78
+ end
79
+ def computed_by
80
+ @binding_options[:computed_by]
81
+ end
82
+ def computed_binding_options
83
+ @binding_options.reject {|k,v| k == :computed_by}
84
+ end
85
+ def nested_property_observers_for(observer)
86
+ @nested_property_observers_collection ||= {}
87
+ unless @nested_property_observers_collection.has_key?(observer)
88
+ @nested_property_observers_collection[observer] = nested_property_names.reduce({}) do |output, property_name|
89
+ output.merge(
90
+ property_name => Observer.proc do |new_value|
91
+ # Ensure reattaching observers when a higher level nested property is updated (e.g. person.address changes reattaches person.address.street observer)
92
+ add_observer(observer)
93
+ observer.call(evaluate_property)
94
+ end
95
+ )
96
+ end
97
+ end
98
+ # @nested_property_observers_collection[observer].keys.each_with_index do |property_name, i|
99
+ # previous_property_name = nested_property_names[i-1]
100
+ # previous_observer = @nested_property_observers_collection[observer][previous_property_name]
101
+ # nested_property_observer = @nested_property_observers_collection[observer][property_name]
102
+ # previous_observer.add_dependent(nested_property_observer) unless previous_observer.nil?
103
+ # end
104
+ # TODO remove this brainstorming
105
+ # person.addresses[1].streets[2].number
106
+ # person.addresses[1] = ...
107
+ @nested_property_observers_collection[observer]
108
+ end
109
+ def add_observer(observer)
110
+ if computed?
111
+ add_computed_observers(observer)
112
+ elsif nested_property?
113
+ add_nested_observers(observer)
114
+ else
115
+ observer.observe(model, property_name)
116
+ observer.add_dependent([self, nil] => [observer, model, property_name])
117
+ end
118
+ end
119
+ def remove_observer(observer)
120
+ if computed?
121
+ @computed_model_bindings.each do |computed_model_binding|
122
+ computed_observer_for(observer).unobserve(computed_model_binding)
123
+ end
124
+ @computed_observer_collection.delete(observer)
125
+ elsif nested_property?
126
+ nested_property_observers_for(observer).clear
127
+ else
128
+ observer.unobserve(model, property_name)
129
+ end
130
+ end
131
+ def computed_observer_for(observer)
132
+ @computed_observer_collection ||= {}
133
+ unless @computed_observer_collection.has_key?(observer)
134
+ @computed_observer_collection[observer] = Observer.proc do |new_value|
135
+ observer.call(evaluate_property)
136
+ end
137
+ end
138
+ @computed_observer_collection[observer]
139
+ end
140
+ def add_computed_observers(observer)
141
+ @computed_model_bindings.each do |computed_model_binding|
142
+ computed_observer_for(observer).observe(computed_model_binding)
143
+ observer.add_dependent([self, nil] => [computed_observer_for(observer), computed_model_binding, nil])
144
+ end
145
+ end
146
+ def add_nested_observers(observer)
147
+ nested_property_observers = nested_property_observers_for(observer)
148
+ nested_models.zip(nested_property_names).each_with_index do |zip, i|
149
+ model, property_name = zip
150
+ nested_property_observer = nested_property_observers[property_name]
151
+ previous_index = i - 1
152
+ parent_model = previous_index.negative? ? self : nested_models[previous_index]
153
+ parent_property_name = previous_index.negative? ? nil : nested_property_names[previous_index]
154
+ parent_observer = previous_index.negative? ? observer : nested_property_observers[parent_property_name]
155
+ parent_property_name = nil if parent_property_name.to_s.start_with?('[')
156
+ unless model.nil?
157
+ if property_indexed?(property_name)
158
+ # TODO figure out a way to deal with this more uniformly
159
+ nested_property_observer.observe(model)
160
+ parent_observer.add_dependent([parent_model, parent_property_name] => [nested_property_observer, model, nil])
161
+ else
162
+ nested_property_observer.observe(model, property_name)
163
+ parent_observer.add_dependent([parent_model, parent_property_name] => [nested_property_observer, model, property_name])
164
+ end
165
+ end
166
+ end
167
+ end
168
+ def call(value)
169
+ return if model.nil?
170
+ converted_value = @@property_type_converters[@property_type].call(value)
171
+ invoke_property_writer(model, "#{property_name}=", converted_value) unless evaluate_property == converted_value
172
+ end
173
+ def evaluate_property
174
+ invoke_property_reader(model, property_name) unless model.nil?
175
+ end
176
+ def evaluate_options_property
177
+ model.send(options_property_name) unless model.nil?
178
+ end
179
+ def options_property_name
180
+ self.property_name + "_options"
181
+ end
182
+ def property_indexed?(property_expression)
183
+ property_expression.start_with?('[')
184
+ end
185
+ def invoke_property_reader(object, property_expression)
186
+ if property_indexed?(property_expression)
187
+ property_method = '[]'
188
+ property_argument = property_expression[1...-1]
189
+ property_argument = property_argument.to_i if property_argument.match(/\d+/)
190
+ object.send(property_method, property_argument)
191
+ else
192
+ object.send(property_expression)
193
+ end
194
+ end
195
+ def invoke_property_writer(object, property_expression, value)
196
+ if property_indexed?(property_expression)
197
+ property_method = '[]='
198
+ property_argument = property_expression[1...-2]
199
+ property_argument = property_argument.to_i if property_argument.match(/\d+/)
200
+ object.send(property_method, property_argument, value)
201
+ else
202
+ object.send(property_expression, value)
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,11 @@
1
+ module Glimmer
2
+ module Observable
3
+ def add_observer(observer, property_or_properties=nil)
4
+ raise 'Not implemented!'
5
+ end
6
+
7
+ def remove_observer(observer, property_or_properties=nil)
8
+ raise 'Not implemented!'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,104 @@
1
+ require 'set'
2
+
3
+ require_relative 'observable'
4
+
5
+ module Glimmer
6
+ # TODO prefix utility methods with double-underscore
7
+ module ObservableArray
8
+ include Observable
9
+
10
+ def add_observer(observer, element_properties=nil)
11
+ return observer if has_observer?(observer) && element_properties.nil?
12
+ property_observer_list << observer
13
+ [element_properties].flatten.compact.each do |property|
14
+ each do |element|
15
+ observer.observe(element, property)
16
+ end
17
+ end
18
+ observer
19
+ end
20
+
21
+ def remove_observer(observer, element_properties=nil)
22
+ property_observer_list.delete(observer)
23
+ [element_properties].flatten.compact.each do |property|
24
+ each do |element|
25
+ observer.unobserve(element, property)
26
+ end
27
+ end
28
+ observer
29
+ end
30
+
31
+ def has_observer?(observer)
32
+ property_observer_list.include?(observer)
33
+ end
34
+
35
+ def property_observer_list
36
+ @property_observer_list ||= Set.new
37
+ end
38
+
39
+ def notify_observers
40
+ property_observer_list.each {|observer| observer.call}
41
+ end
42
+
43
+ def self.extend_object(array)
44
+ array.instance_eval("alias __original_add <<")
45
+ array.instance_eval <<-end_eval, __FILE__, __LINE__
46
+ def <<(value)
47
+ self.__original_add(value)
48
+ notify_observers
49
+ end
50
+ end_eval
51
+
52
+ array.instance_eval("alias __original_set_value []=")
53
+ array.instance_eval <<-end_eval, __FILE__, __LINE__
54
+ def []=(index, value)
55
+ old_value = self[index]
56
+ unregister_dependent_observers(old_value)
57
+ self.__original_set_value(index, value)
58
+ notify_observers
59
+ end
60
+ end_eval
61
+
62
+ array.instance_eval("alias __original_delete delete")
63
+ array.instance_eval <<-end_eval, __FILE__, __LINE__
64
+ def delete(value)
65
+ unregister_dependent_observers(value)
66
+ self.__original_delete(value)
67
+ notify_observers
68
+ end
69
+ end_eval
70
+
71
+ array.instance_eval("alias __original_delete_at delete_at")
72
+ array.instance_eval <<-end_eval, __FILE__, __LINE__
73
+ def delete_at(index)
74
+ old_value = self[index]
75
+ unregister_dependent_observers(old_value)
76
+ self.__original_delete_at(index)
77
+ notify_observers
78
+ end
79
+ end_eval
80
+
81
+ array.instance_eval("alias __original_clear clear")
82
+ array.instance_eval <<-end_eval, __FILE__, __LINE__
83
+ def clear
84
+ each do |old_value|
85
+ unregister_dependent_observers(old_value)
86
+ end
87
+ self.__original_clear
88
+ notify_observers
89
+ end
90
+ end_eval
91
+
92
+ super
93
+ end
94
+
95
+ def unregister_dependent_observers(old_value)
96
+ # TODO look into optimizing this
97
+ return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray)
98
+ property_observer_list.each do |observer|
99
+ observer.unregister_dependents_with_observable([self, nil], old_value)
100
+ end
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,105 @@
1
+ require 'set'
2
+
3
+ require_relative 'observable'
4
+ require_relative 'observer'
5
+
6
+ module Glimmer
7
+ # TODO prefix utility methods with double-underscore
8
+ module ObservableModel
9
+ include Observable
10
+
11
+ class Notifier
12
+ include Observer
13
+ def initialize(observable_model, property_name)
14
+ @observable_model = observable_model
15
+ @property_name = property_name
16
+ end
17
+ def call(new_value=nil)
18
+ @observable_model.notify_observers(@property_name)
19
+ end
20
+ end
21
+
22
+ def add_observer(observer, property_name)
23
+ return observer if has_observer?(observer, property_name)
24
+ property_observer_list(property_name) << observer
25
+ add_property_writer_observers(property_name)
26
+ observer
27
+ end
28
+
29
+ def remove_observer(observer, property_name)
30
+ property_observer_list(property_name).delete(observer)
31
+ end
32
+
33
+ def has_observer?(observer, property_name)
34
+ property_observer_list(property_name).include?(observer)
35
+ end
36
+
37
+ def has_observer_for_any_property?(observer)
38
+ property_observer_hash.values.map(&:to_a).sum.include?(observer)
39
+ end
40
+
41
+ def property_observer_hash
42
+ @property_observers = Hash.new unless @property_observers
43
+ @property_observers
44
+ end
45
+
46
+ def property_observer_list(property_name)
47
+ property_observer_hash[property_name.to_sym] = Set.new unless property_observer_hash[property_name.to_sym]
48
+ property_observer_hash[property_name.to_sym]
49
+ end
50
+
51
+ def notify_observers(property_name)
52
+ property_observer_list(property_name).each {|observer| observer.call(send(property_name))}
53
+ end
54
+ #TODO upon updating values, make sure dependent observers are cleared (not added as dependents here)
55
+
56
+ def add_property_writer_observers(property_name)
57
+ property_writer_name = "#{property_name}="
58
+ method(property_writer_name)
59
+ ensure_array_object_observer(property_name, send(property_name))
60
+ begin
61
+ method("__original_#{property_writer_name}")
62
+ rescue
63
+ instance_eval "alias __original_#{property_writer_name} #{property_writer_name}"
64
+ instance_eval <<-end_eval, __FILE__, __LINE__
65
+ def #{property_writer_name}(value)
66
+ old_value = self.#{property_name}
67
+ unregister_dependent_observers('#{property_name}', old_value)
68
+ self.__original_#{property_writer_name}(value)
69
+ notify_observers('#{property_name}')
70
+ ensure_array_object_observer('#{property_name}', value, old_value)
71
+ end
72
+ end_eval
73
+ end
74
+ rescue => e
75
+ # ignore writing if no property writer exists
76
+ Glimmer.logger.debug "No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"
77
+ end
78
+
79
+ def unregister_dependent_observers(property_name, old_value)
80
+ # TODO look into optimizing this
81
+ return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray)
82
+ property_observer_list(property_name).each do |observer|
83
+ observer.unregister_dependents_with_observable([self, property_name], old_value)
84
+ end
85
+ end
86
+
87
+ def ensure_array_object_observer(property_name, object, old_object = nil)
88
+ return unless object.is_a?(Array)
89
+ array_object_observer = array_object_observer_for(property_name)
90
+ array_object_observer.observe(object)
91
+ property_observer_list(property_name).each do |observer|
92
+ observer.add_dependent([self, property_name] => [array_object_observer, object, nil])
93
+ end
94
+ array_object_observer_for(property_name).unregister(old_object) if old_object.is_a?(ObservableArray)
95
+ end
96
+
97
+ def array_object_observer_for(property_name)
98
+ @array_object_observers ||= {}
99
+ unless @array_object_observers.has_key?(property_name)
100
+ @array_object_observers[property_name] = ObservableModel::Notifier.new(self, property_name)
101
+ end
102
+ @array_object_observers[property_name]
103
+ end
104
+ end
105
+ end