volt 0.8.18 → 0.8.19

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/Readme.md +2 -1
  4. data/VERSION +1 -1
  5. data/app/volt/controllers/notices_controller.rb +12 -2
  6. data/app/volt/models/user.rb +34 -1
  7. data/app/volt/tasks/store_tasks.rb +26 -23
  8. data/app/volt/tasks/user_tasks.rb +26 -2
  9. data/app/volt/views/notices/index.html +8 -6
  10. data/lib/volt.rb +47 -1
  11. data/lib/volt/cli/asset_compile.rb +24 -42
  12. data/lib/volt/config.rb +24 -14
  13. data/lib/volt/controllers/model_controller.rb +6 -1
  14. data/lib/volt/data_stores/mongo_driver.rb +7 -3
  15. data/lib/volt/models.rb +3 -0
  16. data/lib/volt/models/array_model.rb +21 -3
  17. data/lib/volt/models/model.rb +109 -48
  18. data/lib/volt/models/model_hash_behaviour.rb +25 -8
  19. data/lib/volt/models/persistors/array_store.rb +5 -2
  20. data/lib/volt/models/persistors/cookies.rb +91 -0
  21. data/lib/volt/models/persistors/model_store.rb +52 -14
  22. data/lib/volt/models/persistors/query/query_listener.rb +4 -1
  23. data/lib/volt/models/persistors/query/query_listener_pool.rb +1 -1
  24. data/lib/volt/models/persistors/store_state.rb +5 -5
  25. data/lib/volt/models/validations.rb +87 -18
  26. data/lib/volt/models/validators/length_validator.rb +1 -1
  27. data/lib/volt/models/validators/presence_validator.rb +1 -1
  28. data/lib/volt/models/validators/unique_validator.rb +28 -0
  29. data/lib/volt/page/bindings/template_binding.rb +2 -2
  30. data/lib/volt/page/page.rb +4 -0
  31. data/lib/volt/page/tasks.rb +2 -2
  32. data/lib/volt/reactive/computation.rb +2 -0
  33. data/lib/volt/reactive/hash_dependency.rb +1 -3
  34. data/lib/volt/reactive/reactive_array.rb +7 -0
  35. data/lib/volt/reactive/reactive_hash.rb +17 -0
  36. data/lib/volt/server.rb +1 -1
  37. data/lib/volt/server/component_templates.rb +3 -6
  38. data/lib/volt/server/html_parser/view_scope.rb +1 -1
  39. data/lib/volt/server/rack/component_code.rb +3 -2
  40. data/lib/volt/server/rack/component_paths.rb +15 -0
  41. data/lib/volt/server/rack/index_files.rb +1 -1
  42. data/lib/volt/tasks/dispatcher.rb +26 -12
  43. data/lib/volt/tasks/task_handler.rb +17 -15
  44. data/spec/apps/kitchen_sink/app/main/config/routes.rb +3 -0
  45. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +26 -0
  46. data/spec/apps/kitchen_sink/app/main/controllers/users_test_controller.rb +27 -0
  47. data/spec/apps/kitchen_sink/app/main/views/main/cookie_test.html +25 -0
  48. data/spec/apps/kitchen_sink/app/main/views/main/flash.html +13 -0
  49. data/spec/apps/kitchen_sink/app/main/views/main/main.html +3 -0
  50. data/spec/apps/kitchen_sink/app/main/views/users_test/index.html +22 -0
  51. data/spec/apps/kitchen_sink/config/app.rb +3 -0
  52. data/spec/integration/bindings_spec.rb +1 -1
  53. data/spec/integration/cookies_spec.rb +48 -0
  54. data/spec/integration/flash_spec.rb +32 -0
  55. data/spec/models/model_spec.rb +3 -4
  56. data/spec/models/validations_spec.rb +13 -13
  57. data/spec/tasks/dispatcher_spec.rb +1 -1
  58. data/templates/project/config/app.rb.tt +6 -1
  59. data/templates/project/{public → config/base_page}/index.html +0 -0
  60. data/volt.gemspec +4 -0
  61. metadata +47 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 643b4bf773281e2714b4002a68362b71e886b84b
4
- data.tar.gz: c229e47bfe005bbb3e4b5b0cfd54bef225b3eb87
3
+ metadata.gz: 7d48b14a1bdcbb5bd981ddba1c5eb3605a9ab64c
4
+ data.tar.gz: 7c74bc1525175653a03f9dc2053eab4cbecc5c29
5
5
  SHA512:
6
- metadata.gz: e794f7cce33a794913abb6c06d516e50d9fb4fe55c099d63c2a478cb2d1339b75c96d46943e0960f6a42c16be8efc9d3526e77613362cc927ac9941955d844e8
7
- data.tar.gz: 6e6b49d6d6504f25911bbf766d99453a25c34a9dee63f27b73f73dcfc5dd31f811ef29492a0189ff1ecbbdaa528d4ea5d87677cfef697f044cb3cae433512906
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.18
1
+ 0.8.19
@@ -6,8 +6,18 @@ module Volt
6
6
  'yep'
7
7
  end
8
8
 
9
- def page
10
- $page.page
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
@@ -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
- self._password = '--encoded: ' + val
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
- if model_class
26
- # Load the model, use the Store persistor and set the path
27
- model = model_class.new(data, persistor: Volt::Persistors::StoreFactory.new(nil), path: path)
28
- return model
29
- end
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
- return nil
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 = load_model(collection, path, data)
37
-
38
- errors = model.errors
41
+ model = nil
42
+ Volt::Model.nosave do
43
+ model = load_model(collection, path, data)
44
+ end
39
45
 
40
- if model.errors.size == 0
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
- # On the backend, the promise is resolved before its returned, so we can
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
- return errors
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
- def create_user(username, password)
4
- $page.store._users << { email: username, password: password }
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
- {{ if !flash.empty? }}
15
- <div class="notices alert alert-info" e-click="flash.clear">
16
- {{ flash._notices.each do |notice| }}
17
- <p>{{ notice }}</p>
18
- {{ end }}
19
- </div>
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
- write_file(logical_path)
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 = logical_path.gsub(/^\/assets\//, '')
79
- write_file(logical_path)
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 write_file(logical_path)
84
- path = "#{@root_path}/compiled/assets/#{logical_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
- File.open(path, 'wb') do |file|
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
- components_folder = File.join(Volt.root, '/compiled/components')
102
- FileUtils.mkdir_p(components_folder)
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}/compiled/index.html"
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, 'w') do |file|
113
- file.write(@index_files.html)
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
- module Config
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
- # Resets the configuration to the default (empty hash)
13
- def reset_config!
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