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.
- checksums.yaml +4 -4
- data/README.markdown +94 -32
- data/lib/glimmer.rb +4 -6
- data/lib/glimmer/command_handler.rb +10 -0
- data/lib/glimmer/command_handler_chain_factory.rb +32 -0
- data/lib/glimmer/command_handler_chain_link.rb +21 -0
- data/lib/{command_handlers.rb → glimmer/command_handlers.rb} +21 -17
- data/lib/glimmer/command_handlers/bind_command_handler.rb +51 -0
- data/lib/glimmer/command_handlers/color_command_handler.rb +26 -0
- data/lib/glimmer/command_handlers/combo_selection_data_binding_command_handler.rb +40 -0
- data/lib/glimmer/command_handlers/data_binding_command_handler.rb +69 -0
- data/lib/glimmer/command_handlers/display_command_handler.rb +16 -0
- data/lib/glimmer/command_handlers/list_selection_data_binding_command_handler.rb +45 -0
- data/lib/glimmer/command_handlers/models/g_color.rb +34 -0
- data/lib/glimmer/command_handlers/models/g_display.rb +26 -0
- data/lib/glimmer/command_handlers/models/g_font.rb +62 -0
- data/lib/glimmer/command_handlers/models/g_runnable.rb +13 -0
- data/lib/glimmer/command_handlers/models/g_shell.rb +27 -0
- data/lib/glimmer/command_handlers/models/g_swt.rb +22 -0
- data/lib/glimmer/command_handlers/models/g_tab_item_composite.rb +33 -0
- data/lib/glimmer/command_handlers/models/g_widget.rb +199 -0
- data/lib/glimmer/command_handlers/models/g_widget_listener.rb +11 -0
- data/lib/glimmer/command_handlers/models/list_selection_binding.rb +47 -0
- data/lib/glimmer/command_handlers/models/model_binding.rb +206 -0
- data/lib/glimmer/command_handlers/models/observable.rb +11 -0
- data/lib/glimmer/command_handlers/models/observable_array.rb +104 -0
- data/lib/glimmer/command_handlers/models/observable_model.rb +105 -0
- data/lib/glimmer/command_handlers/models/observer.rb +115 -0
- data/lib/glimmer/command_handlers/models/table_items_binding.rb +45 -0
- data/lib/glimmer/command_handlers/models/tree_items_binding.rb +49 -0
- data/lib/glimmer/command_handlers/models/widget_binding.rb +29 -0
- data/lib/glimmer/command_handlers/shell_command_handler.rb +17 -0
- data/lib/glimmer/command_handlers/tab_item_command_handler.rb +21 -0
- data/lib/glimmer/command_handlers/table_column_properties_data_binding_command_handler.rb +25 -0
- data/lib/glimmer/command_handlers/table_items_data_binding_command_handler.rb +30 -0
- data/lib/glimmer/command_handlers/tree_items_data_binding_command_handler.rb +29 -0
- data/lib/glimmer/command_handlers/tree_properties_data_binding_command_handler.rb +25 -0
- data/lib/glimmer/command_handlers/widget_command_handler.rb +22 -0
- data/lib/glimmer/command_handlers/widget_listener_command_handler.rb +39 -0
- data/lib/glimmer/command_handlers/widget_method_command_handler.rb +21 -0
- data/lib/glimmer/parent.rb +7 -0
- data/lib/{shine.rb → glimmer/shine.rb} +1 -1
- data/lib/glimmer/swt_packages.rb +13 -0
- data/lib/{xml_command_handlers.rb → glimmer/xml_command_handlers.rb} +10 -8
- data/lib/glimmer/xml_command_handlers/html_command_handler.rb +47 -0
- data/lib/glimmer/xml_command_handlers/models/depth_first_search_iterator.rb +19 -0
- data/lib/glimmer/xml_command_handlers/models/name_space_visitor.rb +20 -0
- data/lib/glimmer/xml_command_handlers/models/node.rb +82 -0
- data/lib/glimmer/xml_command_handlers/models/node_visitor.rb +11 -0
- data/lib/glimmer/xml_command_handlers/models/xml_visitor.rb +61 -0
- data/lib/glimmer/xml_command_handlers/xml_command_handler.rb +20 -0
- data/lib/glimmer/xml_command_handlers/xml_name_space_command_handler.rb +33 -0
- data/lib/glimmer/xml_command_handlers/xml_tag_command_handler.rb +25 -0
- data/lib/glimmer/xml_command_handlers/xml_text_command_handler.rb +21 -0
- metadata +53 -54
- data/lib/command_handler.rb +0 -8
- data/lib/command_handler_chain_factory.rb +0 -30
- data/lib/command_handler_chain_link.rb +0 -19
- data/lib/command_handlers/bind_command_handler.rb +0 -49
- data/lib/command_handlers/color_command_handler.rb +0 -24
- data/lib/command_handlers/combo_selection_data_binding_command_handler.rb +0 -38
- data/lib/command_handlers/data_binding_command_handler.rb +0 -67
- data/lib/command_handlers/list_selection_data_binding_command_handler.rb +0 -43
- data/lib/command_handlers/models/block_observer.rb +0 -14
- data/lib/command_handlers/models/list_selection_binding.rb +0 -45
- data/lib/command_handlers/models/model_binding.rb +0 -205
- data/lib/command_handlers/models/observable.rb +0 -9
- data/lib/command_handlers/models/observable_array.rb +0 -102
- data/lib/command_handlers/models/observable_model.rb +0 -103
- data/lib/command_handlers/models/observer.rb +0 -88
- data/lib/command_handlers/models/r_color.rb +0 -32
- data/lib/command_handlers/models/r_font.rb +0 -60
- data/lib/command_handlers/models/r_runnable.rb +0 -11
- data/lib/command_handlers/models/r_shell.rb +0 -24
- data/lib/command_handlers/models/r_swt.rb +0 -18
- data/lib/command_handlers/models/r_tab_item_composite.rb +0 -31
- data/lib/command_handlers/models/r_widget.rb +0 -183
- data/lib/command_handlers/models/r_widget_listener.rb +0 -9
- data/lib/command_handlers/models/table_items_binding.rb +0 -43
- data/lib/command_handlers/models/tree_items_binding.rb +0 -47
- data/lib/command_handlers/models/widget_binding.rb +0 -27
- data/lib/command_handlers/shell_command_handler.rb +0 -15
- data/lib/command_handlers/tab_item_command_handler.rb +0 -19
- data/lib/command_handlers/table_column_properties_data_binding_command_handler.rb +0 -23
- data/lib/command_handlers/table_items_data_binding_command_handler.rb +0 -28
- data/lib/command_handlers/tree_items_data_binding_command_handler.rb +0 -27
- data/lib/command_handlers/tree_properties_data_binding_command_handler.rb +0 -23
- data/lib/command_handlers/widget_command_handler.rb +0 -20
- data/lib/command_handlers/widget_listener_command_handler.rb +0 -37
- data/lib/command_handlers/widget_method_command_handler.rb +0 -19
- data/lib/parent.rb +0 -5
- data/lib/string.rb +0 -8
- data/lib/swt_packages.rb +0 -11
- data/lib/symbol.rb +0 -10
- data/lib/xml_command_handlers/html_command_handler.rb +0 -45
- data/lib/xml_command_handlers/models/depth_first_search_iterator.rb +0 -17
- data/lib/xml_command_handlers/models/name_space_visitor.rb +0 -18
- data/lib/xml_command_handlers/models/node.rb +0 -80
- data/lib/xml_command_handlers/models/node_visitor.rb +0 -9
- data/lib/xml_command_handlers/models/xml_visitor.rb +0 -59
- data/lib/xml_command_handlers/xml_command_handler.rb +0 -18
- data/lib/xml_command_handlers/xml_name_space_command_handler.rb +0 -31
- data/lib/xml_command_handlers/xml_tag_command_handler.rb +0 -23
- data/lib/xml_command_handlers/xml_text_command_handler.rb +0 -19
data/lib/command_handler.rb
DELETED
@@ -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
|