volt 0.9.5.pre9 → 0.9.5.pre11
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 +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
|