volt 0.8.18 → 0.8.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/Readme.md +2 -1
- data/VERSION +1 -1
- data/app/volt/controllers/notices_controller.rb +12 -2
- data/app/volt/models/user.rb +34 -1
- data/app/volt/tasks/store_tasks.rb +26 -23
- data/app/volt/tasks/user_tasks.rb +26 -2
- data/app/volt/views/notices/index.html +8 -6
- data/lib/volt.rb +47 -1
- data/lib/volt/cli/asset_compile.rb +24 -42
- data/lib/volt/config.rb +24 -14
- data/lib/volt/controllers/model_controller.rb +6 -1
- data/lib/volt/data_stores/mongo_driver.rb +7 -3
- data/lib/volt/models.rb +3 -0
- data/lib/volt/models/array_model.rb +21 -3
- data/lib/volt/models/model.rb +109 -48
- data/lib/volt/models/model_hash_behaviour.rb +25 -8
- data/lib/volt/models/persistors/array_store.rb +5 -2
- data/lib/volt/models/persistors/cookies.rb +91 -0
- data/lib/volt/models/persistors/model_store.rb +52 -14
- data/lib/volt/models/persistors/query/query_listener.rb +4 -1
- data/lib/volt/models/persistors/query/query_listener_pool.rb +1 -1
- data/lib/volt/models/persistors/store_state.rb +5 -5
- data/lib/volt/models/validations.rb +87 -18
- data/lib/volt/models/validators/length_validator.rb +1 -1
- data/lib/volt/models/validators/presence_validator.rb +1 -1
- data/lib/volt/models/validators/unique_validator.rb +28 -0
- data/lib/volt/page/bindings/template_binding.rb +2 -2
- data/lib/volt/page/page.rb +4 -0
- data/lib/volt/page/tasks.rb +2 -2
- data/lib/volt/reactive/computation.rb +2 -0
- data/lib/volt/reactive/hash_dependency.rb +1 -3
- data/lib/volt/reactive/reactive_array.rb +7 -0
- data/lib/volt/reactive/reactive_hash.rb +17 -0
- data/lib/volt/server.rb +1 -1
- data/lib/volt/server/component_templates.rb +3 -6
- data/lib/volt/server/html_parser/view_scope.rb +1 -1
- data/lib/volt/server/rack/component_code.rb +3 -2
- data/lib/volt/server/rack/component_paths.rb +15 -0
- data/lib/volt/server/rack/index_files.rb +1 -1
- data/lib/volt/tasks/dispatcher.rb +26 -12
- data/lib/volt/tasks/task_handler.rb +17 -15
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +3 -0
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +26 -0
- data/spec/apps/kitchen_sink/app/main/controllers/users_test_controller.rb +27 -0
- data/spec/apps/kitchen_sink/app/main/views/main/cookie_test.html +25 -0
- data/spec/apps/kitchen_sink/app/main/views/main/flash.html +13 -0
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +3 -0
- data/spec/apps/kitchen_sink/app/main/views/users_test/index.html +22 -0
- data/spec/apps/kitchen_sink/config/app.rb +3 -0
- data/spec/integration/bindings_spec.rb +1 -1
- data/spec/integration/cookies_spec.rb +48 -0
- data/spec/integration/flash_spec.rb +32 -0
- data/spec/models/model_spec.rb +3 -4
- data/spec/models/validations_spec.rb +13 -13
- data/spec/tasks/dispatcher_spec.rb +1 -1
- data/templates/project/config/app.rb.tt +6 -1
- data/templates/project/{public → config/base_page}/index.html +0 -0
- data/volt.gemspec +4 -0
- metadata +47 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d48b14a1bdcbb5bd981ddba1c5eb3605a9ab64c
|
4
|
+
data.tar.gz: 7c74bc1525175653a03f9dc2053eab4cbecc5c29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a234d3f5b18ad192ffb575e7cbfbbf39586048a6e6b6ac835ccc06c2c13b1aaab720a0e5fbd3b8e84326b2553e153ba9fa9df42b7098ef210bd775f8b41198c
|
7
|
+
data.tar.gz: 6f70ce074f81d8c2d3da698090c9cfdd2b871b43ee9483198aaf2e61df477589002f82a8d469f1b8380803ccd7c5d8833f7286a3f375fda537d09cca7fba60d9
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.8.19 - 2014-11-05
|
4
|
+
### Breaking Changes
|
5
|
+
- the default index page is now moved from ```public/index.html``` to ```config/base/index.html``` Please update your app's accordingly. Since the public page is essentially static at the moment, public will only be used for asset pre-compilation (and index.html will be rendered in place)
|
6
|
+
- validations do not use underscore for the field name
|
7
|
+
|
8
|
+
### Added
|
9
|
+
- you can precompile an app with ```bundle exec volt precompile``` - still a work in process
|
10
|
+
- update flash to handle successes, notices, warnings, errors.
|
11
|
+
- Add .keys to models (you can use .keys.each do |key| until we get .each_pair binding support)
|
12
|
+
- added ```cookies``` collection. See docs for more info
|
13
|
+
- ```validate :field_name, unique: true``` now supported (scope coming soon)
|
14
|
+
- added custom validations by passing a block to ```validate``` and returning a hash of errors like ```{field_name => ['...', '...']}```
|
15
|
+
|
3
16
|
## 0.8.18 - 2014-10-26
|
4
17
|
### Added
|
5
18
|
- Added a default app.css.scss file
|
@@ -7,6 +20,7 @@
|
|
7
20
|
### Changed
|
8
21
|
- back button fixed
|
9
22
|
- improve security on task dispatcher
|
23
|
+
- lots of minor bug fixes
|
10
24
|
|
11
25
|
## 0.8.17 - 2014-10-20
|
12
26
|
### Added
|
data/Readme.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[![Build Status](http://img.shields.io/travis/voltrb/volt/master.svg)](https://travis-ci.org/voltrb/volt)
|
4
4
|
[![Inline docs](http://inch-ci.org/github/voltrb/volt.svg?branch=master)](http://inch-ci.org/github/voltrb/volt)
|
5
5
|
[![Volt Chat](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/voltrb/volt)
|
6
|
+
[![Stories in Ready](https://badge.waffle.io/voltrb/volt.png?label=ready&title=Ready)](https://waffle.io/voltrb/volt)
|
6
7
|
|
7
8
|
** For the current status of volt, read: http://voltframework.com/blog
|
8
9
|
|
@@ -33,4 +34,4 @@ Read the [full docs on Volt here](http://voltframework.com/docs)
|
|
33
34
|
|
34
35
|
You want to contribute? Great! Thanks for being awesome! At the moment, we have a big internal todo list, hop on https://gitter.im/voltrb/volt so we don't duplicate work. Pull requests are always welcome, but asking about helping on gitter should save some duplication.
|
35
36
|
|
36
|
-
[![Pledgie](https://pledgie.com/campaigns/26731.png?skin_name=chrome)](https://pledgie.com/campaigns/26731)
|
37
|
+
[![Pledgie](https://pledgie.com/campaigns/26731.png?skin_name=chrome)](https://pledgie.com/campaigns/26731)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.8.
|
1
|
+
0.8.19
|
@@ -6,8 +6,18 @@ module Volt
|
|
6
6
|
'yep'
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
|
9
|
+
def map_key_class(key)
|
10
|
+
case key
|
11
|
+
when 'errors'
|
12
|
+
'danger'
|
13
|
+
when 'warnings'
|
14
|
+
'warning'
|
15
|
+
when 'successes'
|
16
|
+
'success'
|
17
|
+
else
|
18
|
+
# notices
|
19
|
+
'info'
|
20
|
+
end
|
11
21
|
end
|
12
22
|
end
|
13
23
|
end
|
data/app/volt/models/user.rb
CHANGED
@@ -1,5 +1,38 @@
|
|
1
|
+
if RUBY_PLATFORM != 'opal'
|
2
|
+
require 'bcrypt'
|
3
|
+
end
|
4
|
+
|
1
5
|
class User < Volt::Model
|
6
|
+
validate :username, unique: true, length: 8
|
7
|
+
if RUBY_PLATFORM == 'opal'
|
8
|
+
# Don't validate on the server
|
9
|
+
validate :password, length: 8
|
10
|
+
end
|
11
|
+
|
2
12
|
def password=(val)
|
3
|
-
|
13
|
+
if Volt.server?
|
14
|
+
# on the server, we bcrypt the password and store the result
|
15
|
+
self._hashed_password = BCrypt::Password.create(val)
|
16
|
+
else
|
17
|
+
self._password = val
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Login the user, return a promise for success
|
22
|
+
def self.login(username, password)
|
23
|
+
puts "Login now"
|
24
|
+
UserTasks.login(username, password).then do |result|
|
25
|
+
puts "Got: #{result.inspect}"
|
26
|
+
|
27
|
+
# Assign the user_id cookie for the user
|
28
|
+
$page.cookies._user_id = result
|
29
|
+
|
30
|
+
# Pass nil back
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.logout
|
36
|
+
$page.cookies.delete(:user_id)
|
4
37
|
end
|
5
38
|
end
|
@@ -12,6 +12,7 @@ class StoreTasks < Volt::TaskHandler
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def load_model(collection, path, data)
|
15
|
+
puts "Load Model: #{path.inspect}"
|
15
16
|
model_name = collection.singularize.camelize
|
16
17
|
|
17
18
|
# TODO: Security check to make sure we have a valid model
|
@@ -22,37 +23,39 @@ class StoreTasks < Volt::TaskHandler
|
|
22
23
|
model_class = Volt::Model
|
23
24
|
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
# Load the model, use the Store persistor and set the path
|
27
|
+
model = model_class.new({}, persistor: Volt::Persistors::StoreFactory.new(nil), path: path)
|
28
|
+
model.persistor.change_state_to(:loaded)
|
29
|
+
|
30
|
+
# Create a buffer
|
31
|
+
buffer = model.buffer
|
30
32
|
|
31
|
-
|
33
|
+
# Assign the data
|
34
|
+
buffer.attributes = data
|
35
|
+
|
36
|
+
return buffer
|
32
37
|
end
|
33
38
|
|
34
39
|
def save(collection, path, data)
|
35
40
|
data = data.symbolize_keys
|
36
|
-
model =
|
37
|
-
|
38
|
-
|
41
|
+
model = nil
|
42
|
+
Volt::Model.nosave do
|
43
|
+
model = load_model(collection, path, data)
|
44
|
+
end
|
39
45
|
|
40
|
-
|
46
|
+
# On the backend, the promise is resolved before its returned, so we can
|
47
|
+
# return from within it.
|
48
|
+
#
|
49
|
+
# Pass the channel as a thread-local so that we don't update the client
|
50
|
+
# who sent the update.
|
51
|
+
Thread.current['in_channel'] = @channel
|
52
|
+
promise = model.save!.then do |result|
|
53
|
+
return nil
|
54
|
+
end
|
41
55
|
|
42
|
-
|
43
|
-
# return from within it.
|
44
|
-
#
|
45
|
-
# Pass the channel as a thread-local so that we don't update the client
|
46
|
-
# who sent the update.
|
47
|
-
Thread.current['in_channel'] = @channel
|
48
|
-
model.persistor.changed do |errors|
|
49
|
-
Thread.current['in_channel'] = nil
|
56
|
+
Thread.current['in_channel'] = nil
|
50
57
|
|
51
|
-
|
52
|
-
end
|
53
|
-
else
|
54
|
-
return errors
|
55
|
-
end
|
58
|
+
return promise
|
56
59
|
end
|
57
60
|
|
58
61
|
def delete(collection, id)
|
@@ -1,6 +1,30 @@
|
|
1
1
|
class UserTasks < Volt::TaskHandler
|
2
2
|
# Login a user, takes a username and password
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
def login(username, password)
|
5
|
+
puts "META: " + Thread.current['meta'].inspect
|
6
|
+
|
7
|
+
if Volt.user
|
8
|
+
puts "USER: " + Volt.user._name
|
9
|
+
end
|
10
|
+
|
11
|
+
return store._users.find(username: username).then do |users|
|
12
|
+
user = users.first
|
13
|
+
|
14
|
+
match_pass = BCrypt::Password.new(user._hashed_password)
|
15
|
+
if match_pass == password
|
16
|
+
raise "app_secret is not configured" unless Volt.config.app_secret
|
17
|
+
|
18
|
+
# TODO: returning here should be possible, but causes some issues
|
19
|
+
|
20
|
+
# Salt the user id with the app_secret so the end user can't tamper with the cookie
|
21
|
+
signature = BCrypt::Password.create("#{Volt.config.app_secret}::#{user._id}")
|
22
|
+
|
23
|
+
# Return user_id:hash on user id
|
24
|
+
next "#{user._id}:#{signature}"
|
25
|
+
else
|
26
|
+
raise "Password did not match"
|
27
|
+
end
|
28
|
+
end
|
5
29
|
end
|
6
30
|
end
|
@@ -11,10 +11,12 @@
|
|
11
11
|
{{ if page._reconnected }}
|
12
12
|
<div class="notices alert alert-success">Reconnected!</div>
|
13
13
|
{{ end }}
|
14
|
-
{{
|
15
|
-
|
16
|
-
{{
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
{{ flash.keys.each do |key| }}
|
15
|
+
{{ if flash.send(:"_#{key}").present? }}
|
16
|
+
<div class="notices alert alert-{{ map_key_class(key) }}" e-click="flash.clear">
|
17
|
+
{{ flash.send(:"_#{key}").each do |notice| }}
|
18
|
+
<p>{{ notice }}</p>
|
19
|
+
{{ end }}
|
20
|
+
</div>
|
21
|
+
{{ end }}
|
20
22
|
{{ end }}
|
data/lib/volt.rb
CHANGED
@@ -16,7 +16,6 @@ module Volt
|
|
16
16
|
end
|
17
17
|
|
18
18
|
class << self
|
19
|
-
include Volt::Config unless RUBY_PLATFORM == 'opal'
|
20
19
|
def root
|
21
20
|
@root ||= File.expand_path(Dir.pwd)
|
22
21
|
end
|
@@ -48,5 +47,52 @@ module Volt
|
|
48
47
|
def in_browser?
|
49
48
|
@in_browser
|
50
49
|
end
|
50
|
+
|
51
|
+
# Get the user_id from the cookie
|
52
|
+
def user_id
|
53
|
+
if Volt.client?
|
54
|
+
user_id_signature = $page.cookies._user_id
|
55
|
+
else
|
56
|
+
# Check meta for the user id and validate it
|
57
|
+
meta_data = Thread.current['meta']
|
58
|
+
if meta_data
|
59
|
+
user_id_signature = meta_data['user_id']
|
60
|
+
else
|
61
|
+
user_id_signature = nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
if user_id_signature.nil?
|
66
|
+
return nil
|
67
|
+
else
|
68
|
+
index = user_id_signature.index(':')
|
69
|
+
user_id = user_id_signature[0...index]
|
70
|
+
|
71
|
+
if RUBY_PLATFORM != 'opal'
|
72
|
+
hash = user_id_signature[(index+1)..-1]
|
73
|
+
|
74
|
+
# Make sure the user hash matches
|
75
|
+
if BCrypt::Password.new(hash) != "#{Volt.config.app_secret}::#{user._id}"
|
76
|
+
# user id has been tampered with, reject
|
77
|
+
raise "user id or hash has been tampered with"
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
return user_id
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return the current user.
|
87
|
+
def user
|
88
|
+
user_id = self.user_id
|
89
|
+
if user_id
|
90
|
+
return $page.store._users.find_one(_id: user_id)
|
91
|
+
else
|
92
|
+
return nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
51
96
|
end
|
52
97
|
end
|
98
|
+
|
@@ -6,37 +6,19 @@ module Volt
|
|
6
6
|
compile
|
7
7
|
end
|
8
8
|
|
9
|
-
desc 'watch', 'compiles the project to /compiled when a file changes'
|
10
|
-
|
11
|
-
def watch
|
12
|
-
require 'listen'
|
13
|
-
|
14
|
-
listener = Listen.to('app') do |modified, added, removed|
|
15
|
-
compile
|
16
|
-
end
|
17
|
-
|
18
|
-
listener.start # non-blocking
|
19
|
-
|
20
|
-
Signal.trap('SIGINT') do
|
21
|
-
listener.stop
|
22
|
-
end
|
23
|
-
|
24
|
-
compile
|
25
|
-
|
26
|
-
begin
|
27
|
-
sleep
|
28
|
-
rescue ThreadError => e
|
29
|
-
# ignore, breaks out on sigint
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
9
|
private
|
34
10
|
|
35
11
|
def compile
|
36
12
|
print 'compiling project...'
|
37
13
|
require 'fileutils'
|
14
|
+
ENV['SERVER'] = 'true'
|
15
|
+
|
38
16
|
require 'opal'
|
39
17
|
require 'volt'
|
18
|
+
require 'volt/boot'
|
19
|
+
|
20
|
+
Volt.boot(Dir.pwd)
|
21
|
+
|
40
22
|
require 'volt/server/rack/component_paths'
|
41
23
|
require 'volt/server/rack/component_code'
|
42
24
|
require 'volt/server/rack/opal_files'
|
@@ -68,28 +50,26 @@ module Volt
|
|
68
50
|
logical_path = logical_path.to_s
|
69
51
|
# Only include files that aren't compiled elsewhere, like fonts
|
70
52
|
unless logical_path[/[.](y|css|js|html|erb)$/]
|
71
|
-
|
53
|
+
write_sprocket_file(logical_path)
|
72
54
|
end
|
73
55
|
end
|
74
56
|
end
|
75
57
|
|
76
58
|
def write_js_and_css
|
77
59
|
(@index_files.javascript_files + @index_files.css_files).each do |logical_path|
|
78
|
-
logical_path
|
79
|
-
|
60
|
+
if logical_path =~ /^\/assets\//
|
61
|
+
logical_path = logical_path.gsub(/^\/assets\//, '')
|
62
|
+
write_sprocket_file(logical_path)
|
63
|
+
end
|
80
64
|
end
|
81
65
|
end
|
82
66
|
|
83
|
-
def
|
84
|
-
path = "#{@root_path}/
|
85
|
-
|
86
|
-
FileUtils.mkdir_p(File.dirname(path))
|
67
|
+
def write_sprocket_file(logical_path)
|
68
|
+
path = "#{@root_path}/public/assets/#{logical_path}"
|
87
69
|
|
88
70
|
begin
|
89
71
|
content = @opal_files.environment[logical_path].to_s
|
90
|
-
|
91
|
-
file.write(content)
|
92
|
-
end
|
72
|
+
write_file(path, content)
|
93
73
|
rescue Sprockets::FileNotFound, SyntaxError => e
|
94
74
|
# ignore
|
95
75
|
end
|
@@ -98,19 +78,21 @@ module Volt
|
|
98
78
|
def write_component_js
|
99
79
|
javascript_code = @component_handler.compile_for_component('main')
|
100
80
|
|
101
|
-
|
102
|
-
|
103
|
-
File.open(File.join(components_folder, '/main.js'), 'w') do |file|
|
104
|
-
file.write(javascript_code)
|
105
|
-
end
|
81
|
+
path = File.join(Volt.root, '/public/components/main.js')
|
82
|
+
write_file(path, javascript_code)
|
106
83
|
end
|
107
84
|
|
108
85
|
def write_index
|
109
|
-
path = "#{@root_path}/
|
86
|
+
path = "#{@root_path}/public/index.html"
|
87
|
+
|
88
|
+
write_file(path, @index_files.html)
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_file(path, data)
|
110
92
|
FileUtils.mkdir_p(File.dirname(path))
|
111
93
|
|
112
|
-
File.open(path, '
|
113
|
-
file.write(
|
94
|
+
File.open(path, 'wb') do |file|
|
95
|
+
file.write(data)
|
114
96
|
end
|
115
97
|
end
|
116
98
|
end
|
data/lib/volt/config.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
1
|
# Config lets a user set global config options for Volt.
|
2
|
+
require 'configurations'
|
2
3
|
module Volt
|
3
|
-
|
4
|
-
def setup
|
5
|
-
yield config
|
6
|
-
end
|
7
|
-
|
8
|
-
def config
|
9
|
-
@config || self.reset_config!
|
10
|
-
end
|
4
|
+
include Configurations
|
11
5
|
|
12
|
-
|
13
|
-
def
|
6
|
+
class << self
|
7
|
+
def defaults
|
14
8
|
app_name = File.basename(Dir.pwd)
|
15
|
-
|
16
|
-
@config = OpenStruct.new(
|
9
|
+
{
|
17
10
|
app_name: app_name,
|
18
11
|
db_name: ENV['DB_NAME'] || (app_name + '_' + Volt.env.to_s),
|
19
12
|
db_host: ENV['DB_HOST'] || 'localhost',
|
20
13
|
db_port: (ENV['DB_PORT'] || 27_017).to_i,
|
21
|
-
db_driver: ENV['DB_DRIVER'] || 'mongo'
|
22
|
-
|
14
|
+
db_driver: ENV['DB_DRIVER'] || 'mongo',
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Resets the configuration to the default (empty hash)
|
19
|
+
def reset_config!
|
20
|
+
self.configure do |c|
|
21
|
+
c.from_h(defaults)
|
22
|
+
end
|
23
23
|
end
|
24
24
|
|
25
25
|
# Load in all .rb files in the config folder
|
@@ -28,5 +28,15 @@ module Volt
|
|
28
28
|
require(config_file)
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
alias_method :setup, :configure
|
33
|
+
alias_method :config, :configuration
|
34
|
+
end
|
35
|
+
|
36
|
+
configuration_defaults do |c|
|
37
|
+
c.from_h(Volt.defaults)
|
31
38
|
end
|
39
|
+
|
40
|
+
|
41
|
+
|
32
42
|
end
|