volt 0.9.3.pre1 → 0.9.3.pre2
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/.ruby-version +1 -1
- data/CHANGELOG.md +15 -1
- data/Gemfile +30 -3
- data/README.md +7 -2
- data/Rakefile +17 -0
- data/app/volt/models/user.rb +1 -1
- data/app/volt/tasks/live_query/live_query.rb +0 -1
- data/app/volt/tasks/live_query/live_query_pool.rb +8 -2
- data/app/volt/tasks/live_query/query_tracker.rb +2 -2
- data/app/volt/tasks/query_tasks.rb +10 -27
- data/app/volt/tasks/store_tasks.rb +6 -5
- data/app/volt/tasks/user_tasks.rb +2 -2
- data/docs/UPGRADE_GUIDE.md +14 -0
- data/lib/volt/boot.rb +1 -0
- data/lib/volt/cli/asset_compile.rb +25 -7
- data/lib/volt/cli/console.rb +6 -5
- data/lib/volt/cli/generate.rb +2 -2
- data/lib/volt/config.rb +2 -1
- data/lib/volt/controllers/http_controller.rb +4 -3
- data/lib/volt/controllers/model_controller.rb +41 -19
- data/lib/volt/controllers/template_helpers.rb +19 -0
- data/lib/volt/extra_core/array.rb +6 -0
- data/lib/volt/extra_core/hash.rb +8 -26
- data/lib/volt/extra_core/string.rb +1 -1
- data/lib/volt/models/array_model.rb +12 -4
- data/lib/volt/models/associations.rb +11 -13
- data/lib/volt/models/buffer.rb +1 -1
- data/lib/volt/models/model.rb +22 -13
- data/lib/volt/models/model_helpers/model_change_helpers.rb +0 -1
- data/lib/volt/models/model_helpers/model_helpers.rb +11 -0
- data/lib/volt/models/permissions.rb +9 -12
- data/lib/volt/models/persistors/array_store.rb +7 -7
- data/lib/volt/models/persistors/base.rb +9 -0
- data/lib/volt/models/persistors/cookies.rb +0 -4
- data/lib/volt/models/persistors/flash.rb +0 -4
- data/lib/volt/models/persistors/local_store.rb +0 -4
- data/lib/volt/models/persistors/model_store.rb +13 -21
- data/lib/volt/models/persistors/page.rb +22 -0
- data/lib/volt/models/persistors/params.rb +0 -4
- data/lib/volt/models/persistors/query/query_listener.rb +3 -2
- data/lib/volt/models/url.rb +2 -2
- data/lib/volt/models/validators/unique_validator.rb +1 -1
- data/lib/volt/models.rb +1 -0
- data/lib/volt/page/bindings/attribute_binding.rb +2 -2
- data/lib/volt/page/bindings/base_binding.rb +7 -3
- data/lib/volt/page/bindings/bindings.rb +9 -0
- data/lib/volt/page/bindings/content_binding.rb +2 -2
- data/lib/volt/page/bindings/each_binding.rb +16 -12
- data/lib/volt/page/bindings/event_binding.rb +4 -4
- data/lib/volt/page/bindings/if_binding.rb +3 -3
- data/lib/volt/page/bindings/view_binding.rb +4 -4
- data/lib/volt/page/bindings/yield_binding.rb +3 -3
- data/lib/volt/page/channel.rb +6 -0
- data/lib/volt/page/channel_stub.rb +1 -1
- data/lib/volt/page/page.rb +20 -54
- data/lib/volt/page/path_string_renderer.rb +5 -6
- data/lib/volt/page/string_template_renderer.rb +2 -2
- data/lib/volt/page/targets/attribute_section.rb +47 -0
- data/lib/volt/page/targets/base_section.rb +5 -5
- data/lib/volt/page/targets/binding_document/component_node.rb +6 -1
- data/lib/volt/page/template_renderer.rb +4 -4
- data/lib/volt/reactive/computation.rb +32 -3
- data/lib/volt/router/routes.rb +5 -5
- data/lib/volt/server/component_templates.rb +30 -2
- data/lib/volt/server/forking_server.rb +2 -2
- data/lib/volt/server/message_bus/base_message_bus.rb +52 -0
- data/lib/volt/server/message_bus/message_encoder.rb +64 -0
- data/lib/volt/server/message_bus/peer_to_peer/peer_connection.rb +186 -0
- data/lib/volt/server/message_bus/peer_to_peer/peer_server.rb +78 -0
- data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +57 -0
- data/lib/volt/server/message_bus/peer_to_peer/socket_with_timeout.rb +27 -0
- data/lib/volt/server/message_bus/peer_to_peer.rb +198 -0
- data/lib/volt/server/message_bus/redis.rb +1 -0
- data/lib/volt/server/rack/asset_files.rb +2 -2
- data/lib/volt/server/rack/component_paths.rb +1 -1
- data/lib/volt/server/rack/http_resource.rb +3 -2
- data/lib/volt/server/rack/opal_files.rb +6 -9
- data/lib/volt/server/websocket/websocket_handler.rb +0 -3
- data/lib/volt/server.rb +5 -3
- data/lib/volt/spec/setup.rb +11 -12
- data/lib/volt/tasks/dispatcher.rb +8 -12
- data/lib/volt/tasks/task_handler.rb +3 -2
- data/lib/volt/utils/csso_patch.rb +24 -0
- data/lib/volt/utils/promise_patch.rb +2 -0
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +73 -36
- data/lib/volt/volt/server_setup/app.rb +81 -0
- data/lib/volt.rb +22 -1
- data/spec/apps/kitchen_sink/Gemfile +1 -1
- data/spec/controllers/http_controller_spec.rb +5 -3
- data/spec/controllers/model_controller_spec.rb +2 -2
- data/spec/extra_core/hash_spec.rb +9 -0
- data/spec/integration/list_spec.rb +3 -3
- data/spec/models/associations_spec.rb +10 -2
- data/spec/models/dirty_spec.rb +7 -7
- data/spec/models/model_spec.rb +10 -2
- data/spec/models/permissions_spec.rb +9 -0
- data/spec/models/persistors/store_spec.rb +8 -0
- data/spec/page/bindings/content_binding_spec.rb +6 -2
- data/spec/page/bindings/each_binding_spec.rb +59 -0
- data/spec/page/bindings/if_binding_spec.rb +57 -0
- data/spec/page/path_string_renderer_spec.rb +5 -5
- data/spec/reactive/computation_spec.rb +65 -1
- data/spec/router/routes_spec.rb +1 -1
- data/spec/server/html_parser/sandlebars_parser_spec.rb +12 -22
- data/spec/server/message_bus/message_encoder_spec.rb +49 -0
- data/spec/server/message_bus/peer_to_peer/peer_connection_spec.rb +108 -0
- data/spec/server/message_bus/peer_to_peer/peer_server_spec.rb +66 -0
- data/spec/server/message_bus/peer_to_peer/socket_with_timeout_spec.rb +11 -0
- data/spec/server/message_bus/peer_to_peer_spec.rb +11 -0
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/spec/server/rack/http_resource_spec.rb +4 -4
- data/spec/spec_helper.rb +16 -3
- data/spec/tasks/dispatcher_spec.rb +17 -5
- data/spec/tasks/live_query_spec.rb +1 -1
- data/spec/tasks/query_tracker_spec.rb +34 -34
- data/spec/tasks/user_tasks_spec.rb +4 -2
- data/templates/project/Gemfile.tt +14 -3
- data/templates/project/config/app.rb.tt +27 -2
- data/volt.gemspec +3 -8
- metadata +32 -101
- data/docs/FAQ.md +0 -7
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# A file to require all client side bindings
|
|
2
|
+
require 'volt/page/bindings/attribute_binding'
|
|
3
|
+
require 'volt/page/bindings/content_binding'
|
|
4
|
+
require 'volt/page/bindings/each_binding'
|
|
5
|
+
require 'volt/page/bindings/if_binding'
|
|
6
|
+
require 'volt/page/bindings/view_binding'
|
|
7
|
+
require 'volt/page/bindings/yield_binding'
|
|
8
|
+
require 'volt/page/bindings/component_binding'
|
|
9
|
+
require 'volt/page/bindings/event_binding'
|
|
@@ -6,8 +6,8 @@ module Volt
|
|
|
6
6
|
HTML_ESCAPE_REGEXP = /[&"'><\n]/
|
|
7
7
|
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''', "\n" => "<br />\n" }
|
|
8
8
|
|
|
9
|
-
def initialize(
|
|
10
|
-
super(
|
|
9
|
+
def initialize(volt_app, target, context, binding_name, getter)
|
|
10
|
+
super(volt_app, target, context, binding_name)
|
|
11
11
|
|
|
12
12
|
# Listen for changes
|
|
13
13
|
@computation = lambda do
|
|
@@ -2,8 +2,8 @@ require 'volt/page/bindings/base_binding'
|
|
|
2
2
|
|
|
3
3
|
module Volt
|
|
4
4
|
class EachBinding < BaseBinding
|
|
5
|
-
def initialize(
|
|
6
|
-
super(
|
|
5
|
+
def initialize(volt_app, target, context, binding_name, getter, variable_name, index_name, template_name)
|
|
6
|
+
super(volt_app, target, context, binding_name)
|
|
7
7
|
|
|
8
8
|
@item_name = variable_name
|
|
9
9
|
@index_name = index_name
|
|
@@ -14,18 +14,22 @@ module Volt
|
|
|
14
14
|
@getter = getter
|
|
15
15
|
|
|
16
16
|
# Listen for changes
|
|
17
|
-
@computation =
|
|
18
|
-
|
|
17
|
+
@computation = lambda do
|
|
18
|
+
begin
|
|
19
|
+
value = @context.instance_eval(&@getter)
|
|
20
|
+
rescue => e
|
|
21
|
+
Volt.logger.error("EachBinding Error: #{e.inspect}")
|
|
22
|
+
value = []
|
|
23
|
+
end
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
value = @context.instance_eval(&@getter)
|
|
24
|
-
rescue => e
|
|
25
|
-
Volt.logger.error("EachBinding Error: #{e.inspect}")
|
|
26
|
-
value = []
|
|
25
|
+
value
|
|
26
|
+
end.watch_and_resolve! do |value|
|
|
27
|
+
update(value)
|
|
27
28
|
end
|
|
29
|
+
end
|
|
28
30
|
|
|
31
|
+
# When a changed event happens, we update to the new size.
|
|
32
|
+
def update(value)
|
|
29
33
|
# Since we're checking things like size, we don't want this to be re-triggered on a
|
|
30
34
|
# size change, so we run without tracking.
|
|
31
35
|
Computation.run_without_tracking do
|
|
@@ -110,7 +114,7 @@ module Volt
|
|
|
110
114
|
end
|
|
111
115
|
end
|
|
112
116
|
|
|
113
|
-
item_template = TemplateRenderer.new(@
|
|
117
|
+
item_template = TemplateRenderer.new(@volt_app, @target, item_context, binding_name, @template_name)
|
|
114
118
|
@templates.insert(position, item_template)
|
|
115
119
|
|
|
116
120
|
update_indexes_after(position)
|
|
@@ -29,8 +29,8 @@ module Volt
|
|
|
29
29
|
class EventBinding < BaseBinding
|
|
30
30
|
attr_accessor :context, :binding_name
|
|
31
31
|
|
|
32
|
-
def initialize(
|
|
33
|
-
super(
|
|
32
|
+
def initialize(volt_app, target, context, binding_name, event_name, call_proc)
|
|
33
|
+
super(volt_app, target, context, binding_name)
|
|
34
34
|
@event_name = event_name
|
|
35
35
|
|
|
36
36
|
handler = proc do |js_event|
|
|
@@ -42,12 +42,12 @@ module Volt
|
|
|
42
42
|
result = @context.instance_exec(event, &call_proc)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
@listener =
|
|
45
|
+
@listener = page.events.add(event_name, self, handler)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
# Remove the event binding
|
|
49
49
|
def remove
|
|
50
|
-
|
|
50
|
+
page.events.remove(@event_name, self)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
end
|
|
@@ -2,8 +2,8 @@ require 'volt/page/bindings/base_binding'
|
|
|
2
2
|
|
|
3
3
|
module Volt
|
|
4
4
|
class IfBinding < BaseBinding
|
|
5
|
-
def initialize(
|
|
6
|
-
super(
|
|
5
|
+
def initialize(volt_app, target, context, binding_name, branches)
|
|
6
|
+
super(volt_app, target, context, binding_name)
|
|
7
7
|
|
|
8
8
|
getter, template_name = branches[0]
|
|
9
9
|
|
|
@@ -88,7 +88,7 @@ module Volt
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
if true_template
|
|
91
|
-
@template = TemplateRenderer.new(@
|
|
91
|
+
@template = TemplateRenderer.new(@volt_app, @target, @context, binding_name, true_template)
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
end
|
|
@@ -10,8 +10,8 @@ module Volt
|
|
|
10
10
|
# lookup paths in ViewLookupForPath
|
|
11
11
|
# @param [String|nil] content_template_path is the path to the template for the content
|
|
12
12
|
# provided in the tag.
|
|
13
|
-
def initialize(
|
|
14
|
-
super(
|
|
13
|
+
def initialize(volt_app, target, context, binding_name, binding_in_path, getter, content_template_path = nil)
|
|
14
|
+
super(volt_app, target, context, binding_name)
|
|
15
15
|
|
|
16
16
|
@content_template_path = content_template_path
|
|
17
17
|
|
|
@@ -189,7 +189,7 @@ module Volt
|
|
|
189
189
|
# from the group)
|
|
190
190
|
generated_new = true
|
|
191
191
|
# Setup the controller
|
|
192
|
-
controller_class.new(*args)
|
|
192
|
+
controller_class.new(@volt_app, *args)
|
|
193
193
|
end
|
|
194
194
|
|
|
195
195
|
# Fetch grouped controllers if we're grouping
|
|
@@ -217,7 +217,7 @@ module Volt
|
|
|
217
217
|
|
|
218
218
|
# The context for templates can be either a controller, or the original context.
|
|
219
219
|
def render_template(full_path, path)
|
|
220
|
-
@current_template = TemplateRenderer.new(@
|
|
220
|
+
@current_template = TemplateRenderer.new(@volt_app, @target, @controller, @binding_name, full_path, path)
|
|
221
221
|
|
|
222
222
|
call_ready
|
|
223
223
|
end
|
|
@@ -5,8 +5,8 @@ require 'volt/page/template_renderer'
|
|
|
5
5
|
|
|
6
6
|
module Volt
|
|
7
7
|
class YieldBinding < BaseBinding
|
|
8
|
-
def initialize(
|
|
9
|
-
super(
|
|
8
|
+
def initialize(volt_app, target, context, binding_name)
|
|
9
|
+
super(volt_app, target, context, binding_name)
|
|
10
10
|
|
|
11
11
|
# Get the path to the template to yield
|
|
12
12
|
full_path = @context.attrs.content_template_path
|
|
@@ -14,7 +14,7 @@ module Volt
|
|
|
14
14
|
# Grab the controller for the content
|
|
15
15
|
controller = @context.attrs.content_controller
|
|
16
16
|
|
|
17
|
-
@current_template = TemplateRenderer.new(
|
|
17
|
+
@current_template = TemplateRenderer.new(volt_app, @target, controller, @binding_name, full_path)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def remove
|
data/lib/volt/page/channel.rb
CHANGED
|
@@ -64,6 +64,9 @@ module Volt
|
|
|
64
64
|
@queue.each do |message|
|
|
65
65
|
send_message(message)
|
|
66
66
|
end
|
|
67
|
+
|
|
68
|
+
# Trigger a connect event
|
|
69
|
+
trigger!('connect')
|
|
67
70
|
end
|
|
68
71
|
|
|
69
72
|
def closed(error)
|
|
@@ -71,6 +74,9 @@ module Volt
|
|
|
71
74
|
self.connected = false
|
|
72
75
|
self.error = `error.reason`
|
|
73
76
|
|
|
77
|
+
# Trigger a disconnect event
|
|
78
|
+
trigger!('disconnect')
|
|
79
|
+
|
|
74
80
|
reconnect!
|
|
75
81
|
end
|
|
76
82
|
|
data/lib/volt/page/page.rb
CHANGED
|
@@ -1,38 +1,10 @@
|
|
|
1
|
-
require 'opal'
|
|
2
|
-
require 'volt/models'
|
|
3
|
-
require 'volt/controllers/model_controller'
|
|
4
|
-
require 'volt/tasks/task_handler'
|
|
5
|
-
require 'volt/page/bindings/attribute_binding'
|
|
6
|
-
require 'volt/page/bindings/content_binding'
|
|
7
|
-
require 'volt/page/bindings/each_binding'
|
|
8
|
-
require 'volt/page/bindings/if_binding'
|
|
9
|
-
require 'volt/page/bindings/view_binding'
|
|
10
|
-
require 'volt/page/bindings/yield_binding'
|
|
11
|
-
require 'volt/page/bindings/component_binding'
|
|
12
|
-
require 'volt/page/bindings/event_binding'
|
|
13
|
-
require 'volt/page/template_renderer'
|
|
14
|
-
require 'volt/page/string_template_renderer'
|
|
15
|
-
require 'volt/page/document_events'
|
|
16
|
-
require 'volt/page/sub_context'
|
|
17
|
-
require 'volt/page/targets/dom_target'
|
|
18
|
-
require 'volt/data_stores/base_adaptor_client'
|
|
19
|
-
|
|
20
|
-
if RUBY_PLATFORM == 'opal'
|
|
21
|
-
require 'volt/page/channel'
|
|
22
|
-
else
|
|
23
|
-
require 'volt/page/channel_stub'
|
|
24
|
-
end
|
|
25
|
-
require 'volt/router/routes'
|
|
26
|
-
require 'volt/models/url'
|
|
27
|
-
require 'volt/page/url_tracker'
|
|
28
|
-
require 'volt/benchmark/benchmark'
|
|
29
|
-
require 'volt/page/tasks'
|
|
30
1
|
|
|
31
2
|
module Volt
|
|
32
3
|
class Page
|
|
33
4
|
attr_reader :url, :params, :page, :routes, :events
|
|
34
5
|
|
|
35
|
-
def initialize
|
|
6
|
+
def initialize(volt_app)
|
|
7
|
+
@volt_app = volt_app
|
|
36
8
|
# Run the code to setup the page
|
|
37
9
|
@page = Model.new
|
|
38
10
|
|
|
@@ -44,24 +16,26 @@ module Volt
|
|
|
44
16
|
@events = DocumentEvents.new
|
|
45
17
|
|
|
46
18
|
if RUBY_PLATFORM == 'opal'
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
19
|
+
if Volt.in_browser?
|
|
20
|
+
# Setup escape binding for console
|
|
21
|
+
`
|
|
22
|
+
$(document).keyup(function(e) {
|
|
23
|
+
if (e.keyCode == 27) {
|
|
24
|
+
Opal.gvars.page.$launch_console();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
$(document).on('click', 'a', function(event) {
|
|
29
|
+
return Opal.gvars.page.$link_clicked($(this).attr('href'), event);
|
|
30
|
+
});
|
|
31
|
+
`
|
|
32
|
+
end
|
|
59
33
|
end
|
|
60
34
|
|
|
61
35
|
# Initialize tasks so we can get the reload message
|
|
62
36
|
tasks if Volt.env.development?
|
|
63
37
|
|
|
64
|
-
if Volt.
|
|
38
|
+
if Volt.in_browser?
|
|
65
39
|
channel.on('reconnected') do
|
|
66
40
|
@page._reconnected = true
|
|
67
41
|
|
|
@@ -178,13 +152,13 @@ module Volt
|
|
|
178
152
|
# Do the initial url params parse
|
|
179
153
|
@url_tracker.url_updated(true)
|
|
180
154
|
|
|
181
|
-
main_controller = Main::MainController.new
|
|
155
|
+
main_controller = Main::MainController.new(@volt_app)
|
|
182
156
|
|
|
183
157
|
# Setup main page template
|
|
184
|
-
TemplateRenderer.new(
|
|
158
|
+
TemplateRenderer.new(@volt_app, DomTarget.new, main_controller, 'CONTENT', 'main/main/main/body')
|
|
185
159
|
|
|
186
160
|
# Setup title reactive template
|
|
187
|
-
@title_template = StringTemplateRenderer.new(
|
|
161
|
+
@title_template = StringTemplateRenderer.new(@volt_app, main_controller, 'main/main/main/title')
|
|
188
162
|
|
|
189
163
|
# Watch for changes to the title template
|
|
190
164
|
proc do
|
|
@@ -214,12 +188,4 @@ module Volt
|
|
|
214
188
|
Volt.logger.error("Unable to restore: #{e.inspect}")
|
|
215
189
|
end
|
|
216
190
|
end
|
|
217
|
-
|
|
218
|
-
if Volt.client?
|
|
219
|
-
$page = Page.new
|
|
220
|
-
|
|
221
|
-
`$(document).ready(function() {`
|
|
222
|
-
$page.start
|
|
223
|
-
`});`
|
|
224
|
-
end
|
|
225
191
|
end
|
|
@@ -9,13 +9,12 @@ module Volt
|
|
|
9
9
|
class ViewLookupException < Exception; end
|
|
10
10
|
class PathStringRenderer
|
|
11
11
|
attr_reader :html
|
|
12
|
-
def initialize(path, attrs = nil, page = nil, render_from_path = nil)
|
|
13
|
-
# use the global page if one is not passed in
|
|
14
|
-
page ||= $page
|
|
15
|
-
|
|
12
|
+
def initialize(volt_app, path, attrs = nil, page = nil, render_from_path = nil)
|
|
16
13
|
# where to do the path lookup from
|
|
17
14
|
render_from_path ||= 'main/main/main/body'
|
|
18
15
|
|
|
16
|
+
page ||= volt_app.page
|
|
17
|
+
|
|
19
18
|
# Make path into a full path
|
|
20
19
|
@view_lookup = Volt::ViewLookupForPath.new(page, render_from_path)
|
|
21
20
|
full_path, controller_path = @view_lookup.path_for_template(path, nil)
|
|
@@ -26,10 +25,10 @@ module Volt
|
|
|
26
25
|
|
|
27
26
|
controller_class, action = ControllerHandler.get_controller_and_action(controller_path)
|
|
28
27
|
|
|
29
|
-
controller = controller_class.new # (SubContext.new(attrs, nil, true))
|
|
28
|
+
controller = controller_class.new(volt_app) # (SubContext.new(attrs, nil, true))
|
|
30
29
|
controller.model = SubContext.new(attrs, nil, true)
|
|
31
30
|
|
|
32
|
-
renderer = StringTemplateRenderer.new(
|
|
31
|
+
renderer = StringTemplateRenderer.new(volt_app, controller, full_path)
|
|
33
32
|
|
|
34
33
|
@html = renderer.html
|
|
35
34
|
|
|
@@ -5,12 +5,12 @@ module Volt
|
|
|
5
5
|
# StringTemplateRenderer will intellegently update the string in the same way
|
|
6
6
|
# a normal bindings will update the dom.
|
|
7
7
|
class StringTemplateRenderer
|
|
8
|
-
def initialize(
|
|
8
|
+
def initialize(volt_app, context, template_path)
|
|
9
9
|
@dependency = Dependency.new
|
|
10
10
|
|
|
11
11
|
@template_path = template_path
|
|
12
12
|
@target = AttributeTarget.new(nil, nil, self)
|
|
13
|
-
@template = TemplateRenderer.new(
|
|
13
|
+
@template = TemplateRenderer.new(volt_app, @target, context, 'main', template_path)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
# Render the template and get the current value
|
|
@@ -18,9 +18,56 @@ module Volt
|
|
|
18
18
|
set_content_and_rezero_bindings(value, {})
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def insert_anchor_before_end(binding_name)
|
|
22
|
+
end_node = @target.find_by_binding_id(@binding_name)
|
|
23
|
+
if end_node.is_a?(ComponentNode)
|
|
24
|
+
component_node = ComponentNode.new(binding_name, end_node, end_node.root || end_node)
|
|
25
|
+
end_node.insert(-1, component_node)
|
|
26
|
+
else
|
|
27
|
+
raise "can not insert on HtmlNode"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# When using bindings, we have to change the binding id so we don't reuse
|
|
32
|
+
# the same id when rendering a binding multiple times.
|
|
33
|
+
def rezero_bindings(html, bindings)
|
|
34
|
+
@@base_binding_id ||= 20_000
|
|
35
|
+
# rezero
|
|
36
|
+
parts = html.split(/(\<\!\-\- \$\/?[0-9]+ \-\-\>)/).reject { |v| v == '' }
|
|
37
|
+
|
|
38
|
+
new_html = []
|
|
39
|
+
new_bindings = {}
|
|
40
|
+
id_map = {}
|
|
41
|
+
|
|
42
|
+
parts.each do |part|
|
|
43
|
+
case part
|
|
44
|
+
when /\<\!\-\- \$[0-9]+ \-\-\>/
|
|
45
|
+
# Open
|
|
46
|
+
binding_id = part.match(/\<\!\-\- \$([0-9]+) \-\-\>/)[1].to_i
|
|
47
|
+
binding = bindings[binding_id]
|
|
48
|
+
new_bindings[@@base_binding_id] = binding if binding
|
|
49
|
+
|
|
50
|
+
new_html << "<!-- $#{@@base_binding_id} -->"
|
|
51
|
+
id_map[binding_id] = @@base_binding_id
|
|
52
|
+
@@base_binding_id += 1
|
|
53
|
+
when /\<\!\-\- \$\/[0-9]+ \-\-\>/
|
|
54
|
+
# Close
|
|
55
|
+
binding_id = part.match(/\<\!\-\- \$\/([0-9]+) \-\-\>/)[1].to_i
|
|
56
|
+
new_html << "<!-- $/#{id_map[binding_id]} -->"
|
|
57
|
+
else
|
|
58
|
+
# html string
|
|
59
|
+
new_html << part
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
return new_html.join(''), new_bindings
|
|
64
|
+
end
|
|
65
|
+
|
|
21
66
|
# Takes in our html and bindings, and rezero's the comment names, and the
|
|
22
67
|
# bindings. Returns an updated bindings hash
|
|
23
68
|
def set_content_and_rezero_bindings(html, bindings)
|
|
69
|
+
html, bindings = rezero_bindings(html, bindings)
|
|
70
|
+
|
|
24
71
|
if @binding_name == 'main'
|
|
25
72
|
@target.html = html
|
|
26
73
|
else
|
|
@@ -6,19 +6,19 @@ module Volt
|
|
|
6
6
|
@@template_cache = {}
|
|
7
7
|
|
|
8
8
|
def remove
|
|
9
|
-
fail 'not implemented'
|
|
9
|
+
fail 'remove is not implemented'
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def remove_anchors
|
|
13
|
-
fail 'not implemented'
|
|
13
|
+
fail 'remove_anchors is not implemented'
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def insert_anchor_before_end
|
|
17
|
-
fail 'not implemented'
|
|
16
|
+
def insert_anchor_before_end(binding_name)
|
|
17
|
+
fail 'insert_anchor_before_end is not implemented'
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def set_template
|
|
21
|
-
fail 'not implemented'
|
|
21
|
+
fail 'set_template is not implemented'
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def set_content_to_template(page, template_name)
|
|
@@ -9,7 +9,7 @@ module Volt
|
|
|
9
9
|
class ComponentNode < BaseNode
|
|
10
10
|
include Eventable
|
|
11
11
|
|
|
12
|
-
attr_accessor :parent, :binding_id, :nodes
|
|
12
|
+
attr_accessor :parent, :binding_id, :nodes, :root
|
|
13
13
|
|
|
14
14
|
def initialize(binding_id = nil, parent = nil, root = nil)
|
|
15
15
|
@nodes = []
|
|
@@ -65,6 +65,11 @@ module Volt
|
|
|
65
65
|
@nodes << node
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
def insert(index, node)
|
|
69
|
+
@nodes.insert(index, node)
|
|
70
|
+
changed!
|
|
71
|
+
end
|
|
72
|
+
|
|
68
73
|
def to_html
|
|
69
74
|
str = []
|
|
70
75
|
@nodes.each do |node|
|
|
@@ -4,16 +4,16 @@ module Volt
|
|
|
4
4
|
class TemplateRenderer < BaseBinding
|
|
5
5
|
attr_reader :context
|
|
6
6
|
|
|
7
|
-
def initialize(
|
|
8
|
-
super(
|
|
7
|
+
def initialize(volt_app, target, context, binding_name, template_name)
|
|
8
|
+
super(volt_app, target, context, binding_name)
|
|
9
9
|
|
|
10
10
|
@sub_bindings = []
|
|
11
11
|
|
|
12
|
-
bindings = dom_section.set_content_to_template(page, template_name)
|
|
12
|
+
bindings = dom_section.set_content_to_template(volt_app.page, template_name)
|
|
13
13
|
|
|
14
14
|
bindings.each_pair do |id, bindings_for_id|
|
|
15
15
|
bindings_for_id.each do |binding|
|
|
16
|
-
@sub_bindings << binding.call(
|
|
16
|
+
@sub_bindings << binding.call(volt_app, target, context, id)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
end
|
|
@@ -74,6 +74,10 @@ module Volt
|
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
+
def stopped?
|
|
78
|
+
@stopped
|
|
79
|
+
end
|
|
80
|
+
|
|
77
81
|
# Runs in this computation as the current computation, returns the computation
|
|
78
82
|
def run_in
|
|
79
83
|
previous = Computation.current
|
|
@@ -172,20 +176,45 @@ class Proc
|
|
|
172
176
|
#
|
|
173
177
|
# Example:
|
|
174
178
|
# -> { }
|
|
175
|
-
def watch_and_resolve!
|
|
179
|
+
def watch_and_resolve!(yield_nil_for_unresolved_promise=false)
|
|
176
180
|
unless block_given?
|
|
177
181
|
fail 'watch_and_resolve! requires a block to call when the value is resolved or another value other than a promise is returned in the watch.'
|
|
178
182
|
end
|
|
179
183
|
|
|
180
|
-
|
|
184
|
+
# Keep results between runs
|
|
185
|
+
result = nil
|
|
186
|
+
|
|
187
|
+
computation = proc do |comp|
|
|
181
188
|
result = call
|
|
189
|
+
last_promise = nil
|
|
182
190
|
|
|
183
191
|
if result.is_a?(Promise)
|
|
192
|
+
last_promise = result
|
|
193
|
+
|
|
194
|
+
# Often you want a to be alerted that an unresolved promise is waiting
|
|
195
|
+
# to be resolved.
|
|
196
|
+
if yield_nil_for_unresolved_promise && !result.resolved?
|
|
197
|
+
yield(nil)
|
|
198
|
+
end
|
|
199
|
+
|
|
184
200
|
result.then do |final|
|
|
185
|
-
|
|
201
|
+
# Check to make sure that a new value didn't get reactively pushed
|
|
202
|
+
# before the promise resolved.
|
|
203
|
+
if last_promise.is_a?(Promise) && last_promise == result
|
|
204
|
+
# Don't resolve if the computation was stopped
|
|
205
|
+
unless comp.stopped?
|
|
206
|
+
yield(final)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Clear result for GC
|
|
210
|
+
result = nil
|
|
211
|
+
end
|
|
186
212
|
end
|
|
187
213
|
else
|
|
188
214
|
yield(result)
|
|
215
|
+
|
|
216
|
+
# Clear result for GC
|
|
217
|
+
result = nil
|
|
189
218
|
end
|
|
190
219
|
end.watch!
|
|
191
220
|
|
data/lib/volt/router/routes.rb
CHANGED
|
@@ -5,8 +5,8 @@ module Volt
|
|
|
5
5
|
# a url to params, and params to url.
|
|
6
6
|
# routes do
|
|
7
7
|
# client "/about", _view: 'about'
|
|
8
|
-
# client "/blog/{
|
|
9
|
-
# client "/blog/{
|
|
8
|
+
# client "/blog/{id}/edit", _view: 'blog/edit', _action: 'edit'
|
|
9
|
+
# client "/blog/{id}", _view: 'blog/show', _action: 'show'
|
|
10
10
|
# client "/blog", _view: 'blog'
|
|
11
11
|
# client "/blog/new", _view: 'blog/new', _action: 'new'
|
|
12
12
|
# client "/cool/{_name}", _view: 'cool'
|
|
@@ -27,16 +27,16 @@ module Volt
|
|
|
27
27
|
# @indirect_routes = {
|
|
28
28
|
# '*' => {
|
|
29
29
|
# 'edit' => {
|
|
30
|
-
# nil => {
|
|
30
|
+
# nil => {id: 1, _view: 'blog/edit', _action: 'edit'}
|
|
31
31
|
# }
|
|
32
|
-
# nil => {
|
|
32
|
+
# nil => {id: 1, _view: 'blog/show', _action: 'show'}
|
|
33
33
|
# }
|
|
34
34
|
# }
|
|
35
35
|
# }
|
|
36
36
|
#
|
|
37
37
|
# Match for params
|
|
38
38
|
# @param_matches = [
|
|
39
|
-
# {
|
|
39
|
+
# {id: nil, _view: 'blog/edit', _action: 'edit'} => Proc.new {|params| "/blog/#{params.id}/edit", params.reject {|k,v| k == :id }}
|
|
40
40
|
# ]
|
|
41
41
|
class Routes
|
|
42
42
|
def initialize
|
|
@@ -79,6 +79,9 @@ module Volt
|
|
|
79
79
|
|
|
80
80
|
# Load all templates in the folder
|
|
81
81
|
Dir["#{views_path}*/*.{#{exts.join(',')}}"].sort.each do |view_path|
|
|
82
|
+
path_parts = view_path.scan(/([^\/]+)\/([^\/]+)\/[^\/]+\/([^\/]+)[.](html|email)$/)
|
|
83
|
+
component_name, controller_name, view, _ = path_parts[0]
|
|
84
|
+
|
|
82
85
|
# file extension
|
|
83
86
|
format = File.extname(view_path).downcase.delete('.').to_sym
|
|
84
87
|
|
|
@@ -88,6 +91,8 @@ module Volt
|
|
|
88
91
|
|
|
89
92
|
file_contents = File.read(view_path)
|
|
90
93
|
|
|
94
|
+
template_calls = []
|
|
95
|
+
|
|
91
96
|
# Process template if we have a handler for this file type
|
|
92
97
|
if handler = ComponentTemplates.handler_for_extension(format)
|
|
93
98
|
file_contents = handler.call(file_contents)
|
|
@@ -107,8 +112,11 @@ module Volt
|
|
|
107
112
|
binding_code = "{#{binding_code.join(', ')}}"
|
|
108
113
|
|
|
109
114
|
code << "#{page_reference}.add_template(#{name.inspect}, #{template['html'].inspect}, #{binding_code})\n"
|
|
115
|
+
template_calls << "template(#{name.inspect}, #{template['html'].inspect}, #{binding_code})"
|
|
110
116
|
end
|
|
111
117
|
end
|
|
118
|
+
|
|
119
|
+
# puts "module #{component_name.camelize}\n class #{controller_name.camelize}\n class VoltTemplates < VoltTemplates\n #{template_calls.join("\n")}\n end\n end\nend"
|
|
112
120
|
end
|
|
113
121
|
|
|
114
122
|
code
|
|
@@ -118,9 +126,29 @@ module Volt
|
|
|
118
126
|
def generate_controller_code
|
|
119
127
|
code = ''
|
|
120
128
|
controllers_path = "#{@component_path}/controllers/"
|
|
129
|
+
views_path = "#{@component_path}/views/"
|
|
121
130
|
|
|
122
|
-
|
|
123
|
-
|
|
131
|
+
# Controllers are optional, specifying a view folder is enough to auto
|
|
132
|
+
# generate the controller.
|
|
133
|
+
|
|
134
|
+
implicit_controllers = Dir["#{views_path}*"].sort.map do |path|
|
|
135
|
+
# remove the /views/ folder and add _controller.rb
|
|
136
|
+
path.split('/').tap {|v| v[-2] = 'controllers' }.join('/') + '_controller.rb'
|
|
137
|
+
end
|
|
138
|
+
explicit_controllers = Dir["#{controllers_path}*_controller.rb"].sort
|
|
139
|
+
|
|
140
|
+
controllers = (implicit_controllers + explicit_controllers).uniq
|
|
141
|
+
controllers.each do |path|
|
|
142
|
+
if File.exists?(path)
|
|
143
|
+
code << File.read(path) + "\n\n"
|
|
144
|
+
else
|
|
145
|
+
# parts = path.scan(/([^\/]+)\/controllers\/([^\/]+)_controller[.]rb$/)
|
|
146
|
+
# component, controller = parts[0]
|
|
147
|
+
|
|
148
|
+
# # Generate a blank controller. (We need to actually generate one so
|
|
149
|
+
# # the Template can be attached to it for template inheritance)
|
|
150
|
+
# code << "\nmodule #{component.camelize}\n class #{controller.camelize} < Volt::ModelController\n end\nend\n"
|
|
151
|
+
end
|
|
124
152
|
end
|
|
125
153
|
|
|
126
154
|
code
|
|
@@ -58,11 +58,11 @@ module Volt
|
|
|
58
58
|
# Running as child
|
|
59
59
|
@reader.close
|
|
60
60
|
|
|
61
|
-
@server.boot_volt
|
|
61
|
+
volt_app = @server.boot_volt
|
|
62
62
|
@rack_app = @server.new_server
|
|
63
63
|
|
|
64
64
|
# Set the drb object locally
|
|
65
|
-
@dispatcher = Dispatcher.new
|
|
65
|
+
@dispatcher = Dispatcher.new(volt_app)
|
|
66
66
|
drb_object = DRb.start_service('drbunix:', [self, @dispatcher])
|
|
67
67
|
|
|
68
68
|
@writer.puts(drb_object.uri)
|