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
@@ -1,8 +0,0 @@
1
- module CommandHandler
2
- def can_handle?(parent, command_symbol, *args, &block)
3
- raise "must be implemented by a class"
4
- end
5
- def do_handle(parent, command_symbol, *args, &block)
6
- raise "must be implemented by a class"
7
- end
8
- end
@@ -1,30 +0,0 @@
1
- require File.dirname(__FILE__) + "/command_handler_chain_link"
2
-
3
- class CommandHandlerChainFactory
4
- @@dsls = {}
5
-
6
- def self.def_dsl(dsl, *command_handler_array)
7
- @@last_chain_link = nil
8
- @@chain = nil
9
- command_handler_array.each do |command_handler|
10
- Glimmer.logger.debug "Loading #{command_handler.class.to_s}..."
11
- chain_link = CommandHandlerChainLink.new(command_handler)
12
- @@last_chain_link.chain_to(chain_link) if @@last_chain_link
13
- @@last_chain_link = chain_link
14
- @@chain = chain_link unless @@chain
15
- end
16
- @@dsls[dsl] = {
17
- :last_chain_link => @@last_chain_link,
18
- :chain => @@chain
19
- }
20
- end
21
-
22
- def self.select_dsl(dsl)
23
- @@last_chain_link = @@dsls[dsl][:last_chain_link]
24
- @@chain = @@dsls[dsl][:chain]
25
- end
26
-
27
- def self.chain
28
- @@chain
29
- end
30
- end
@@ -1,19 +0,0 @@
1
- class CommandHandlerChainLink
2
- def initialize(command_handler)
3
- @command_handler = command_handler
4
- end
5
- def chain_to(next_chain_link)
6
- @next_chain_link = next_chain_link
7
- end
8
- def handle(parent, command_symbol, *args, &block)
9
- if (@command_handler.can_handle?(parent, command_symbol, *args, &block))
10
- Glimmer.logger.debug "#{@command_handler.class.to_s} will handle command: #{command_symbol} with arguments #{args}"
11
- return @command_handler.do_handle(parent, command_symbol, *args, &block)
12
- elsif @next_chain_link
13
- return @next_chain_link.handle(parent, command_symbol, *args, &block)
14
- else
15
- Glimmer.logger.debug "Command: #{command_symbol} cannot be handled!"
16
- return nil
17
- end
18
- end
19
- end
@@ -1,49 +0,0 @@
1
- require File.dirname(__FILE__) + "/../command_handler"
2
- require File.dirname(__FILE__) + "/models/r_widget"
3
- require File.dirname(__FILE__) + "/models/model_binding"
4
-
5
- # Responsible for setting up the return value of the bind keyword (command symbol)
6
- # as a ModelBinding. It is then used by another command handler like
7
- # DataBindingCommandHandler for text and selection properties on Text and Spinner
8
- # or TableItemsDataBindingCommandHandler for items in a Table
9
- class BindCommandHandler
10
- include CommandHandler
11
-
12
- include_package 'org.eclipse.swt.widgets'
13
-
14
- def can_handle?(parent, command_symbol, *args, &block)
15
- (
16
- parent.is_a?(RWidget) and
17
- command_symbol.to_s == "bind" and
18
- (
19
- (
20
- (args.size == 2) and
21
- (
22
- args[1].is_a?(Symbol) or
23
- args[1].is_a?(String)
24
- )
25
- ) or
26
- (
27
- (args.size == 3) and
28
- (args[1].is_a?(Symbol) or args[1].is_a?(String)) and
29
- (args[2].is_a?(Symbol) or args[2].is_a?(String) or args[2].is_a?(Hash))
30
- ) or
31
- (
32
- (args.size == 4) and
33
- (args[1].is_a?(Symbol) or args[1].is_a?(String)) and
34
- (args[2].is_a?(Symbol) or args[2].is_a?(String)) and
35
- (args[3].is_a?(Hash))
36
- )
37
- ) and
38
- block == nil
39
- )
40
- end
41
-
42
- def do_handle(parent, command_symbol, *args, &block)
43
- property_type = args[2] if (args.size == 3) && !args[2].is_a?(Hash)
44
- binding_options = args[2] if args[2].is_a?(Hash)
45
- binding_options = args[3] if args[3].is_a?(Hash)
46
- ModelBinding.new(args[0], args[1].to_s, property_type, binding_options)
47
- end
48
-
49
- end
@@ -1,24 +0,0 @@
1
- require_relative '../command_handler'
2
- require_relative 'models/r_color'
3
-
4
- class ColorCommandHandler
5
- include CommandHandler
6
-
7
- include_package 'org.eclipse.swt.widgets'
8
-
9
- def can_handle?(parent, command_symbol, *args, &block)
10
- ['rgba', 'rgb'].include?(command_symbol.to_s) and
11
- (3..5).include?(args.count)
12
- end
13
-
14
- def do_handle(parent, command_symbol, *args, &block)
15
- if args.first.is_a?(Display)
16
- display = args.delete_at(0)
17
- elsif parent.is_a?(RWidget)
18
- display = parent.widget.display
19
- else
20
- display = nil
21
- end
22
- RColor.new(display, *args)
23
- end
24
- end
@@ -1,38 +0,0 @@
1
- require File.dirname(__FILE__) + "/../command_handler"
2
- require File.dirname(__FILE__) + "/models/r_widget"
3
-
4
- class ComboSelectionDataBindingCommandHandler
5
- include CommandHandler
6
- include Glimmer
7
-
8
- include_package 'org.eclipse.swt.widgets'
9
-
10
- def can_handle?(parent, command_symbol, *args, &block)
11
- parent.is_a?(RWidget) and
12
- parent.widget.is_a?(Combo) and
13
- command_symbol.to_s == "selection" and
14
- args.size == 1 and
15
- args[0].is_a?(ModelBinding) and
16
- args[0].evaluate_options_property.is_a?(Array) and
17
- block == nil
18
- end
19
-
20
- def do_handle(parent, command_symbol, *args, &block)
21
- model_binding = args[0]
22
- widget_binding = WidgetBinding.new(parent, "items")
23
- widget_binding.update(model_binding.evaluate_options_property)
24
- model = model_binding.base_model
25
- widget_binding.observe(model, model_binding.options_property_name)
26
-
27
- widget_binding = WidgetBinding.new(parent, "text")
28
- widget_binding.update(model_binding.evaluate_property)
29
- widget_binding.observe(model, model_binding.property_name_expression)
30
-
31
- add_contents(parent) {
32
- on_widget_selected {
33
- model_binding.update(widget_binding.evaluate_property)
34
- }
35
- }
36
- end
37
-
38
- end
@@ -1,67 +0,0 @@
1
- require File.dirname(__FILE__) + "/../command_handler"
2
- require File.dirname(__FILE__) + "/models/r_widget"
3
- require File.dirname(__FILE__) + "/models/observable_model"
4
- require File.dirname(__FILE__) + "/models/model_binding"
5
- require File.dirname(__FILE__) + "/models/widget_binding"
6
-
7
- # Responsible for wiring two-way data-binding for text and selection properties
8
- # on Text, Button, and Spinner widgets.
9
- # Does so by using the output of the bind(model, property) command in the form
10
- # of a ModelBinding, which is then connected to an anonymous widget observer
11
- # (aka widget_data_binder as per widget_data_binders array)
12
- #
13
- # Depends on BindCommandHandler
14
- class DataBindingCommandHandler
15
- extend Glimmer
16
- include CommandHandler
17
-
18
- include_package 'org.eclipse.swt.widgets'
19
-
20
- @@widget_data_binders = {
21
- Java::OrgEclipseSwtWidgets::Text => {
22
- :text => Proc.new do |rwidget, model_binding|
23
- add_contents(rwidget) {
24
- on_modify_text { |modify_event|
25
- model_binding.update(rwidget.widget.getText)
26
- }
27
- }
28
- end,
29
- },
30
- Java::OrgEclipseSwtWidgets::Button => {
31
- :selection => Proc.new do |rwidget, model_binding|
32
- add_contents(rwidget) {
33
- on_widget_selected { |selection_event|
34
- model_binding.update(rwidget.widget.getSelection)
35
- }
36
- }
37
- end
38
- },
39
- Java::OrgEclipseSwtWidgets::Spinner => {
40
- :selection => Proc.new do |rwidget, model_binding|
41
- add_contents(rwidget) {
42
- on_widget_selected { |selection_event|
43
- model_binding.update(rwidget.widget.getSelection)
44
- }
45
- }
46
- end
47
- }
48
- }
49
-
50
- def can_handle?(parent, command_symbol, *args, &block)
51
- (parent.is_a?(RWidget) and
52
- args.size == 1 and
53
- args[0].is_a?(ModelBinding))
54
- end
55
-
56
- def do_handle(parent, command_symbol, *args, &block)
57
- model_binding = args[0]
58
- widget_binding_parameters = [parent, command_symbol.to_s]
59
- widget_binding = WidgetBinding.new(*widget_binding_parameters)
60
- widget_binding.update(model_binding.evaluate_property)
61
- widget_binding.observe(model_binding)
62
- widget_data_binder_map = @@widget_data_binders[parent.widget.class]
63
- widget_data_binder = widget_data_binder_map[command_symbol.to_s.to_sym] if widget_data_binder_map
64
- widget_data_binder.call(parent, model_binding) if widget_data_binder
65
- end
66
-
67
- end
@@ -1,43 +0,0 @@
1
- require File.dirname(__FILE__) + "/../command_handler"
2
- require File.dirname(__FILE__) + "/models/r_widget"
3
- require File.dirname(__FILE__) + "/models/list_selection_binding"
4
-
5
- class ListSelectionDataBindingCommandHandler
6
- include CommandHandler
7
- include Glimmer
8
-
9
- include_package 'org.eclipse.swt.widgets'
10
-
11
- def can_handle?(parent, command_symbol, *args, &block)
12
- parent.is_a?(RWidget) and
13
- parent.widget.is_a?(List) and
14
- command_symbol.to_s == "selection" and
15
- args.size == 1 and
16
- args[0].is_a?(ModelBinding) and
17
- args[0].evaluate_options_property.is_a?(Array) and
18
- block == nil
19
- end
20
-
21
- def do_handle(parent, command_symbol, *args, &block)
22
- model_binding = args[0]
23
- widget_binding = WidgetBinding.new(parent, "items")
24
- widget_binding.update(model_binding.evaluate_options_property)
25
- model = model_binding.base_model
26
- #TODO make this options observer dependent and all similar observers in widget specific data binding handlers
27
- widget_binding.observe(model, model_binding.options_property_name)
28
-
29
- property_type = :string
30
- property_type = :array if parent.has_style?(:multi)
31
- list_selection_binding = ListSelectionBinding.new(parent, property_type)
32
- list_selection_binding.update(model_binding.evaluate_property)
33
- #TODO check if nested data binding works for list widget and other widgets that need custom data binding
34
- list_selection_binding.observe(model, model_binding.property_name_expression)
35
-
36
- add_contents(parent) {
37
- on_widget_selected {
38
- model_binding.update(list_selection_binding.evaluate_property)
39
- }
40
- }
41
- end
42
-
43
- end
@@ -1,14 +0,0 @@
1
- require_relative 'observer'
2
-
3
- # Observer that takes an updater block to process updates
4
- class BlockObserver
5
- include Observer
6
-
7
- def initialize(&updater)
8
- @updater = updater
9
- end
10
-
11
- def update(changed_value=nil)
12
- @updater.call(changed_value)
13
- end
14
- end
@@ -1,45 +0,0 @@
1
- require_relative 'observable'
2
- require_relative 'observer'
3
-
4
- # SWT List widget selection binding
5
- class ListSelectionBinding
6
- include Glimmer
7
- include Observable
8
- include Observer
9
-
10
- attr_reader :widget
11
- @@property_type_updaters = {
12
- :string => lambda { |widget, value| widget.widget.select(widget.widget.index_of(value.to_s)) },
13
- :array => lambda { |widget, value| widget.widget.selection=((value or []).to_java :string) }
14
- }
15
- @@property_evaluators = {
16
- :string => lambda do |selection_array|
17
- return nil if selection_array.empty?
18
- selection_array[0]
19
- end,
20
- :array => lambda do |selection_array|
21
- selection_array
22
- end
23
- }
24
- # Initialize with list widget and property_type
25
- # property_type :string represents default list single selection
26
- # property_type :array represents list multi selection
27
- def initialize(widget, property_type)
28
- property_type = :string if property_type.nil? or property_type == :undefined
29
- @widget = widget
30
- @property_type = property_type
31
- add_contents(@widget) {
32
- on_widget_disposed { |dispose_event|
33
- unregister_all_observables
34
- }
35
- }
36
- end
37
- def update(value)
38
- @@property_type_updaters[@property_type].call(@widget, value) unless evaluate_property == value
39
- end
40
- def evaluate_property
41
- selection_array = @widget.widget.send("selection").to_a
42
- property_value = @@property_evaluators[@property_type].call(selection_array)
43
- return property_value
44
- end
45
- end
@@ -1,205 +0,0 @@
1
- require_relative 'observable'
2
- require_relative 'observer'
3
- require_relative 'block_observer'
4
-
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 => BlockObserver.new do |changed_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.update(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] = BlockObserver.new do |changed_value|
135
- observer.update(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 update(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