volt 0.9.5 → 0.9.6.pre1
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/CHANGELOG.md +10 -0
- data/app/volt/tasks/query_tasks.rb +1 -1
- data/app/volt/tasks/user_tasks.rb +6 -0
- data/docs/UPGRADE_GUIDE.md +19 -0
- data/lib/volt.rb +0 -1
- data/lib/volt/cli.rb +3 -0
- data/lib/volt/cli/destroy.rb +8 -0
- data/lib/volt/cli/generate.rb +1 -105
- data/lib/volt/cli/generators.rb +111 -0
- data/lib/volt/controllers/model_controller.rb +1 -1
- data/lib/volt/helpers/time.rb +5 -5
- data/lib/volt/models/array_model.rb +1 -1
- data/lib/volt/models/helpers/base.rb +28 -12
- data/lib/volt/models/persistors/page.rb +6 -6
- data/lib/volt/models/persistors/query/query_listener.rb +1 -1
- data/lib/volt/page/bindings/each_binding.rb +1 -1
- data/lib/volt/page/channel.rb +18 -7
- data/lib/volt/page/tasks.rb +10 -4
- data/lib/volt/reactive/computation.rb +20 -8
- data/lib/volt/reactive/dependency.rb +3 -1
- data/lib/volt/server/component_templates.rb +4 -3
- data/lib/volt/server/middleware/default_middleware_stack.rb +6 -6
- data/lib/volt/server/rack/index_files.rb +0 -12
- data/lib/volt/server/rack/opal_files.rb +1 -1
- data/lib/volt/server/socket_connection_handler.rb +40 -1
- data/lib/volt/server/template_handlers/sprockets_component_handler.rb +5 -2
- data/lib/volt/server/template_handlers/view_processor.rb +4 -4
- data/lib/volt/tasks/task.rb +2 -1
- data/lib/volt/utils/csso_patch.rb +1 -1
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +9 -0
- data/lib/volt/volt/server_setup/app.rb +19 -0
- data/lib/volt/volt/users.rb +4 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +3 -0
- data/spec/apps/kitchen_sink/app/main/models/user.rb +18 -0
- data/spec/apps/kitchen_sink/app/main/views/main/callbacks.html +7 -0
- data/spec/integration/bindings_spec.rb +1 -1
- data/spec/integration/callbacks_spec.rb +31 -0
- data/spec/integration/todos_spec.rb +2 -2
- data/spec/models/array_model_spec.rb +13 -0
- data/spec/models/associations_spec.rb +1 -1
- data/spec/models/field_helpers_spec.rb +1 -1
- data/spec/models/model_spec.rb +18 -0
- data/spec/models/permissions_spec.rb +1 -2
- data/spec/models/persistors/page_spec.rb +19 -0
- data/spec/reactive/computation_spec.rb +33 -0
- data/spec/server/socket_connection_handler_spec.rb +99 -0
- data/spec/tasks/dispatcher_spec.rb +1 -1
- data/spec/tasks/user_tasks_spec.rb +1 -1
- data/spec/utils/task_argument_filtererer_spec.rb +1 -1
- data/templates/project/Gemfile.tt +1 -1
- data/templates/project/README.md.tt +1 -1
- data/templates/project/config/app.rb.tt +10 -0
- data/templates/view/index.html.tt +1 -1
- metadata +14 -5
- data/lib/volt/utils/set_patch.rb +0 -25
@@ -47,7 +47,7 @@ module Volt
|
|
47
47
|
Volt.logger.error(msg)
|
48
48
|
|
49
49
|
# If we get back that the user signature is wrong, log the user out.
|
50
|
-
if err.
|
50
|
+
if err.start_with?('VoltUserError:')
|
51
51
|
# Delete the invalid cookie
|
52
52
|
Volt.current_app.cookies.delete(:user_id)
|
53
53
|
end
|
@@ -104,7 +104,7 @@ module Volt
|
|
104
104
|
dom_section.insert_anchor_before(binding_name, @templates[position].binding_name)
|
105
105
|
end
|
106
106
|
|
107
|
-
# TODORW: :
|
107
|
+
# TODORW: parent: @value may change
|
108
108
|
item_context = SubContext.new({ _index_value: position, parent: @value }, @context)
|
109
109
|
item_context.locals[@item_name.to_sym] = proc do
|
110
110
|
# Fetch only whats there currently, no promises.
|
data/lib/volt/page/channel.rb
CHANGED
@@ -27,14 +27,25 @@ module Volt
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def connect!
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
# The websocket url can be overridden by config.public.websocket_url
|
31
|
+
socket_url = Volt.config.try(:public).try(:websocket_url) || begin
|
32
|
+
"#{`document.location.host`}/socket"
|
33
|
+
end
|
34
|
+
|
35
|
+
if socket_url !~ /^wss?[:]\/\//
|
36
|
+
if socket_url !~ /^[:]\/\//
|
37
|
+
# Add :// to the front
|
38
|
+
socket_url = "://#{socket_url}"
|
39
|
+
end
|
40
|
+
|
41
|
+
ws_proto = (`document.location.protocol` == 'https://') ? 'wss' : 'ws'
|
36
42
|
|
37
|
-
|
43
|
+
# Add wss? to the front
|
44
|
+
socket_url = "#{ws_proto}#{socket_url}"
|
45
|
+
end
|
46
|
+
|
47
|
+
`
|
48
|
+
this.socket = new WebSocket(socket_url);
|
38
49
|
|
39
50
|
this.socket.onopen = function () {
|
40
51
|
self.$opened();
|
data/lib/volt/page/tasks.rb
CHANGED
@@ -73,11 +73,17 @@ module Volt
|
|
73
73
|
|
74
74
|
def reload
|
75
75
|
# Stash the current page value
|
76
|
-
|
76
|
+
begin
|
77
|
+
value = EJSON.stringify(Volt.current_app.page.to_h)
|
77
78
|
|
78
|
-
|
79
|
-
|
80
|
-
|
79
|
+
# If this browser supports session storage, store the page, so it will
|
80
|
+
# be in the same state when we reload.
|
81
|
+
`sessionStorage.setItem('___page', value);` if `sessionStorage`
|
82
|
+
rescue EJSON::NonEjsonType => e
|
83
|
+
# Unable to serailize the page, ignore stashing it
|
84
|
+
# clear the ___page stash
|
85
|
+
`sessionStorage.removeItem('___page');`
|
86
|
+
end
|
81
87
|
|
82
88
|
Volt.current_app.page._reloading = true
|
83
89
|
`window.location.reload(false);`
|
@@ -21,21 +21,33 @@ module Volt
|
|
21
21
|
|
22
22
|
# Runs the computation, called on initial run and
|
23
23
|
# when changed!
|
24
|
-
def compute!
|
24
|
+
def compute!(initial_run=false)
|
25
25
|
@invalidated = false
|
26
26
|
|
27
27
|
unless @stopped
|
28
28
|
|
29
29
|
@computing = true
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
begin
|
31
|
+
run_in do
|
32
|
+
if @computation.arity > 0
|
33
|
+
# Pass in the Computation so it can be canceled from within
|
34
|
+
@computation.call(self)
|
35
|
+
else
|
36
|
+
@computation.call
|
37
|
+
end
|
38
|
+
end
|
39
|
+
rescue => e
|
40
|
+
if initial_run
|
41
|
+
# Re-raise if we are in the initial run
|
42
|
+
raise
|
34
43
|
else
|
35
|
-
|
44
|
+
msg = "Exception During Compute: " + e.inspect
|
45
|
+
msg += "\n" + e.backtrace.join("\n") if e.respond_to?(:backtrace)
|
46
|
+
Volt.logger.error(msg)
|
36
47
|
end
|
48
|
+
ensure
|
49
|
+
@computing = false
|
37
50
|
end
|
38
|
-
@computing = false
|
39
51
|
end
|
40
52
|
end
|
41
53
|
|
@@ -140,7 +152,7 @@ class Proc
|
|
140
152
|
computation = Volt::Computation.new(self)
|
141
153
|
|
142
154
|
# Initial run
|
143
|
-
computation.compute!
|
155
|
+
computation.compute!(true)
|
144
156
|
|
145
157
|
# return the computation
|
146
158
|
computation
|
@@ -34,7 +34,7 @@ module Volt
|
|
34
34
|
# If @dependencies is nil, this Dependency has been removed
|
35
35
|
if @dependencies
|
36
36
|
# For set, .delete returns a boolean if it was deleted
|
37
|
-
deleted = @dependencies.delete(current)
|
37
|
+
deleted = @dependencies.delete?(current)
|
38
38
|
|
39
39
|
# Call on stop dep if no more deps
|
40
40
|
@on_stop_dep.call if @on_stop_dep && deleted && @dependencies.size == 0
|
@@ -54,6 +54,8 @@ module Volt
|
|
54
54
|
|
55
55
|
deps.each(&:invalidate!)
|
56
56
|
|
57
|
+
# Call on stop dep here because we are clearing out the @dependencies, so
|
58
|
+
# it won't trigger on the invalidates
|
57
59
|
@on_stop_dep.call if @on_stop_dep
|
58
60
|
end
|
59
61
|
|
@@ -66,15 +66,16 @@ module Volt
|
|
66
66
|
# handle things.
|
67
67
|
code << "\nrequire '#{require_path}'\n"
|
68
68
|
else
|
69
|
+
valid_exts_re = exts.join('|')
|
69
70
|
# On the sever side, we eval the compiled code
|
70
|
-
path_parts = view_path.scan(/([^\/]+)\/([^\/]+)\/[^\/]+\/([^\/]+)[.](
|
71
|
+
path_parts = view_path.scan(/([^\/]+)\/([^\/]+)\/[^\/]+\/([^\/]+)[.](#{valid_exts_re})$/)
|
71
72
|
component_name, controller_name, view, _ = path_parts[0]
|
72
73
|
|
73
74
|
# file extension
|
74
75
|
format = File.extname(view_path).downcase.delete('.').to_sym
|
75
76
|
|
76
77
|
# Get the path for the template, supports templates in folders
|
77
|
-
template_path = view_path[views_path.size..-1].gsub(/[.](#{
|
78
|
+
template_path = view_path[views_path.size..-1].gsub(/[.](#{valid_exts_re})$/, '')
|
78
79
|
template_path = "#{@component_name}/#{template_path}"
|
79
80
|
|
80
81
|
html = File.read(view_path)
|
@@ -189,4 +190,4 @@ module Volt
|
|
189
190
|
|
190
191
|
|
191
192
|
end
|
192
|
-
end
|
193
|
+
end
|
@@ -26,11 +26,11 @@ module Volt
|
|
26
26
|
rack_app.use Rack::ETag
|
27
27
|
|
28
28
|
rack_app.use Rack::Session::Cookie, {
|
29
|
-
:
|
30
|
-
# :
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
29
|
+
key: 'rack.session',
|
30
|
+
# domain: 'localhost.com',
|
31
|
+
path: '/',
|
32
|
+
expire_after: 2592000,
|
33
|
+
secret: Volt.config.app_secret
|
34
34
|
}
|
35
35
|
|
36
36
|
rack_app.use QuietCommonLogger
|
@@ -57,7 +57,7 @@ module Volt
|
|
57
57
|
|
58
58
|
# serve assets from public
|
59
59
|
rack_app.use Rack::Static,
|
60
|
-
urls: ['
|
60
|
+
urls: [''],
|
61
61
|
root: 'public',
|
62
62
|
index: 'index.html',
|
63
63
|
header_rules: [
|
@@ -11,18 +11,6 @@ module Volt
|
|
11
11
|
@opal_files = opal_files
|
12
12
|
|
13
13
|
@@router = volt_app.router
|
14
|
-
|
15
|
-
@@router.define do
|
16
|
-
# Load routes for each component
|
17
|
-
component_paths.components.values.flatten.uniq.each do |component_path|
|
18
|
-
routes_path = "#{component_path}/config/routes.rb"
|
19
|
-
|
20
|
-
if File.exist?(routes_path)
|
21
|
-
route_file = File.read(routes_path)
|
22
|
-
instance_eval(route_file, routes_path, 0)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
14
|
end
|
27
15
|
|
28
16
|
def route_match?(path)
|
@@ -110,7 +110,7 @@ module Volt
|
|
110
110
|
def add_image_compression
|
111
111
|
if defined?(ImageOptim)
|
112
112
|
env = @environment
|
113
|
-
image_optim = ImageOptim.new({:
|
113
|
+
image_optim = ImageOptim.new({pngout: false, svgo: false})
|
114
114
|
|
115
115
|
processor = proc do |_context, data|
|
116
116
|
image_optim.optimize_image_data(data) || data
|
@@ -15,6 +15,29 @@ module Volt
|
|
15
15
|
|
16
16
|
@@channels ||= []
|
17
17
|
@@channels << self
|
18
|
+
|
19
|
+
# Trigger a client connect event
|
20
|
+
@@dispatcher.volt_app.trigger!("client_connect")
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_user_id(user_id)
|
25
|
+
if !@user_id && user_id
|
26
|
+
# If there is currently no user id associated with this channel
|
27
|
+
# and we get a new valid user_id, set it then trigger a
|
28
|
+
# user_connect event
|
29
|
+
@user_id = user_id
|
30
|
+
@@dispatcher.volt_app.trigger!("user_connect", @user_id)
|
31
|
+
elsif @user_id && !user_id
|
32
|
+
# If there is currently a user id associated with this channel
|
33
|
+
# and we get a nil user id, trigger a user_disconnect event then
|
34
|
+
# set the id to nil
|
35
|
+
@@dispatcher.volt_app.trigger!("user_disconnect", @user_id)
|
36
|
+
@user_id = user_id
|
37
|
+
else
|
38
|
+
# Otherwise, lets just set the id (should never really run)
|
39
|
+
@user_id = user_id
|
40
|
+
end
|
18
41
|
end
|
19
42
|
|
20
43
|
def self.dispatcher=(val)
|
@@ -22,7 +45,11 @@ module Volt
|
|
22
45
|
end
|
23
46
|
|
24
47
|
def self.dispatcher
|
25
|
-
@@dispatcher
|
48
|
+
defined?(@@dispatcher) ? @@dispatcher : nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.channels
|
52
|
+
@@channels
|
26
53
|
end
|
27
54
|
|
28
55
|
# Sends a message to all, optionally skipping a users channel
|
@@ -85,6 +112,18 @@ module Volt
|
|
85
112
|
|
86
113
|
begin
|
87
114
|
@@dispatcher.close_channel(self)
|
115
|
+
|
116
|
+
# Check for volt_app (@@dispatcher could be an ErrorDispatcher)
|
117
|
+
if @@dispatcher.respond_to?(:volt_app)
|
118
|
+
# Trigger a client disconnect event
|
119
|
+
@@dispatcher.volt_app.trigger!("client_disconnect")
|
120
|
+
|
121
|
+
# Trigger a user disconnect event even if the user hasn't logged out
|
122
|
+
if @user_id
|
123
|
+
@@dispatcher.volt_app.trigger!("user_disconnect", @user_id)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
88
127
|
rescue DRb::DRbConnError => e
|
89
128
|
# ignore drb read of @@dispatcher error if child has closed
|
90
129
|
end
|
@@ -54,8 +54,11 @@ module Sprockets
|
|
54
54
|
data = env.read_file(input[:filename], input[:content_type])
|
55
55
|
end
|
56
56
|
|
57
|
-
dependencies = Set.new(input[:metadata][:dependencies])
|
58
|
-
dependencies += [env.build_file_digest_uri(input[:filename])]
|
57
|
+
# dependencies = Set.new(input[:metadata][:dependencies])
|
58
|
+
# dependencies += [env.build_file_digest_uri(input[:filename])]
|
59
|
+
|
60
|
+
dependencies = input[:metadata][:dependencies]
|
61
|
+
# dependencies.merge(env.build_file_digest_uri(input[:filename]))
|
59
62
|
|
60
63
|
{ data: data, dependencies: dependencies }
|
61
64
|
end
|
@@ -23,7 +23,7 @@ module Volt
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def cache_key
|
26
|
-
@cache_key ||= "#{self.class.name}:0.
|
26
|
+
@cache_key ||= "#{self.class.name}:0.2".freeze
|
27
27
|
end
|
28
28
|
|
29
29
|
# def evaluate(context, locals, &block)
|
@@ -65,8 +65,6 @@ module Volt
|
|
65
65
|
exts = ComponentTemplates::Preprocessors.extensions
|
66
66
|
template_path = view_path.split('/')[-4..-1].join('/').gsub('/views/', '/').gsub(/[.](#{exts.join('|')})$/, '')
|
67
67
|
|
68
|
-
exts = ComponentTemplates::Preprocessors.extensions
|
69
|
-
|
70
68
|
format = File.extname(view_path).downcase.delete('.').to_sym
|
71
69
|
code = ''
|
72
70
|
|
@@ -82,7 +80,9 @@ module Volt
|
|
82
80
|
end
|
83
81
|
|
84
82
|
def self.setup(sprockets=$volt_app.sprockets)
|
85
|
-
|
83
|
+
exts = ComponentTemplates::Preprocessors.extensions.map{ |ext| ".#{ext}" }
|
84
|
+
|
85
|
+
sprockets.register_mime_type 'application/vtemplate', extensions: exts
|
86
86
|
sprockets.register_transformer 'application/vtemplate', 'application/javascript', Volt::ViewProcessor.new(true)
|
87
87
|
end
|
88
88
|
end
|
data/lib/volt/tasks/task.rb
CHANGED
@@ -31,7 +31,7 @@ module Csso
|
|
31
31
|
})
|
32
32
|
sprockets.css_compressor = :csso
|
33
33
|
else
|
34
|
-
Sprockets::Compressors.register_css_compressor(:csso, 'Csso::Compressor', :
|
34
|
+
Sprockets::Compressors.register_css_compressor(:csso, 'Csso::Compressor', default: true)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
data/lib/volt/version.rb
CHANGED
data/lib/volt/volt/app.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
require 'opal'
|
2
2
|
|
3
|
+
# On the server, setup the server env by default
|
4
|
+
unless RUBY_PLATFORM == 'opal'
|
5
|
+
ENV['SERVER'] ||= 'true'
|
6
|
+
end
|
7
|
+
|
3
8
|
# on the client, we want to include the main volt.rb file
|
4
9
|
require 'volt'
|
5
10
|
require 'volt/models'
|
@@ -48,6 +53,8 @@ module Volt
|
|
48
53
|
attr_accessor :sprockets, :opal_files
|
49
54
|
|
50
55
|
def initialize(app_path=nil)
|
56
|
+
app_path ||= Dir.pwd
|
57
|
+
|
51
58
|
if Volt.server? && !app_path
|
52
59
|
raise "Volt::App.new requires an app path to boot"
|
53
60
|
end
|
@@ -102,6 +109,8 @@ module Volt
|
|
102
109
|
# Setup the middleware that we can only setup after all components boot.
|
103
110
|
setup_postboot_middleware
|
104
111
|
|
112
|
+
setup_routes
|
113
|
+
|
105
114
|
start_message_bus
|
106
115
|
end
|
107
116
|
end
|
@@ -10,6 +10,9 @@ end
|
|
10
10
|
module Volt
|
11
11
|
module ServerSetup
|
12
12
|
module App
|
13
|
+
# Include Eventable to allow for lifecycle callbacks
|
14
|
+
include Eventable
|
15
|
+
|
13
16
|
# The root url is where the volt app is mounted
|
14
17
|
attr_reader :root_url
|
15
18
|
# The app url is where the app folder (and sprockets) is mounted
|
@@ -37,6 +40,22 @@ module Volt
|
|
37
40
|
@router = Routes.new
|
38
41
|
end
|
39
42
|
|
43
|
+
def setup_routes
|
44
|
+
component_paths = @component_paths
|
45
|
+
@router.define do
|
46
|
+
# Load routes for each component
|
47
|
+
component_paths.components.values.flatten.uniq.each do |component_path|
|
48
|
+
routes_path = "#{component_path}/config/routes.rb"
|
49
|
+
|
50
|
+
if File.exist?(routes_path)
|
51
|
+
route_file = File.read(routes_path)
|
52
|
+
instance_eval(route_file, routes_path, 0)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
40
59
|
def setup_preboot_middleware
|
41
60
|
@middleware = MiddlewareStack.new
|
42
61
|
DefaultMiddlewareStack.preboot_setup(self, @middleware)
|
data/lib/volt/volt/users.rb
CHANGED
@@ -114,6 +114,10 @@ module Volt
|
|
114
114
|
end
|
115
115
|
|
116
116
|
def logout
|
117
|
+
# Notify the backend so we can remove the user_id from the user's channel
|
118
|
+
UserTasks.logout
|
119
|
+
|
120
|
+
# Remove the cookie so user is no longer logged in
|
117
121
|
Volt.current_app.cookies.delete(:user_id)
|
118
122
|
end
|
119
123
|
|
@@ -14,6 +14,7 @@ client '/missing', action: 'missing'
|
|
14
14
|
client '/require_test', action: 'require_test'
|
15
15
|
client '/images', action: 'images'
|
16
16
|
client '/login_from_task', action: 'login_from_task'
|
17
|
+
client '/callbacks', action: 'callbacks'
|
17
18
|
|
18
19
|
# Events
|
19
20
|
client '/events', component: 'main', controller: 'events', action: 'index'
|