volt 0.9.5 → 0.9.6.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|