volt 0.9.5.pre9 → 0.9.5.pre11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -1
- data/docs/UPGRADE_GUIDE.md +8 -0
- data/lib/volt/cli.rb +8 -2
- data/lib/volt/cli/new_gem.rb +1 -1
- data/lib/volt/controllers/http_controller.rb +3 -0
- data/lib/volt/controllers/login_as_helper.rb +12 -0
- data/lib/volt/models.rb +1 -2
- data/lib/volt/models/persistors/query/query_listener.rb +1 -1
- data/lib/volt/models/url.rb +4 -0
- data/lib/volt/page/bindings/attribute_binding.rb +46 -20
- data/lib/volt/page/tasks.rb +10 -2
- data/lib/volt/server/component_templates.rb +14 -8
- data/lib/volt/server/message_bus/message_encoder.rb +1 -1
- data/lib/volt/server/rack/asset_files.rb +14 -4
- data/lib/volt/server/rack/component_code.rb +8 -6
- data/lib/volt/server/rack/component_paths.rb +9 -2
- data/lib/volt/server/rack/sprockets_helpers_setup.rb +3 -2
- data/lib/volt/server/socket_connection_handler.rb +10 -1
- data/lib/volt/server/template_handlers/view_processor.rb +5 -1
- data/lib/volt/tasks/dispatcher.rb +13 -6
- data/lib/volt/tasks/task.rb +13 -0
- data/lib/volt/utils/ejson.rb +10 -5
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +2 -0
- data/lib/volt/volt/server_setup/app.rb +4 -0
- data/spec/apps/kitchen_sink/app/main/assets/css/app.scss +11 -0
- data/spec/apps/kitchen_sink/app/main/assets/images/volt-logo.jpg +0 -0
- data/spec/apps/kitchen_sink/app/main/config/initializers/sample_model_extend.rb +7 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +2 -0
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +5 -1
- data/spec/apps/kitchen_sink/app/main/tasks/login_tasks.rb +7 -0
- data/spec/apps/kitchen_sink/app/main/views/main/images.html +10 -0
- data/spec/apps/kitchen_sink/app/main/views/main/login_from_task.html +7 -0
- data/spec/integration/images_spec.rb +10 -0
- data/spec/integration/user_spec.rb +8 -0
- data/spec/tasks/dispatcher_spec.rb +19 -4
- data/spec/utils/ejson_spec.rb +6 -0
- data/templates/project/config/app.rb.tt +6 -0
- data/templates/project/config/initializers/boot.rb +4 -0
- data/volt.gemspec +1 -1
- metadata +26 -7
- data/templates/project/config/initializers/.empty_directory +0 -0
- data/templates/project/config/initializers/client/.empty_directory +0 -0
- data/templates/project/config/initializers/server/.empty_directory +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c855e92da3bc6bcee176d539ae365747bfe848e
|
4
|
+
data.tar.gz: ca339e18a47c685787f2e55eed17bd49f2c5de13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ec01b186962830327f074a0c061ed6a41c4735ac12516175a666d1175a016da1a649b0dbf3be8275de78f8467df593d3d7d35dcfdf3453e5ebfa16dabf59257
|
7
|
+
data.tar.gz: ae01a3abe872c61cadd82426be2e940242d533bbd66a7cc342187a8a3b24ad139af02b3e5bcc036183af9c785234f62b6757cbca3fa82e68a2b017089938b210
|
data/CHANGELOG.md
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
- You can now disable auto-import of JS/CSS with ```disable_auto_import``` in a dependencies.rb file
|
9
9
|
- Opal was upgraded to 0.8, which brings sourcemaps back (yah!)
|
10
10
|
- Page load performance was improved, and more of sprockets was used for component loading.
|
11
|
-
- You can now return promises in permissions blocks. Also, can_read?, can_create?, and .can_delete?
|
11
|
+
- You can now return promises in permissions blocks. Also, can_read?, can_create?, and .can_delete? now return promises.
|
12
12
|
- Anything in /public is now served via Rack::Static in the default middleware stack. (So you can put user uploaded images in there)
|
13
13
|
- You can now use _ or - in volt tag names and attributes. (We're moving to using dash ( - ) as the standard in html)
|
14
14
|
- You can now trigger events on controllers rendered as tags. The events will bubble up through the DOM and can be caught by any e- bindings. See the docs for more information.
|
@@ -21,6 +21,10 @@
|
|
21
21
|
### Changed
|
22
22
|
- fix issue with ```raw``` and promises (#275)
|
23
23
|
- fix issue with .length on store (#269)
|
24
|
+
- The {root}/config/initializers directory is now only for server side code.
|
25
|
+
- Redid the initializer load order so all initializers run before any controllers/models/views are loaded.
|
26
|
+
- Added error message for when an unserializable object is returned from a Task
|
27
|
+
- Fixed issue with disable_encryption option
|
24
28
|
|
25
29
|
## 0.9.4
|
26
30
|
### Lingo Change
|
data/docs/UPGRADE_GUIDE.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# 0.9.4 to 0.9.5
|
2
|
+
|
3
|
+
CSS url's now should be referenced either 1) as relative paths from the css file, or 2) using the full path from inside of app (eg: main/assets/images/background.jpg)
|
4
|
+
|
5
|
+
On models, .can_delete?, .can_read?, and .can_create? now return promises.
|
6
|
+
|
7
|
+
Check the CHANGELOG for more info.
|
8
|
+
|
1
9
|
# 0.9.3 to 0.9.4
|
2
10
|
|
3
11
|
We moved logic out of Volt::User and into the generated user file, so it is easier to customize. Add the following to your app/main/models/user.rb:
|
data/lib/volt/cli.rb
CHANGED
@@ -120,11 +120,17 @@ module Volt
|
|
120
120
|
no_tasks do
|
121
121
|
# The logic for creating a new project. We want to be able to invoke this
|
122
122
|
# inside of a method so we can run it with Dir.chdir
|
123
|
-
def new_project(name, skip_gemfile = false)
|
123
|
+
def new_project(name, skip_gemfile = false, disable_encryption = false)
|
124
124
|
require 'securerandom'
|
125
125
|
|
126
126
|
# Grab the current volt version
|
127
|
-
directory('project', name,
|
127
|
+
directory('project', name, {
|
128
|
+
version: Volt::Version::STRING,
|
129
|
+
name: name,
|
130
|
+
domain: name.dasherize.downcase,
|
131
|
+
app_name: name.capitalize,
|
132
|
+
disable_encryption: disable_encryption
|
133
|
+
})
|
128
134
|
|
129
135
|
unless skip_gemfile
|
130
136
|
# Move into the directory
|
data/lib/volt/cli/new_gem.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
require 'volt/server/rack/http_response_header'
|
2
2
|
require 'volt/server/rack/http_response_renderer'
|
3
3
|
require 'volt/controllers/http_controller/http_cookie_persistor'
|
4
|
+
require 'volt/controllers/login_as_helper'
|
4
5
|
require 'volt/utils/lifecycle_callbacks'
|
5
6
|
|
6
7
|
module Volt
|
7
8
|
# Allow you to create controllers that act as http endpoints
|
8
9
|
class HttpController
|
9
10
|
include LifecycleCallbacks
|
11
|
+
include LoginAsHelper
|
12
|
+
|
10
13
|
# Setup before_action and after_action
|
11
14
|
setup_action_helpers_in_class(:before_action, :after_action)
|
12
15
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Volt
|
2
|
+
module LoginAsHelper
|
3
|
+
def login_as(user)
|
4
|
+
unless user.is_a?(Volt::User)
|
5
|
+
raise "login_as must be passed a user instance, you passed a #{user.class.to_s}"
|
6
|
+
end
|
7
|
+
|
8
|
+
# Assign the user_id cookie to the signature for the user id
|
9
|
+
cookies._user_id = Volt.user_login_signature(user)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/volt/models.rb
CHANGED
@@ -16,9 +16,8 @@ require 'volt/models/root_models/root_models'
|
|
16
16
|
if RUBY_PLATFORM == 'opal'
|
17
17
|
require 'promise'
|
18
18
|
else
|
19
|
-
# Opal doesn't expose its promise library directly
|
20
19
|
require 'opal'
|
21
|
-
|
20
|
+
# Opal doesn't expose its promise library directly
|
22
21
|
gem_dir = File.join(Opal.gem_dir, '..')
|
23
22
|
require(gem_dir + '/stdlib/promise')
|
24
23
|
end
|
@@ -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.starts_with?('VoltUserError:')
|
51
51
|
# Delete the invalid cookie
|
52
52
|
Volt.current_app.cookies.delete(:user_id)
|
53
53
|
end
|
data/lib/volt/models/url.rb
CHANGED
@@ -72,6 +72,10 @@ module Volt
|
|
72
72
|
|
73
73
|
path, params = @router.params_to_url(params)
|
74
74
|
|
75
|
+
if path == nil
|
76
|
+
raise "No route matched, make sure you have the base route defined last: `client '/', {}`"
|
77
|
+
end
|
78
|
+
|
75
79
|
new_url = "#{scheme}://#{host_with_port}#{path.chomp('/')}"
|
76
80
|
|
77
81
|
# Add query params
|
@@ -14,6 +14,39 @@ module Volt
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def setup
|
17
|
+
if `#{element}.is('select')`
|
18
|
+
@is_select = true
|
19
|
+
elsif `#{element}.is('[type=hidden]')`
|
20
|
+
@is_hidden = true
|
21
|
+
elsif `#{element}.is('[type=radio]')`
|
22
|
+
@is_radio = true
|
23
|
+
@selected_value = `#{element}.attr('value') || ''`
|
24
|
+
elsif `#{element}.is('option')`
|
25
|
+
@is_option = true
|
26
|
+
end
|
27
|
+
|
28
|
+
if @is_option
|
29
|
+
else
|
30
|
+
# Bind so when this value updates, we update
|
31
|
+
case @attribute_name
|
32
|
+
when 'value'
|
33
|
+
changed_event = Proc.new { changed }
|
34
|
+
if @is_select
|
35
|
+
`#{element}.on('change.attrbind', #{changed_event})`
|
36
|
+
|
37
|
+
invalidate_proc = Proc.new { invalidate }
|
38
|
+
`#{element}.on('invalidate', #{invalidate_proc})`
|
39
|
+
elsif @is_hidden
|
40
|
+
`#{element}.watch('value', #{changed_event})`
|
41
|
+
else
|
42
|
+
`#{element}.on('input.attrbind', #{changed_event})`
|
43
|
+
end
|
44
|
+
when 'checked'
|
45
|
+
changed_event = proc { |event| changed(event) }
|
46
|
+
`#{element}.on('change.attrbind', #{changed_event})`
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
17
50
|
# Listen for changes
|
18
51
|
@computation = lambda do
|
19
52
|
begin
|
@@ -27,26 +60,6 @@ module Volt
|
|
27
60
|
method(:getter_fail)
|
28
61
|
)
|
29
62
|
|
30
|
-
@is_select = `#{element}.is('select')`
|
31
|
-
@is_hidden = `#{element}.is('[type=hidden]')`
|
32
|
-
@is_radio = `#{element}.is('[type=radio]')`
|
33
|
-
@selected_value = `#{element}.attr('value') || ''` if @is_radio
|
34
|
-
|
35
|
-
# Bind so when this value updates, we update
|
36
|
-
case @attribute_name
|
37
|
-
when 'value'
|
38
|
-
changed_event = Proc.new { changed }
|
39
|
-
if @is_select
|
40
|
-
`#{element}.on('change.attrbind', #{changed_event})`
|
41
|
-
elsif @is_hidden
|
42
|
-
`#{element}.watch('value', #{changed_event})`
|
43
|
-
else
|
44
|
-
`#{element}.on('input.attrbind', #{changed_event})`
|
45
|
-
end
|
46
|
-
when 'checked'
|
47
|
-
changed_event = proc { |event| changed(event) }
|
48
|
-
`#{element}.on('change.attrbind', #{changed_event})`
|
49
|
-
end
|
50
63
|
end
|
51
64
|
|
52
65
|
def changed(event = nil)
|
@@ -99,6 +112,12 @@ module Volt
|
|
99
112
|
def value=(val)
|
100
113
|
case @attribute_name
|
101
114
|
when 'value'
|
115
|
+
if @is_option
|
116
|
+
# When a new option is added, we trigger the invalidate event on the
|
117
|
+
# parent select so it will re-run update on the next tick and set
|
118
|
+
# the correct option.
|
119
|
+
`#{element}.parent('select').trigger('invalidate');`
|
120
|
+
end
|
102
121
|
# TODO: only update if its not the same, this keeps it from moving the
|
103
122
|
# cursor in text fields.
|
104
123
|
`#{element}.val(#{val})` if val != `(#{element}.val() || '')`
|
@@ -116,6 +135,12 @@ module Volt
|
|
116
135
|
end
|
117
136
|
end
|
118
137
|
|
138
|
+
# On select boxes, when an option is added/changed, we want to run update
|
139
|
+
# again. By calling invalidate, it will run at most once on the next tick.
|
140
|
+
def invalidate
|
141
|
+
@computation.invalidate!
|
142
|
+
end
|
143
|
+
|
119
144
|
def update_checked(value)
|
120
145
|
value = false if value.is_a?(NilMethodCall) || value.nil?
|
121
146
|
|
@@ -131,6 +156,7 @@ module Volt
|
|
131
156
|
when 'value'
|
132
157
|
if @is_select
|
133
158
|
`#{element}.off('change.attrbind')`
|
159
|
+
`#{element}.off('invalidate')`
|
134
160
|
elsif @is_hidden
|
135
161
|
`#{element}.unwatch('value')`
|
136
162
|
else
|
data/lib/volt/page/tasks.rb
CHANGED
@@ -41,14 +41,22 @@ module Volt
|
|
41
41
|
|
42
42
|
# When a request is sent to the backend, it can attach a callback,
|
43
43
|
# this is called from the backend to pass to the callback.
|
44
|
-
def response(promise_id, result, error)
|
44
|
+
def response(promise_id, result, error, cookies)
|
45
|
+
# Set the cookies
|
46
|
+
if cookies
|
47
|
+
cookies.each do |key, value|
|
48
|
+
@volt_app.cookies.set(key, value)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
45
52
|
promise = @promises.delete(promise_id)
|
46
53
|
|
47
54
|
if promise
|
48
55
|
if error
|
49
56
|
# TODO: full error handling
|
50
57
|
Volt.logger.error('Task Response:')
|
51
|
-
Volt.logger.error(error
|
58
|
+
Volt.logger.error(error)
|
59
|
+
|
52
60
|
promise.reject(error)
|
53
61
|
else
|
54
62
|
promise.resolve(result)
|
@@ -22,12 +22,22 @@ module Volt
|
|
22
22
|
@client = client
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
|
25
|
+
def initializer_code
|
26
|
+
if @client
|
27
|
+
generate_initializers_code
|
28
|
+
else
|
29
|
+
''
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def component_code
|
34
|
+
code = ''
|
35
|
+
|
36
|
+
code << generate_routes_code + generate_view_code
|
27
37
|
if @client
|
28
38
|
# On the backend, we just need the views
|
29
39
|
code << generate_controller_code + generate_model_code +
|
30
|
-
generate_tasks_code
|
40
|
+
generate_tasks_code
|
31
41
|
end
|
32
42
|
|
33
43
|
code
|
@@ -161,16 +171,12 @@ module Volt
|
|
161
171
|
end
|
162
172
|
|
163
173
|
def generate_initializers_code
|
164
|
-
# Include the root initializers
|
165
|
-
paths = Dir["#{Volt.root}/config/initializers/*.rb"]
|
166
|
-
paths += Dir["#{Volt.root}/config/initializers/client/*.rb"]
|
167
|
-
|
168
174
|
paths = Dir["#{@component_path}/config/initializers/*.rb"]
|
169
175
|
paths += Dir["#{@component_path}/config/initializers/client/*.rb"]
|
170
176
|
|
171
177
|
code = "\n" + paths.map { |path| "require '#{localize_path(path)}'" }.join("\n")
|
172
178
|
|
173
|
-
code
|
179
|
+
code + "\n\n"
|
174
180
|
end
|
175
181
|
|
176
182
|
private
|
@@ -13,7 +13,7 @@ module Volt
|
|
13
13
|
end
|
14
14
|
|
15
15
|
# Message bus is encrypted by default
|
16
|
-
disable = Volt.config.message_bus.
|
16
|
+
disable = (msg_bus = Volt.config.message_bus) && msg_bus.disable_encryption
|
17
17
|
@encrypted = !windows && (disable != true)
|
18
18
|
|
19
19
|
if @encrypted
|
@@ -130,9 +130,11 @@ module Volt
|
|
130
130
|
case type
|
131
131
|
when :folder
|
132
132
|
# for a folder, we search for all .js files and return a tag for them
|
133
|
+
base_path = base(path)
|
133
134
|
javascript_files += Dir["#{path}/**/*.js"].sort.map do |folder|
|
134
135
|
# Grab the component folder/assets/js/file.js
|
135
|
-
|
136
|
+
local_path = folder[path.size..-1]
|
137
|
+
@app_url + '/' + base_path + local_path
|
136
138
|
end
|
137
139
|
when :javascript_file
|
138
140
|
# javascript_file is a cdn path to a JS file
|
@@ -175,9 +177,10 @@ module Volt
|
|
175
177
|
# Don't import any css/scss files that start with an underscore, so scss partials
|
176
178
|
# aren't imported by default:
|
177
179
|
# http://sass-lang.com/guide
|
178
|
-
|
179
|
-
|
180
|
-
|
180
|
+
base_path = base(path)
|
181
|
+
css_files += Dir["#{path}/**/[^_]*.{css,scss,sass}"].sort.map do |folder|
|
182
|
+
local_path = folder[path.size..-1].gsub(/[.](scss|sass)$/, '')
|
183
|
+
css_path = @app_url + '/' + base_path + local_path
|
181
184
|
css_path += '.css' unless css_path =~ /[.]css$/
|
182
185
|
css_path
|
183
186
|
end
|
@@ -207,5 +210,12 @@ module Volt
|
|
207
210
|
end
|
208
211
|
end
|
209
212
|
end
|
213
|
+
|
214
|
+
private
|
215
|
+
def base(path)
|
216
|
+
path.split('/')[-2..-1].join('/')
|
217
|
+
end
|
218
|
+
|
219
|
+
|
210
220
|
end
|
211
221
|
end
|
@@ -2,8 +2,8 @@ require 'volt/server/html_parser/view_parser'
|
|
2
2
|
require 'volt/server/component_templates'
|
3
3
|
require 'volt/server/rack/asset_files'
|
4
4
|
|
5
|
-
# Takes in the name and all component paths
|
6
|
-
#
|
5
|
+
# Takes in the name and all component paths returns all of the ruby code for the
|
6
|
+
# component and its dependencies.
|
7
7
|
module Volt
|
8
8
|
class ComponentCode
|
9
9
|
def initialize(volt_app, component_name, component_paths, client = true)
|
@@ -16,15 +16,17 @@ module Volt
|
|
16
16
|
# The client argument is for if this code is being generated for the client
|
17
17
|
def code
|
18
18
|
# Start with config code
|
19
|
-
|
19
|
+
initializer_code = @client ? generate_config_code : ''
|
20
|
+
component_code = ''
|
20
21
|
|
21
22
|
asset_files = AssetFiles.from_cache(@volt_app.app_url, @component_name, @component_paths)
|
22
23
|
asset_files.component_paths.each do |component_path, component_name|
|
23
|
-
|
24
|
-
|
24
|
+
comp_template = ComponentTemplates.new(component_path, component_name, @client)
|
25
|
+
initializer_code << comp_template.initializer_code + "\n\n"
|
26
|
+
component_code << comp_template.component_code + "\n\n"
|
25
27
|
end
|
26
28
|
|
27
|
-
|
29
|
+
initializer_code + component_code
|
28
30
|
end
|
29
31
|
|
30
32
|
def generate_config_code
|
@@ -54,13 +54,20 @@ module Volt
|
|
54
54
|
@components
|
55
55
|
end
|
56
56
|
|
57
|
+
# Setup load path for components
|
58
|
+
def setup_load_paths
|
59
|
+
unless RUBY_PLATFORM == 'opal'
|
60
|
+
app_folders do |app_folder|
|
61
|
+
$LOAD_PATH.unshift(app_folder)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
57
66
|
# Makes each components classes available on the load path, require classes.
|
58
67
|
def require_in_components(volt_app)
|
59
68
|
if RUBY_PLATFORM == 'opal'
|
60
69
|
else
|
61
70
|
app_folders do |app_folder|
|
62
|
-
$LOAD_PATH.unshift(app_folder)
|
63
|
-
|
64
71
|
# Sort so we get consistent load order across platforms
|
65
72
|
Dir["#{app_folder}/*/{controllers,models,tasks}/*.rb"].each do |ruby_file|
|
66
73
|
path = ruby_file.gsub(/^#{app_folder}\//, '')[0..-4]
|
@@ -33,10 +33,11 @@ module Volt
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def add_linking_in_asset_path
|
36
|
+
app_path = @volt_app.app_path
|
36
37
|
@env.context_class.class_eval do
|
37
38
|
# We "freedom-patch" sprockets-helpers asset_path method to
|
38
39
|
# automatically link assets.
|
39
|
-
|
40
|
+
define_method(:asset_path) do |source, options = {}|
|
40
41
|
relative_path = source =~ /^[.][.]\//
|
41
42
|
if relative_path
|
42
43
|
component_root = logical_path.gsub(/\/[^\/]+$/, '')
|
@@ -47,7 +48,7 @@ module Volt
|
|
47
48
|
if relative_path
|
48
49
|
link_path = source
|
49
50
|
else
|
50
|
-
link_path = source.gsub(/^#{
|
51
|
+
link_path = source.gsub(/^#{app_path}\//, '')
|
51
52
|
end
|
52
53
|
|
53
54
|
# Return for absolute urls (one's off site)
|
@@ -54,9 +54,19 @@ module Volt
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
# Used when the message is already encoded
|
58
|
+
def send_string_message(str)
|
59
|
+
send_raw_message(str)
|
60
|
+
end
|
61
|
+
|
57
62
|
def send_message(*args)
|
63
|
+
# Encode as EJSON
|
58
64
|
str = EJSON.stringify([*args])
|
59
65
|
|
66
|
+
send_raw_message(str)
|
67
|
+
end
|
68
|
+
|
69
|
+
def send_raw_message(str)
|
60
70
|
@session.send(str)
|
61
71
|
|
62
72
|
if RUNNING_SERVER == 'thin'
|
@@ -65,7 +75,6 @@ module Volt
|
|
65
75
|
# TODO: Figure out the cause of the issue and submit a fix upstream.
|
66
76
|
EM.next_tick {}
|
67
77
|
end
|
68
|
-
|
69
78
|
end
|
70
79
|
|
71
80
|
def closed
|
@@ -33,19 +33,23 @@ module Volt
|
|
33
33
|
# end
|
34
34
|
|
35
35
|
def call(input)
|
36
|
+
context = input[:environment].context_class.new(input)
|
37
|
+
# context.link_asset('main/assets/images/lombard.jpg')
|
36
38
|
# pp input
|
37
39
|
data = input[:data]
|
38
40
|
|
39
41
|
# input[:accept] = 'application/javascript'
|
40
42
|
# input[:content_type] = 'application/javascript'
|
41
43
|
# input[:environment].content_type = 'application/javascript'
|
42
|
-
input[:cache].fetch([self.cache_key, data]) do
|
44
|
+
data = input[:cache].fetch([self.cache_key, data]) do
|
43
45
|
filename = input[:filename]
|
44
46
|
# puts input[:data].inspect
|
45
47
|
# Remove all semicolons from source
|
46
48
|
# input[:content_type] = 'application/javascript'
|
47
49
|
compile(filename, input[:data])
|
48
50
|
end
|
51
|
+
|
52
|
+
context.metadata.merge(data: data.to_str)
|
49
53
|
end
|
50
54
|
|
51
55
|
def compile(view_path, html)
|
@@ -22,9 +22,9 @@ module Volt
|
|
22
22
|
@worker_pool = Concurrent::ImmediateExecutor.new
|
23
23
|
else
|
24
24
|
@worker_pool = Concurrent::ThreadPoolExecutor.new(
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
min_threads: Volt.config.min_worker_threads,
|
26
|
+
max_threads: Volt.config.max_worker_threads
|
27
|
+
)
|
28
28
|
end
|
29
29
|
|
30
30
|
@worker_timeout = Volt.config.worker_timeout || 60
|
@@ -102,6 +102,7 @@ module Volt
|
|
102
102
|
klass = Object.send(:const_get, class_name)
|
103
103
|
|
104
104
|
promise = Promise.new
|
105
|
+
cookies = nil
|
105
106
|
|
106
107
|
start_time = Time.now.to_f
|
107
108
|
|
@@ -116,7 +117,9 @@ module Volt
|
|
116
117
|
Timeout.timeout(klass.__timeout || @worker_timeout) do
|
117
118
|
Thread.current['meta'] = meta_data
|
118
119
|
begin
|
119
|
-
|
120
|
+
klass_inst = klass.new(@volt_app, channel, self)
|
121
|
+
result = klass_inst.send(method_name, *args)
|
122
|
+
cookies = klass_inst.fetch_cookies
|
120
123
|
ensure
|
121
124
|
Thread.current['meta'] = nil
|
122
125
|
end
|
@@ -143,12 +146,16 @@ module Volt
|
|
143
146
|
|
144
147
|
# Run the promise and pass the return value/error back to the client
|
145
148
|
promise.then do |result|
|
146
|
-
|
149
|
+
reply = EJSON.stringify(['response', callback_id, result, nil, cookies])
|
150
|
+
channel.send_string_message(reply)
|
147
151
|
|
148
152
|
finish.call
|
149
153
|
end.fail do |error|
|
150
154
|
finish.call(error)
|
151
|
-
|
155
|
+
# Convert the error into a string so it can be serialized.
|
156
|
+
error_str = "#{error.class.to_s}: #{error.to_s}"
|
157
|
+
|
158
|
+
channel.send_message('response', callback_id, nil, error_str, cookies)
|
152
159
|
end
|
153
160
|
|
154
161
|
end
|
data/lib/volt/tasks/task.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'volt/controllers/collection_helpers'
|
2
|
+
require 'volt/controllers/login_as_helper'
|
2
3
|
|
3
4
|
module Volt
|
4
5
|
class Task
|
@@ -17,6 +18,7 @@ module Volt
|
|
17
18
|
end
|
18
19
|
else
|
19
20
|
include CollectionHelpers
|
21
|
+
include LoginAsHelper
|
20
22
|
|
21
23
|
class_attribute :__timeout
|
22
24
|
|
@@ -41,6 +43,17 @@ module Volt
|
|
41
43
|
self.__timeout = value
|
42
44
|
end
|
43
45
|
|
46
|
+
def cookies
|
47
|
+
@cookies ||= Model.new
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get the cookies that got set
|
51
|
+
def fetch_cookies
|
52
|
+
if @cookies
|
53
|
+
@cookies.to_h.reject {|k,v| k == :id }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
44
57
|
# On the backend, we proxy all class methods like we would
|
45
58
|
# on the front-end. This returns a promise, even if the
|
46
59
|
# original code did not.
|
data/lib/volt/utils/ejson.rb
CHANGED
@@ -2,6 +2,10 @@ require 'json'
|
|
2
2
|
|
3
3
|
module Volt
|
4
4
|
class EJSON
|
5
|
+
class NonEjsonType < Exception ; end
|
6
|
+
|
7
|
+
OTHER_VALID_CLASSES = [String, Symbol, TrueClass, FalseClass, Numeric, NilClass]
|
8
|
+
|
5
9
|
def self.stringify(obj)
|
6
10
|
encode(obj).to_json
|
7
11
|
end
|
@@ -48,12 +52,13 @@ module Volt
|
|
48
52
|
|
49
53
|
[key, value]
|
50
54
|
end.to_h
|
55
|
+
elsif Time === obj
|
56
|
+
{'$date' => obj.to_i * 1_000}
|
57
|
+
elsif OTHER_VALID_CLASSES.any? {|klass| obj.is_a?(klass) }
|
58
|
+
obj
|
51
59
|
else
|
52
|
-
|
53
|
-
|
54
|
-
else
|
55
|
-
obj
|
56
|
-
end
|
60
|
+
# Not a valid class for serializing, raise an exception
|
61
|
+
raise NonEjsonType, "Unable to serialize #{obj.inspect} to EJSON"
|
57
62
|
end
|
58
63
|
end
|
59
64
|
end
|
data/lib/volt/version.rb
CHANGED
data/lib/volt/volt/app.rb
CHANGED
@@ -80,6 +80,8 @@ module Volt
|
|
80
80
|
# Require in app and initializers
|
81
81
|
run_app_and_initializers unless RUBY_PLATFORM == 'opal'
|
82
82
|
|
83
|
+
require_components
|
84
|
+
|
83
85
|
# abort_on_exception is a useful debugging tool, and in my opinion something
|
84
86
|
# you probbaly want on. That said you can disable it if you need.
|
85
87
|
unless RUBY_PLATFORM == 'opal'
|
Binary file
|
@@ -12,6 +12,8 @@ client '/todos', controller: 'todos'
|
|
12
12
|
client '/html_safe', action: 'html_safe'
|
13
13
|
client '/missing', action: 'missing'
|
14
14
|
client '/require_test', action: 'require_test'
|
15
|
+
client '/images', action: 'images'
|
16
|
+
client '/login_from_task', action: 'login_from_task'
|
15
17
|
|
16
18
|
# Events
|
17
19
|
client '/events', component: 'main', controller: 'events', action: 'index'
|
@@ -5,7 +5,7 @@ module Main
|
|
5
5
|
class MainController < Volt::ModelController
|
6
6
|
model :page
|
7
7
|
|
8
|
-
reactive_accessor :blur_count, :focus_count
|
8
|
+
reactive_accessor :blur_count, :focus_count, :image_loaded
|
9
9
|
|
10
10
|
def index
|
11
11
|
a = {}
|
@@ -88,6 +88,10 @@ module Main
|
|
88
88
|
self.focus_count += 1
|
89
89
|
end
|
90
90
|
|
91
|
+
def do_login_from_task
|
92
|
+
LoginTasks.login_first_user
|
93
|
+
end
|
94
|
+
|
91
95
|
private
|
92
96
|
|
93
97
|
# the main template contains a #template binding that shows another
|
@@ -88,6 +88,14 @@ describe 'user accounts', type: :feature, sauce: true do
|
|
88
88
|
|
89
89
|
expect(page).to have_content('Password did not match')
|
90
90
|
end
|
91
|
+
|
92
|
+
it 'should let you login from a task' do
|
93
|
+
visit '/login_from_task'
|
94
|
+
|
95
|
+
click_button 'Login First User'
|
96
|
+
|
97
|
+
expect(page).to have_content('Test Account 9550')
|
98
|
+
end
|
91
99
|
end
|
92
100
|
|
93
101
|
end
|
@@ -5,6 +5,10 @@ if RUBY_PLATFORM != 'opal'
|
|
5
5
|
def allowed_method(arg1, arg2)
|
6
6
|
'yes' + arg1 + arg2
|
7
7
|
end
|
8
|
+
|
9
|
+
def set_cookie
|
10
|
+
cookies._something = 'awesome'
|
11
|
+
end
|
8
12
|
end
|
9
13
|
|
10
14
|
class WorkerPoolStub
|
@@ -29,7 +33,9 @@ if RUBY_PLATFORM != 'opal'
|
|
29
33
|
it 'should only allow method calls on Task or above in the inheritance chain' do
|
30
34
|
channel = double('channel')
|
31
35
|
|
32
|
-
|
36
|
+
# Tasks handle their own conversion to EJSON
|
37
|
+
msg = Volt::EJSON.stringify(['response', 0, 'yes it works', nil, nil])
|
38
|
+
expect(channel).to receive(:send_string_message).with(msg)
|
33
39
|
|
34
40
|
dispatcher.dispatch(channel, [0, 'TestTask', :allowed_method, {}, ' it', ' works'])
|
35
41
|
end
|
@@ -37,7 +43,7 @@ if RUBY_PLATFORM != 'opal'
|
|
37
43
|
it 'should not allow eval' do
|
38
44
|
channel = double('channel')
|
39
45
|
|
40
|
-
expect(channel).to receive(:send_message).with('response', 0, nil, RuntimeError
|
46
|
+
expect(channel).to receive(:send_message).with('response', 0, nil, "RuntimeError: unsafe method: eval", nil)
|
41
47
|
|
42
48
|
dispatcher.dispatch(channel, [0, 'TestTask', :eval, '5 + 10'])
|
43
49
|
end
|
@@ -45,7 +51,7 @@ if RUBY_PLATFORM != 'opal'
|
|
45
51
|
it 'should not allow instance_eval' do
|
46
52
|
channel = double('channel')
|
47
53
|
|
48
|
-
expect(channel).to receive(:send_message).with('response', 0, nil, RuntimeError
|
54
|
+
expect(channel).to receive(:send_message).with('response', 0, nil, 'RuntimeError: unsafe method: instance_eval', nil)
|
49
55
|
|
50
56
|
dispatcher.dispatch(channel, [0, 'TestTask', :instance_eval, '5 + 10'])
|
51
57
|
end
|
@@ -53,7 +59,7 @@ if RUBY_PLATFORM != 'opal'
|
|
53
59
|
it 'should not allow #methods' do
|
54
60
|
channel = double('channel')
|
55
61
|
|
56
|
-
expect(channel).to receive(:send_message).with('response', 0, nil, RuntimeError
|
62
|
+
expect(channel).to receive(:send_message).with('response', 0, nil, 'RuntimeError: unsafe method: methods', nil)
|
57
63
|
|
58
64
|
dispatcher.dispatch(channel, [0, 'TestTask', :methods])
|
59
65
|
end
|
@@ -67,6 +73,15 @@ if RUBY_PLATFORM != 'opal'
|
|
67
73
|
dispatcher.dispatch(channel, [0, 'TestTask', :allowed_method, {}, ' it', ' works'])
|
68
74
|
end
|
69
75
|
|
76
|
+
it 'should let you set a cookie' do
|
77
|
+
channel = double('channel')
|
78
|
+
|
79
|
+
allow(channel).to receive(:send_message).with('response', 0, 'yes it works', {:something=>"awesome"})
|
80
|
+
expect(Volt.logger).to receive(:log_dispatch)
|
81
|
+
|
82
|
+
dispatcher.dispatch(channel, [0, 'TestTask', :set_cookie, {}])
|
83
|
+
end
|
84
|
+
|
70
85
|
it 'closes the channel' do
|
71
86
|
disp = dispatcher
|
72
87
|
channel = Volt::ChannelStub.new
|
data/spec/utils/ejson_spec.rb
CHANGED
@@ -92,6 +92,12 @@ describe Volt::EJSON, '.stringify' do
|
|
92
92
|
)
|
93
93
|
end
|
94
94
|
|
95
|
+
it 'should convert symbols to strings' do
|
96
|
+
stringified = subject.stringify({something: :awesome})
|
97
|
+
|
98
|
+
expect(stringified).to eq('{"something":"awesome"}')
|
99
|
+
end
|
100
|
+
|
95
101
|
it 'escapes reserved key when type is incorrect' do
|
96
102
|
stringified = subject.stringify '$date' => 'something'
|
97
103
|
|
@@ -102,7 +102,13 @@ Volt.configure do |config|
|
|
102
102
|
#
|
103
103
|
# Encrypt message bus - messages on the message bus are encrypted by default
|
104
104
|
# using rbnacl.
|
105
|
+
<% if config[:disable_encryption] %>
|
106
|
+
#
|
107
|
+
# For dummy apps, we disable_encryption, to simplify the gem requirements.
|
108
|
+
config.message_bus.disable_encryption = true
|
109
|
+
<% else %>
|
105
110
|
# config.message_bus.disable_encryption = true
|
111
|
+
<% end %>
|
106
112
|
#
|
107
113
|
# ## MessageBus Server -- the message bus binds to a port and ip which the
|
108
114
|
# other volt instances need to be able to connect to. You can customize
|
@@ -0,0 +1,4 @@
|
|
1
|
+
# Any ./config/initializers/*.rb files will when the app starts up on the server.
|
2
|
+
# To load code on the client (or client and server), you can use the
|
3
|
+
# config/initializers folder in a component in the app directory. This folder
|
4
|
+
# is only for things that are server only. (Usually for things like config)
|
data/volt.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_dependency 'sass', '~> 3.4.15'
|
27
27
|
spec.add_dependency 'listen', '~> 3.0.1'
|
28
28
|
spec.add_dependency 'configurations', '~> 2.0.0.pre'
|
29
|
-
spec.add_dependency 'opal', '
|
29
|
+
spec.add_dependency 'opal', ['>= 0.8.0', '< 0.9']
|
30
30
|
spec.add_dependency 'bundler', '>= 1.5'
|
31
31
|
spec.add_dependency 'faye-websocket', '~> 0.10.0'
|
32
32
|
spec.add_dependency 'sprockets-helpers', '~> 1.2.1'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: volt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.5.
|
4
|
+
version: 0.9.5.pre11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Stout
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -98,16 +98,22 @@ dependencies:
|
|
98
98
|
name: opal
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: 0.8.0
|
104
|
+
- - "<"
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0.9'
|
104
107
|
type: :runtime
|
105
108
|
prerelease: false
|
106
109
|
version_requirements: !ruby/object:Gem::Requirement
|
107
110
|
requirements:
|
108
|
-
- - "
|
111
|
+
- - ">="
|
109
112
|
- !ruby/object:Gem::Version
|
110
113
|
version: 0.8.0
|
114
|
+
- - "<"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0.9'
|
111
117
|
- !ruby/object:Gem::Dependency
|
112
118
|
name: bundler
|
113
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -416,6 +422,7 @@ files:
|
|
416
422
|
- lib/volt/controllers/collection_helpers.rb
|
417
423
|
- lib/volt/controllers/http_controller.rb
|
418
424
|
- lib/volt/controllers/http_controller/http_cookie_persistor.rb
|
425
|
+
- lib/volt/controllers/login_as_helper.rb
|
419
426
|
- lib/volt/controllers/model_controller.rb
|
420
427
|
- lib/volt/controllers/template_helpers.rb
|
421
428
|
- lib/volt/data_stores/base_adaptor_client.rb
|
@@ -621,8 +628,11 @@ files:
|
|
621
628
|
- spec/apps/file_loading/app/slideshow/assets/js/test3.js
|
622
629
|
- spec/apps/kitchen_sink/.gitignore
|
623
630
|
- spec/apps/kitchen_sink/Gemfile
|
631
|
+
- spec/apps/kitchen_sink/app/main/assets/css/app.scss
|
624
632
|
- spec/apps/kitchen_sink/app/main/assets/css/todos.css
|
633
|
+
- spec/apps/kitchen_sink/app/main/assets/images/volt-logo.jpg
|
625
634
|
- spec/apps/kitchen_sink/app/main/config/dependencies.rb
|
635
|
+
- spec/apps/kitchen_sink/app/main/config/initializers/sample_model_extend.rb
|
626
636
|
- spec/apps/kitchen_sink/app/main/config/routes.rb
|
627
637
|
- spec/apps/kitchen_sink/app/main/controllers/events_controller.rb
|
628
638
|
- spec/apps/kitchen_sink/app/main/controllers/main_controller.rb
|
@@ -631,6 +641,7 @@ files:
|
|
631
641
|
- spec/apps/kitchen_sink/app/main/controllers/upload_controller.rb
|
632
642
|
- spec/apps/kitchen_sink/app/main/controllers/yield_component_controller.rb
|
633
643
|
- spec/apps/kitchen_sink/app/main/models/user.rb
|
644
|
+
- spec/apps/kitchen_sink/app/main/tasks/login_tasks.rb
|
634
645
|
- spec/apps/kitchen_sink/app/main/views/events/index.html
|
635
646
|
- spec/apps/kitchen_sink/app/main/views/mailers/welcome.email
|
636
647
|
- spec/apps/kitchen_sink/app/main/views/main/bindings.html
|
@@ -639,7 +650,9 @@ files:
|
|
639
650
|
- spec/apps/kitchen_sink/app/main/views/main/flash.html
|
640
651
|
- spec/apps/kitchen_sink/app/main/views/main/form.html
|
641
652
|
- spec/apps/kitchen_sink/app/main/views/main/html_safe.html
|
653
|
+
- spec/apps/kitchen_sink/app/main/views/main/images.html
|
642
654
|
- spec/apps/kitchen_sink/app/main/views/main/index.html
|
655
|
+
- spec/apps/kitchen_sink/app/main/views/main/login_from_task.html
|
643
656
|
- spec/apps/kitchen_sink/app/main/views/main/main.html
|
644
657
|
- spec/apps/kitchen_sink/app/main/views/main/missing.html
|
645
658
|
- spec/apps/kitchen_sink/app/main/views/main/require_test.html
|
@@ -671,6 +684,7 @@ files:
|
|
671
684
|
- spec/integration/first_last_spec.rb
|
672
685
|
- spec/integration/flash_spec.rb
|
673
686
|
- spec/integration/http_endpoints_spec.rb
|
687
|
+
- spec/integration/images_spec.rb
|
674
688
|
- spec/integration/list_spec.rb
|
675
689
|
- spec/integration/missing_spec.rb
|
676
690
|
- spec/integration/raw_html_binding.rb
|
@@ -834,9 +848,7 @@ files:
|
|
834
848
|
- templates/project/config.ru
|
835
849
|
- templates/project/config/app.rb.tt
|
836
850
|
- templates/project/config/base/index.html
|
837
|
-
- templates/project/config/initializers
|
838
|
-
- templates/project/config/initializers/client/.empty_directory
|
839
|
-
- templates/project/config/initializers/server/.empty_directory
|
851
|
+
- templates/project/config/initializers/boot.rb
|
840
852
|
- templates/project/spec/app/main/controllers/server/sample_http_controller_spec.rb
|
841
853
|
- templates/project/spec/app/main/integration/sample_integration_spec.rb
|
842
854
|
- templates/project/spec/app/main/models/sample_model_spec.rb
|
@@ -886,8 +898,11 @@ test_files:
|
|
886
898
|
- spec/apps/file_loading/app/slideshow/assets/js/test3.js
|
887
899
|
- spec/apps/kitchen_sink/.gitignore
|
888
900
|
- spec/apps/kitchen_sink/Gemfile
|
901
|
+
- spec/apps/kitchen_sink/app/main/assets/css/app.scss
|
889
902
|
- spec/apps/kitchen_sink/app/main/assets/css/todos.css
|
903
|
+
- spec/apps/kitchen_sink/app/main/assets/images/volt-logo.jpg
|
890
904
|
- spec/apps/kitchen_sink/app/main/config/dependencies.rb
|
905
|
+
- spec/apps/kitchen_sink/app/main/config/initializers/sample_model_extend.rb
|
891
906
|
- spec/apps/kitchen_sink/app/main/config/routes.rb
|
892
907
|
- spec/apps/kitchen_sink/app/main/controllers/events_controller.rb
|
893
908
|
- spec/apps/kitchen_sink/app/main/controllers/main_controller.rb
|
@@ -896,6 +911,7 @@ test_files:
|
|
896
911
|
- spec/apps/kitchen_sink/app/main/controllers/upload_controller.rb
|
897
912
|
- spec/apps/kitchen_sink/app/main/controllers/yield_component_controller.rb
|
898
913
|
- spec/apps/kitchen_sink/app/main/models/user.rb
|
914
|
+
- spec/apps/kitchen_sink/app/main/tasks/login_tasks.rb
|
899
915
|
- spec/apps/kitchen_sink/app/main/views/events/index.html
|
900
916
|
- spec/apps/kitchen_sink/app/main/views/mailers/welcome.email
|
901
917
|
- spec/apps/kitchen_sink/app/main/views/main/bindings.html
|
@@ -904,7 +920,9 @@ test_files:
|
|
904
920
|
- spec/apps/kitchen_sink/app/main/views/main/flash.html
|
905
921
|
- spec/apps/kitchen_sink/app/main/views/main/form.html
|
906
922
|
- spec/apps/kitchen_sink/app/main/views/main/html_safe.html
|
923
|
+
- spec/apps/kitchen_sink/app/main/views/main/images.html
|
907
924
|
- spec/apps/kitchen_sink/app/main/views/main/index.html
|
925
|
+
- spec/apps/kitchen_sink/app/main/views/main/login_from_task.html
|
908
926
|
- spec/apps/kitchen_sink/app/main/views/main/main.html
|
909
927
|
- spec/apps/kitchen_sink/app/main/views/main/missing.html
|
910
928
|
- spec/apps/kitchen_sink/app/main/views/main/require_test.html
|
@@ -936,6 +954,7 @@ test_files:
|
|
936
954
|
- spec/integration/first_last_spec.rb
|
937
955
|
- spec/integration/flash_spec.rb
|
938
956
|
- spec/integration/http_endpoints_spec.rb
|
957
|
+
- spec/integration/images_spec.rb
|
939
958
|
- spec/integration/list_spec.rb
|
940
959
|
- spec/integration/missing_spec.rb
|
941
960
|
- spec/integration/raw_html_binding.rb
|
File without changes
|
File without changes
|
File without changes
|