glimmer 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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