volt 0.8.14 → 0.8.15
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/.gitignore +1 -1
- data/Readme.md +8 -2
- data/VERSION +1 -1
- data/app/volt/controllers/notices_controller.rb +1 -1
- data/app/volt/models/user.rb +2 -2
- data/app/volt/tasks/live_query/live_query_pool.rb +1 -1
- data/app/volt/tasks/query_tasks.rb +1 -1
- data/app/volt/tasks/store_tasks.rb +1 -1
- data/app/volt/tasks/user_tasks.rb +2 -2
- data/lib/volt/boot.rb +2 -2
- data/lib/volt/cli/asset_compile.rb +31 -27
- data/lib/volt/cli.rb +64 -65
- data/lib/volt/config.rb +25 -23
- data/lib/volt/console.rb +17 -16
- data/lib/volt/controllers/model_controller.rb +82 -80
- data/lib/volt/data_stores/data_store.rb +2 -2
- data/lib/volt/data_stores/mongo_driver.rb +2 -2
- data/lib/volt/extra_core/inflections.rb +2 -2
- data/lib/volt/extra_core/inflector/inflections.rb +185 -183
- data/lib/volt/extra_core/inflector/methods.rb +50 -48
- data/lib/volt/extra_core/string.rb +2 -2
- data/lib/volt/models/array_model.rb +93 -92
- data/lib/volt/models/cursor.rb +3 -2
- data/lib/volt/models/model.rb +248 -251
- data/lib/volt/models/model_hash_behaviour.rb +44 -44
- data/lib/volt/models/model_helpers.rb +38 -36
- data/lib/volt/models/model_state.rb +16 -17
- data/lib/volt/models/model_wrapper.rb +25 -24
- data/lib/volt/models/persistors/array_store.rb +145 -143
- data/lib/volt/models/persistors/base.rb +18 -16
- data/lib/volt/models/persistors/flash.rb +24 -22
- data/lib/volt/models/persistors/local_store.rb +46 -44
- data/lib/volt/models/persistors/model_identity_map.rb +10 -8
- data/lib/volt/models/persistors/model_store.rb +76 -76
- data/lib/volt/models/persistors/params.rb +19 -17
- data/lib/volt/models/persistors/query/query_listener.rb +65 -63
- data/lib/volt/models/persistors/query/query_listener_pool.rb +12 -10
- data/lib/volt/models/persistors/store.rb +28 -28
- data/lib/volt/models/persistors/store_factory.rb +12 -10
- data/lib/volt/models/persistors/store_state.rb +33 -31
- data/lib/volt/models/url.rb +96 -104
- data/lib/volt/models/validations.rb +56 -54
- data/lib/volt/models/validators/length_validator.rb +24 -22
- data/lib/volt/models/validators/presence_validator.rb +14 -12
- data/lib/volt/page/bindings/attribute_binding.rb +106 -106
- data/lib/volt/page/bindings/base_binding.rb +23 -21
- data/lib/volt/page/bindings/component_binding.rb +3 -1
- data/lib/volt/page/bindings/content_binding.rb +34 -34
- data/lib/volt/page/bindings/each_binding.rb +113 -113
- data/lib/volt/page/bindings/event_binding.rb +38 -34
- data/lib/volt/page/bindings/if_binding.rb +56 -54
- data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +24 -22
- data/lib/volt/page/bindings/template_binding.rb +182 -185
- data/lib/volt/page/channel.rb +79 -77
- data/lib/volt/page/channel_stub.rb +29 -27
- data/lib/volt/page/document.rb +6 -5
- data/lib/volt/page/document_events.rb +54 -52
- data/lib/volt/page/page.rb +139 -138
- data/lib/volt/page/string_template_renderer.rb +36 -36
- data/lib/volt/page/sub_context.rb +26 -25
- data/lib/volt/page/targets/attribute_section.rb +27 -25
- data/lib/volt/page/targets/attribute_target.rb +7 -6
- data/lib/volt/page/targets/base_section.rb +27 -26
- data/lib/volt/page/targets/binding_document/base_node.rb +3 -1
- data/lib/volt/page/targets/binding_document/component_node.rb +85 -82
- data/lib/volt/page/targets/binding_document/html_node.rb +11 -9
- data/lib/volt/page/targets/dom_section.rb +78 -77
- data/lib/volt/page/targets/dom_target.rb +8 -6
- data/lib/volt/page/targets/dom_template.rb +90 -88
- data/lib/volt/page/targets/helpers/comment_searchers.rb +51 -49
- data/lib/volt/page/tasks.rb +59 -57
- data/lib/volt/page/template_renderer.rb +17 -14
- data/lib/volt/page/url_tracker.rb +26 -24
- data/lib/volt/reactive/computation.rb +87 -88
- data/lib/volt/reactive/dependency.rb +30 -28
- data/lib/volt/reactive/eventable.rb +64 -62
- data/lib/volt/reactive/hash_dependency.rb +25 -23
- data/lib/volt/reactive/reactive_accessors.rb +34 -32
- data/lib/volt/reactive/reactive_array.rb +162 -162
- data/lib/volt/reactive/reactive_hash.rb +37 -35
- data/lib/volt/router/routes.rb +99 -101
- data/lib/volt/server/component_handler.rb +20 -21
- data/lib/volt/server/component_templates.rb +72 -70
- data/lib/volt/server/html_parser/attribute_scope.rb +109 -99
- data/lib/volt/server/html_parser/each_scope.rb +17 -16
- data/lib/volt/server/html_parser/if_view_scope.rb +51 -49
- data/lib/volt/server/html_parser/sandlebars_parser.rb +184 -177
- data/lib/volt/server/html_parser/textarea_scope.rb +24 -22
- data/lib/volt/server/html_parser/view_handler.rb +66 -65
- data/lib/volt/server/html_parser/view_parser.rb +23 -21
- data/lib/volt/server/html_parser/view_scope.rb +142 -141
- data/lib/volt/server/rack/asset_files.rb +81 -79
- data/lib/volt/server/rack/component_code.rb +17 -15
- data/lib/volt/server/rack/component_html_renderer.rb +14 -12
- data/lib/volt/server/rack/component_paths.rb +72 -71
- data/lib/volt/server/rack/index_files.rb +36 -39
- data/lib/volt/server/rack/opal_files.rb +43 -41
- data/lib/volt/server/rack/source_map_server.rb +23 -21
- data/lib/volt/server/socket_connection_handler.rb +46 -45
- data/lib/volt/server/socket_connection_handler_stub.rb +21 -19
- data/lib/volt/server.rb +60 -58
- data/lib/volt/spec/setup.rb +3 -3
- data/lib/volt/tasks/dispatcher.rb +24 -23
- data/lib/volt/tasks/task_handler.rb +35 -33
- data/lib/volt/utils/ejson.rb +8 -6
- data/lib/volt/utils/generic_counting_pool.rb +33 -31
- data/lib/volt/utils/generic_pool.rb +73 -70
- data/lib/volt/utils/local_storage.rb +42 -38
- data/lib/volt/volt/environment.rb +1 -1
- data/lib/volt.rb +44 -42
- data/spec/apps/kitchen_sink/app/main/assets/css/todos.css +28 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +2 -2
- data/spec/apps/kitchen_sink/app/main/controllers/todos_controller.rb +17 -0
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +1 -0
- data/spec/apps/kitchen_sink/app/main/views/todos/index.html +24 -0
- data/spec/apps/kitchen_sink/config.ru +1 -1
- data/spec/controllers/reactive_accessors_spec.rb +5 -5
- data/spec/extra_core/inflector_spec.rb +2 -2
- data/spec/integration/list_spec.rb +68 -0
- data/spec/models/model_spec.rb +57 -57
- data/spec/models/persistors/params_spec.rb +6 -6
- data/spec/models/persistors/store_spec.rb +7 -7
- data/spec/models/validations_spec.rb +3 -3
- data/spec/page/bindings/content_binding_spec.rb +7 -7
- data/spec/page/bindings/template_binding_spec.rb +4 -5
- data/spec/page/sub_context_spec.rb +2 -2
- data/spec/reactive/computation_spec.rb +10 -10
- data/spec/reactive/dependency_spec.rb +2 -2
- data/spec/reactive/eventable_spec.rb +4 -4
- data/spec/reactive/reactive_array_spec.rb +13 -13
- data/spec/router/routes_spec.rb +5 -5
- data/spec/server/html_parser/sandlebars_parser_spec.rb +9 -9
- data/spec/server/html_parser/view_parser_spec.rb +27 -27
- data/spec/server/rack/asset_files_spec.rb +5 -5
- data/spec/server/rack/component_paths_spec.rb +2 -2
- data/spec/tasks/live_query_spec.rb +2 -2
- data/spec/tasks/query_tasks.rb +1 -1
- data/spec/tasks/query_tracker_spec.rb +1 -1
- data/spec/templates/targets/binding_document/component_node_spec.rb +2 -2
- data/spec/utils/generic_counting_pool_spec.rb +2 -2
- data/spec/utils/generic_pool_spec.rb +2 -2
- data/templates/component/controllers/main_controller.rb +1 -1
- data/templates/model/model.rb.tt +2 -2
- data/templates/newgem/app/newgem/controllers/main_controller.rb.tt +2 -2
- data/templates/project/app/main/controllers/main_controller.rb +1 -1
- data/templates/project/config.ru +1 -1
- metadata +10 -3
- data/app/volt/assets/js/vertxbus.js +0 -216
|
@@ -2,235 +2,232 @@ require 'volt/page/bindings/base_binding'
|
|
|
2
2
|
require 'volt/page/template_renderer'
|
|
3
3
|
require 'volt/page/bindings/template_binding/grouped_controllers'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
module Volt
|
|
6
|
+
class TemplateBinding < BaseBinding
|
|
7
|
+
def initialize(page, target, context, binding_name, binding_in_path, getter)
|
|
8
|
+
super(page, target, context, binding_name)
|
|
9
|
+
|
|
10
|
+
# Binding in path is the path for the template this binding is in
|
|
11
|
+
setup_path(binding_in_path)
|
|
12
|
+
|
|
13
|
+
@current_template = nil
|
|
14
|
+
|
|
15
|
+
# Run the initial render
|
|
16
|
+
@computation = -> do
|
|
17
|
+
# Don't try to render if this has been removed
|
|
18
|
+
if @context
|
|
19
|
+
# Render
|
|
20
|
+
update(*@context.instance_eval(&getter))
|
|
21
|
+
end
|
|
22
|
+
end.watch!
|
|
23
|
+
end
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
def setup_path(binding_in_path)
|
|
26
|
+
path_parts = binding_in_path.split('/')
|
|
27
|
+
@collection_name = path_parts[0]
|
|
28
|
+
@controller_name = path_parts[1]
|
|
29
|
+
@page_name = path_parts[2]
|
|
30
|
+
end
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
# Returns true if there is a template at the path
|
|
33
|
+
def check_for_template?(path)
|
|
34
|
+
@page.templates[path]
|
|
35
|
+
end
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
37
|
+
# Takes in a lookup path and returns the full path for the matching
|
|
38
|
+
# template. Also returns the controller and action name if applicable.
|
|
39
|
+
#
|
|
40
|
+
# Looking up a path is fairly simple. There are 4 parts needed to find
|
|
41
|
+
# the html to be rendered. File paths look like this:
|
|
42
|
+
# app/{component}/views/{controller_name}/{view}.html
|
|
43
|
+
# Within the html file may be one or more sections.
|
|
44
|
+
# 1. component (app/{comp})
|
|
45
|
+
# 2. controller
|
|
46
|
+
# 3. view
|
|
47
|
+
# 4. sections
|
|
48
|
+
#
|
|
49
|
+
# When searching for a file, the lookup starts at the section, and moves up.
|
|
50
|
+
# when moving up, default values are provided for the section, then view/section, etc..
|
|
51
|
+
# until a file is either found or the component level is reached.
|
|
52
|
+
#
|
|
53
|
+
# The defaults are as follows:
|
|
54
|
+
# 1. component - main
|
|
55
|
+
# 2. controller - main
|
|
56
|
+
# 3. view - main
|
|
57
|
+
# 4. section - body
|
|
58
|
+
def path_for_template(lookup_path, force_section=nil)
|
|
59
|
+
parts = lookup_path.split('/')
|
|
60
|
+
parts_size = parts.size
|
|
61
|
+
|
|
62
|
+
default_parts = ['main', 'main', 'index', 'body']
|
|
63
|
+
|
|
64
|
+
# When forcing a sub template, we can default the sub template section
|
|
65
|
+
default_parts[-1] = force_section if force_section
|
|
66
|
+
|
|
67
|
+
(5 - parts_size).times do |path_position|
|
|
68
|
+
# If they passed in a force_section, we can skip the first
|
|
69
|
+
next if force_section && path_position == 0
|
|
70
|
+
|
|
71
|
+
full_path = [@collection_name, @controller_name, @page_name, nil]
|
|
72
|
+
|
|
73
|
+
start_at = full_path.size - parts_size - path_position
|
|
74
|
+
|
|
75
|
+
full_path.size.times do |index|
|
|
76
|
+
if index >= start_at
|
|
77
|
+
if (part = parts[index - start_at])
|
|
78
|
+
full_path[index] = part
|
|
79
|
+
else
|
|
80
|
+
full_path[index] = default_parts[index]
|
|
81
|
+
end
|
|
80
82
|
end
|
|
81
83
|
end
|
|
82
|
-
end
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
path = full_path.join('/')
|
|
86
|
+
if check_for_template?(path)
|
|
87
|
+
controller = nil
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
# Don't return a controller if we are just getting another section
|
|
90
|
+
# from the same controller
|
|
91
|
+
if path_position >= 1
|
|
92
|
+
# Lookup the controller
|
|
93
|
+
controller = [full_path[0], full_path[1] + '_controller', full_path[2]]
|
|
94
|
+
end
|
|
95
|
+
return path, controller
|
|
93
96
|
end
|
|
94
|
-
return path, controller
|
|
95
97
|
end
|
|
98
|
+
|
|
99
|
+
return nil, nil
|
|
96
100
|
end
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
def update(path, section_or_arguments=nil, options={})
|
|
103
|
+
Computation.run_without_tracking do
|
|
104
|
+
# Remove existing template and call _removed
|
|
105
|
+
controller_send(:"#{@action}_removed") if @action && @controller
|
|
106
|
+
@current_template.remove if @current_template
|
|
100
107
|
|
|
101
|
-
|
|
102
|
-
Computation.run_without_tracking do
|
|
103
|
-
# Remove existing template and call _removed
|
|
104
|
-
controller_send(:"#{@action}_removed") if @action && @controller
|
|
105
|
-
@current_template.remove if @current_template
|
|
108
|
+
@options = options
|
|
106
109
|
|
|
107
|
-
|
|
110
|
+
# A blank path needs to load a missing template, otherwise it tries to load
|
|
111
|
+
# the same template.
|
|
112
|
+
path = path.blank? ? '---missing---' : path
|
|
108
113
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
path = path.blank? ? '---missing---' : path
|
|
114
|
+
section = nil
|
|
115
|
+
@arguments = nil
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
if section_or_arguments.is_a?(String)
|
|
118
|
+
# Render this as a section
|
|
119
|
+
section = section_or_arguments
|
|
120
|
+
else
|
|
121
|
+
# Use the value passed in as the default arguments
|
|
122
|
+
@arguments = section_or_arguments
|
|
123
|
+
end
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
#
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
# Sometimes we want multiple template bindings to share the same controller (usually
|
|
126
|
+
# when displaying a :Title and a :Body), this instance tracks those.
|
|
127
|
+
if @options && (controller_group = @options[:controller_group])
|
|
128
|
+
@grouped_controller = GroupedControllers.new(controller_group)
|
|
129
|
+
else
|
|
130
|
+
clear_grouped_controller
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
full_path, controller_path = path_for_template(path, section)
|
|
134
|
+
render_template(full_path, controller_path)
|
|
135
|
+
|
|
136
|
+
queue_clear_grouped_controller
|
|
122
137
|
end
|
|
138
|
+
end
|
|
123
139
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
140
|
+
# On the next tick, we clear the grouped controller so that any changes to template paths
|
|
141
|
+
# will create a new controller and trigger the action.
|
|
142
|
+
def queue_clear_grouped_controller
|
|
143
|
+
if Volt.in_browser?
|
|
144
|
+
# In the browser, we want to keep a grouped controller around during a single run
|
|
145
|
+
# of the event loop. To make that happen, we clear it on the next tick.
|
|
146
|
+
`setImmediate(function() {`
|
|
147
|
+
clear_grouped_controller
|
|
148
|
+
`});`
|
|
128
149
|
else
|
|
150
|
+
# For the backend, clear it immediately
|
|
129
151
|
clear_grouped_controller
|
|
130
152
|
end
|
|
131
|
-
|
|
132
|
-
full_path, controller_path = path_for_template(path, section)
|
|
133
|
-
render_template(full_path, controller_path)
|
|
134
|
-
|
|
135
|
-
queue_clear_grouped_controller
|
|
136
153
|
end
|
|
137
|
-
end
|
|
138
154
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
# of the event loop. To make that happen, we clear it on the next tick.
|
|
145
|
-
`setImmediate(function() {`
|
|
146
|
-
clear_grouped_controller
|
|
147
|
-
`})`
|
|
148
|
-
else
|
|
149
|
-
# For the backend, clear it immediately
|
|
150
|
-
clear_grouped_controller
|
|
155
|
+
def clear_grouped_controller
|
|
156
|
+
if @grouped_controller
|
|
157
|
+
@grouped_controller.clear
|
|
158
|
+
@grouped_controller = nil
|
|
159
|
+
end
|
|
151
160
|
end
|
|
152
|
-
end
|
|
153
161
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
@
|
|
157
|
-
@grouped_controller = nil
|
|
158
|
-
end
|
|
159
|
-
end
|
|
162
|
+
# The context for templates can be either a controller, or the original context.
|
|
163
|
+
def render_template(full_path, controller_path)
|
|
164
|
+
args = @arguments ? [SubContext.new(@arguments)] : []
|
|
160
165
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
else
|
|
166
|
-
args = []
|
|
167
|
-
end
|
|
166
|
+
@controller = nil
|
|
167
|
+
|
|
168
|
+
# Fetch grouped controllers if we're grouping
|
|
169
|
+
@controller = @grouped_controller.get if @grouped_controller
|
|
168
170
|
|
|
169
|
-
|
|
171
|
+
# The action to be called and rendered
|
|
172
|
+
@action = nil
|
|
170
173
|
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
if @controller
|
|
175
|
+
# Track that we're using the group controller
|
|
176
|
+
@grouped_controller.inc if @grouped_controller
|
|
177
|
+
else
|
|
178
|
+
# Otherwise, make a new controller
|
|
179
|
+
controller_class, @action = get_controller(controller_path)
|
|
173
180
|
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
if controller_class
|
|
182
|
+
# Setup the controller
|
|
183
|
+
@controller = controller_class.new(*args)
|
|
184
|
+
else
|
|
185
|
+
@controller = ModelController.new(*args)
|
|
186
|
+
end
|
|
176
187
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
@grouped_controller.inc if @grouped_controller
|
|
180
|
-
else
|
|
181
|
-
# Otherwise, make a new controller
|
|
182
|
-
controller_class, @action = get_controller(controller_path)
|
|
188
|
+
# Trigger the action
|
|
189
|
+
controller_send(@action) if @action
|
|
183
190
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
@controller = controller_class.new(*args)
|
|
187
|
-
else
|
|
188
|
-
@controller = ModelController.new(*args)
|
|
191
|
+
# Track the grouped controller
|
|
192
|
+
@grouped_controller.set(@controller) if @grouped_controller
|
|
189
193
|
end
|
|
190
194
|
|
|
191
|
-
|
|
192
|
-
controller_send(@action) if @action
|
|
195
|
+
@current_template = TemplateRenderer.new(@page, @target, @controller, @binding_name, full_path)
|
|
193
196
|
|
|
194
|
-
|
|
195
|
-
@grouped_controller.set(@controller) if @grouped_controller
|
|
197
|
+
call_ready
|
|
196
198
|
end
|
|
197
199
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
def call_ready
|
|
201
|
+
if @controller
|
|
202
|
+
# Set the current section on the controller if it wants so it can manipulate
|
|
203
|
+
# the dom if needed
|
|
204
|
+
if @controller.respond_to?(:section=)
|
|
205
|
+
@controller.section = @current_template.dom_section
|
|
206
|
+
end
|
|
202
207
|
|
|
203
|
-
|
|
204
|
-
if @controller
|
|
205
|
-
# Set the current section on the controller if it wants so it can manipulate
|
|
206
|
-
# the dom if needed
|
|
207
|
-
if @controller.respond_to?(:section=)
|
|
208
|
-
@controller.section = @current_template.dom_section
|
|
208
|
+
controller_send(:"#{@action}_ready") if @action
|
|
209
209
|
end
|
|
210
|
-
|
|
211
|
-
controller_send(:"#{@action}_ready") if @action
|
|
212
210
|
end
|
|
213
|
-
end
|
|
214
211
|
|
|
215
|
-
|
|
216
|
-
|
|
212
|
+
def remove
|
|
213
|
+
clear_grouped_controller
|
|
217
214
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
215
|
+
if @current_template
|
|
216
|
+
# Remove the template if one has been rendered, when the template binding is
|
|
217
|
+
# removed.
|
|
218
|
+
@current_template.remove
|
|
219
|
+
end
|
|
223
220
|
|
|
224
|
-
|
|
221
|
+
super
|
|
225
222
|
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
if @controller
|
|
224
|
+
controller_send(:"#{@action}_removed") if @action
|
|
228
225
|
|
|
229
|
-
|
|
226
|
+
@controller = nil
|
|
227
|
+
end
|
|
230
228
|
end
|
|
231
|
-
end
|
|
232
229
|
|
|
233
|
-
|
|
230
|
+
private
|
|
234
231
|
# Sends the action to the controller if it exists
|
|
235
232
|
def controller_send(action_name)
|
|
236
233
|
if @controller.respond_to?(action_name)
|
|
@@ -245,7 +242,7 @@ class TemplateBinding < BaseBinding
|
|
|
245
242
|
action = controller_path[-1]
|
|
246
243
|
|
|
247
244
|
# Get the constant parts
|
|
248
|
-
parts
|
|
245
|
+
parts = controller_path[0..-2].map { |v| v.gsub('-', '_').camelize }
|
|
249
246
|
|
|
250
247
|
# Home doesn't get namespaced
|
|
251
248
|
if parts.first == 'Main'
|
|
@@ -265,5 +262,5 @@ class TemplateBinding < BaseBinding
|
|
|
265
262
|
|
|
266
263
|
return obj, action
|
|
267
264
|
end
|
|
268
|
-
|
|
265
|
+
end
|
|
269
266
|
end
|
data/lib/volt/page/channel.rb
CHANGED
|
@@ -4,101 +4,103 @@ require 'json'
|
|
|
4
4
|
require 'volt/reactive/reactive_accessors'
|
|
5
5
|
require 'volt/reactive/eventable'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
module Volt
|
|
8
|
+
class Channel
|
|
9
|
+
include ReactiveAccessors
|
|
10
|
+
include Eventable
|
|
11
|
+
|
|
12
|
+
reactive_accessor :connected, :status, :error, :reconnect_interval, :retry_count
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@socket = nil
|
|
16
|
+
self.status = :opening
|
|
17
|
+
self.connected = false
|
|
18
|
+
self.error = nil
|
|
19
|
+
self.retry_count = 0
|
|
20
|
+
@queue = []
|
|
21
|
+
|
|
22
|
+
connect!
|
|
23
|
+
end
|
|
12
24
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
self.connected = false
|
|
17
|
-
self.error = nil
|
|
18
|
-
self.retry_count = 0
|
|
19
|
-
@queue = []
|
|
25
|
+
def connected?
|
|
26
|
+
self.connected
|
|
27
|
+
end
|
|
20
28
|
|
|
21
|
-
connect!
|
|
22
|
-
|
|
29
|
+
def connect!
|
|
30
|
+
%x{
|
|
31
|
+
this.socket = new SockJS('/channel');
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
this.socket.onopen = function() {
|
|
34
|
+
self.$opened();
|
|
35
|
+
};
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
this.socket.onmessage = function(message) {
|
|
38
|
+
self['$message_received'](message.data);
|
|
39
|
+
};
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
this.socket.onclose = function(error) {
|
|
42
|
+
self.$closed(error);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
end
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
def opened
|
|
48
|
+
old_status = @status
|
|
49
|
+
@status = :open
|
|
50
|
+
@connected = true
|
|
51
|
+
@reconnect_interval = nil
|
|
52
|
+
@retry_count = 0
|
|
53
|
+
@queue.each do |message|
|
|
54
|
+
send_message(message)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
39
57
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
end
|
|
58
|
+
def closed(error)
|
|
59
|
+
self.status = :closed
|
|
60
|
+
self.connected = false
|
|
61
|
+
self.error = `error.reason`
|
|
45
62
|
|
|
46
|
-
|
|
47
|
-
old_status = @status
|
|
48
|
-
@status = :open
|
|
49
|
-
@connected = true
|
|
50
|
-
@reconnect_interval = nil
|
|
51
|
-
@retry_count = 0
|
|
52
|
-
@queue.each do |message|
|
|
53
|
-
send_message(message)
|
|
63
|
+
reconnect!
|
|
54
64
|
end
|
|
55
|
-
end
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
def reconnect!
|
|
67
|
+
self.status = :reconnecting
|
|
68
|
+
self.reconnect_interval ||= 0
|
|
69
|
+
self.reconnect_interval += (2000 + rand(5000))
|
|
70
|
+
self.retry_count += 1
|
|
61
71
|
|
|
62
|
-
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def reconnect!
|
|
66
|
-
self.status = :reconnecting
|
|
67
|
-
self.reconnect_interval ||= 0
|
|
68
|
-
self.reconnect_interval += (2000 + rand(5000))
|
|
69
|
-
self.retry_count += 1
|
|
72
|
+
interval = self.reconnect_interval
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
%x{
|
|
75
|
+
setTimeout(function() {
|
|
76
|
+
self['$connect!']();
|
|
77
|
+
}, interval);
|
|
78
|
+
}
|
|
79
|
+
end
|
|
72
80
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
self['$connect!']();
|
|
76
|
-
}, interval);
|
|
77
|
-
}
|
|
78
|
-
end
|
|
81
|
+
def message_received(message)
|
|
82
|
+
message = JSON.parse(message)
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
trigger!('message', *message)
|
|
85
|
+
end
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
def send_message(message)
|
|
88
|
+
if self.status != :open
|
|
89
|
+
@queue << message
|
|
90
|
+
else
|
|
91
|
+
# TODO: Temp: wrap message in an array, so we're sure its valid JSON
|
|
92
|
+
message = JSON.dump([message])
|
|
93
|
+
%x{
|
|
94
|
+
this.socket.send(message);
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
end
|
|
85
98
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
@queue << message
|
|
89
|
-
else
|
|
90
|
-
# TODO: Temp: wrap message in an array, so we're sure its valid JSON
|
|
91
|
-
message = JSON.dump([message])
|
|
99
|
+
def close!
|
|
100
|
+
self.status = :closed
|
|
92
101
|
%x{
|
|
93
|
-
this.socket.
|
|
102
|
+
this.socket.close();
|
|
94
103
|
}
|
|
95
104
|
end
|
|
96
105
|
end
|
|
97
|
-
|
|
98
|
-
def close!
|
|
99
|
-
self.status = :closed
|
|
100
|
-
%x{
|
|
101
|
-
this.socket.close();
|
|
102
|
-
}
|
|
103
|
-
end
|
|
104
106
|
end
|
|
@@ -4,32 +4,34 @@
|
|
|
4
4
|
require 'volt/tasks/dispatcher'
|
|
5
5
|
require 'volt/reactive/eventable'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
7
|
+
module Volt
|
|
8
|
+
# Behaves the same as the Channel class, only the Channel class uses
|
|
9
|
+
# sockjs to pass messages to the backend. ChannelStub, simply passes
|
|
10
|
+
# them directly to SocketConnectionHandlerStub.
|
|
11
|
+
class ChannelStub
|
|
12
|
+
include Eventable
|
|
13
|
+
|
|
14
|
+
attr_reader :state, :error, :reconnect_interval
|
|
15
|
+
|
|
16
|
+
def initiailze
|
|
17
|
+
@state = :connected
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def opened
|
|
21
|
+
trigger!('open')
|
|
22
|
+
trigger!('changed')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def message_received(*message)
|
|
26
|
+
trigger!('message', *message)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def send_message(message)
|
|
30
|
+
SocketConnectionHandlerStub.new(self).process_message(message)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def close!
|
|
34
|
+
raise "close! should not be called on the backend channel"
|
|
35
|
+
end
|
|
34
36
|
end
|
|
35
37
|
end
|