volt 0.8.18 → 0.8.19

Sign up to get free protection for your applications and to get access to all the features.
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