volt 0.8.27.beta3 → 0.8.27.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/CHANGELOG.md +11 -0
  4. data/CONTRIBUTING.md +3 -2
  5. data/{Readme.md → README.md} +9 -12
  6. data/Rakefile +2 -9
  7. data/VERSION +1 -1
  8. data/app/volt/models/user.rb +8 -0
  9. data/app/volt/tasks/live_query/data_store.rb +13 -5
  10. data/app/volt/tasks/live_query/live_query.rb +45 -3
  11. data/app/volt/tasks/live_query/live_query_pool.rb +9 -1
  12. data/app/volt/tasks/query_tasks.rb +20 -2
  13. data/app/volt/tasks/store_tasks.rb +37 -20
  14. data/app/volt/tasks/user_tasks.rb +15 -13
  15. data/lib/volt/boot.rb +5 -3
  16. data/lib/volt/cli/console.rb +1 -0
  17. data/lib/volt/cli/generate.rb +15 -0
  18. data/lib/volt/cli.rb +19 -12
  19. data/lib/volt/config.rb +1 -1
  20. data/lib/volt/controllers/model_controller.rb +13 -3
  21. data/lib/volt/extra_core/extra_core.rb +1 -0
  22. data/lib/volt/extra_core/hash.rb +26 -0
  23. data/lib/volt/extra_core/object.rb +5 -1
  24. data/lib/volt/models/array_model.rb +86 -35
  25. data/lib/volt/models/associations.rb +53 -0
  26. data/lib/volt/models/buffer.rb +22 -10
  27. data/lib/volt/models/dirty.rb +88 -0
  28. data/lib/volt/models/errors.rb +21 -0
  29. data/lib/volt/models/field_helpers.rb +2 -2
  30. data/lib/volt/models/listener_tracker.rb +17 -0
  31. data/lib/volt/models/model.rb +213 -69
  32. data/lib/volt/models/model_helpers.rb +27 -17
  33. data/lib/volt/models/permissions.rb +246 -0
  34. data/lib/volt/models/persistors/array_store.rb +149 -81
  35. data/lib/volt/models/persistors/base.rb +16 -0
  36. data/lib/volt/models/persistors/cookies.rb +14 -9
  37. data/lib/volt/models/persistors/flash.rb +3 -0
  38. data/lib/volt/models/persistors/local_store.rb +0 -16
  39. data/lib/volt/models/persistors/model_store.rb +1 -2
  40. data/lib/volt/models/persistors/query/normalizer.rb +51 -0
  41. data/lib/volt/models/persistors/query/query_listener.rb +21 -5
  42. data/lib/volt/models/persistors/query/query_listener_pool.rb +0 -9
  43. data/lib/volt/models/persistors/store.rb +8 -0
  44. data/lib/volt/models/persistors/store_state.rb +4 -27
  45. data/lib/volt/models/state_helpers.rb +11 -0
  46. data/lib/volt/models/state_manager.rb +43 -0
  47. data/lib/volt/models/url.rb +5 -5
  48. data/lib/volt/models/validations.rb +38 -41
  49. data/lib/volt/models/validators/email_validator.rb +4 -9
  50. data/lib/volt/models/validators/format_validator.rb +23 -8
  51. data/lib/volt/models/validators/length_validator.rb +2 -2
  52. data/lib/volt/models/validators/numericality_validator.rb +7 -3
  53. data/lib/volt/models/validators/phone_number_validator.rb +4 -9
  54. data/lib/volt/models/validators/presence_validator.rb +2 -2
  55. data/lib/volt/models/validators/unique_validator.rb +2 -2
  56. data/lib/volt/models/validators/user_validation.rb +6 -0
  57. data/lib/volt/models.rb +8 -3
  58. data/lib/volt/page/bindings/attribute_binding.rb +10 -4
  59. data/lib/volt/page/bindings/content_binding.rb +9 -5
  60. data/lib/volt/page/bindings/if_binding.rb +25 -2
  61. data/lib/volt/page/bindings/template_binding.rb +19 -1
  62. data/lib/volt/page/bindings/yield_binding.rb +31 -0
  63. data/lib/volt/page/page.rb +11 -16
  64. data/lib/volt/reactive/class_eventable.rb +71 -0
  65. data/lib/volt/reactive/computation.rb +79 -10
  66. data/lib/volt/reactive/dependency.rb +27 -8
  67. data/lib/volt/reactive/eventable.rb +36 -22
  68. data/lib/volt/reactive/reactive_array.rb +2 -3
  69. data/lib/volt/reactive/reactive_hash.rb +8 -3
  70. data/lib/volt/router/routes.rb +2 -1
  71. data/lib/volt/server/component_templates.rb +0 -2
  72. data/lib/volt/server/html_parser/component_view_scope.rb +59 -0
  73. data/lib/volt/server/html_parser/view_handler.rb +3 -0
  74. data/lib/volt/server/html_parser/view_parser.rb +1 -0
  75. data/lib/volt/server/html_parser/view_scope.rb +17 -41
  76. data/lib/volt/server/rack/component_paths.rb +1 -10
  77. data/lib/volt/server/rack/index_files.rb +9 -4
  78. data/lib/volt/server/rack/opal_files.rb +22 -14
  79. data/lib/volt/server/rack/quiet_common_logger.rb +1 -1
  80. data/lib/volt/server/socket_connection_handler.rb +4 -0
  81. data/lib/volt/spec/setup.rb +26 -0
  82. data/lib/volt/tasks/dispatcher.rb +11 -0
  83. data/lib/volt/utils/event_counter.rb +29 -0
  84. data/lib/volt/utils/generic_pool.rb +12 -0
  85. data/lib/volt/utils/modes.rb +40 -0
  86. data/lib/volt/utils/promise_patch.rb +66 -0
  87. data/lib/volt/utils/timers.rb +33 -0
  88. data/lib/volt/volt/users.rb +48 -5
  89. data/lib/volt.rb +4 -0
  90. data/spec/apps/kitchen_sink/Gemfile +3 -1
  91. data/spec/apps/kitchen_sink/app/main/config/routes.rb +9 -8
  92. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +9 -0
  93. data/spec/apps/kitchen_sink/app/main/controllers/yield_component_controller.rb +5 -0
  94. data/spec/apps/kitchen_sink/app/main/views/main/cookie_test.html +1 -1
  95. data/spec/apps/kitchen_sink/app/main/views/main/index.html +1 -1
  96. data/spec/apps/kitchen_sink/app/main/views/main/main.html +2 -1
  97. data/spec/apps/kitchen_sink/app/main/views/main/yield.html +18 -0
  98. data/spec/apps/kitchen_sink/app/main/views/yield-component/index.html +4 -0
  99. data/spec/extra_core/logger_spec.rb +4 -2
  100. data/spec/integration/user_spec.rb +42 -42
  101. data/spec/integration/yield_spec.rb +18 -0
  102. data/spec/models/associations_spec.rb +37 -0
  103. data/spec/models/dirty_spec.rb +102 -0
  104. data/spec/models/model_spec.rb +64 -8
  105. data/spec/models/model_state_spec.rb +24 -0
  106. data/spec/models/permissions_spec.rb +96 -0
  107. data/spec/models/user_spec.rb +8 -5
  108. data/spec/models/user_validation_spec.rb +24 -0
  109. data/spec/models/validations_spec.rb +44 -5
  110. data/spec/models/validators/email_validator_spec.rb +109 -82
  111. data/spec/models/validators/format_validator_spec.rb +4 -107
  112. data/spec/models/validators/length_validator_spec.rb +9 -9
  113. data/spec/models/validators/phone_number_validator_spec.rb +60 -103
  114. data/spec/models/validators/shared_examples_for_validators.rb +123 -0
  115. data/spec/reactive/class_eventable_spec.rb +37 -0
  116. data/spec/reactive/computation_spec.rb +68 -3
  117. data/spec/reactive/dependency_spec.rb +71 -0
  118. data/spec/reactive/eventable_spec.rb +21 -0
  119. data/spec/reactive/reactive_hash_spec.rb +12 -0
  120. data/spec/router/routes_spec.rb +50 -50
  121. data/spec/server/html_parser/view_parser_spec.rb +0 -3
  122. data/spec/server/rack/component_paths_spec.rb +11 -0
  123. data/spec/server/rack/quite_common_logger_spec.rb +3 -4
  124. data/spec/spec_helper.rb +7 -3
  125. data/templates/component/config/dependencies.rb +1 -7
  126. data/templates/component/config/routes.rb +1 -1
  127. data/templates/component/controllers/main_controller.rb.tt +20 -0
  128. data/templates/component/views/{index → main}/index.html.tt +0 -0
  129. data/templates/newgem/lib/newgem.rb.tt +1 -3
  130. data/templates/project/app/main/config/routes.rb +3 -3
  131. data/templates/project/app/main/views/main/main.html.tt +4 -4
  132. data/templates/project/config/app.rb.tt +6 -0
  133. data/volt.gemspec +11 -7
  134. metadata +96 -42
  135. data/lib/volt/models/model_state.rb +0 -21
  136. data/templates/component/controllers/main_controller.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e86338c95256cca4f64bc6889a2e6f86626c3e23
4
- data.tar.gz: 71e9eef1f83c4a1fc2213b8ca9610da56518a401
3
+ metadata.gz: 385a2c074963c20e45ace62abbc680bfd52b54c0
4
+ data.tar.gz: 490a62ce8f645fa02310e1af96ef45026849c64e
5
5
  SHA512:
6
- metadata.gz: e689c874e137c1e7aa59303238aa6d287f89e407b8c003bbcd43242201ac7fb2ab3254fafa5ccaa4f105fdfcf4c720d76a631684e8224511c9b8f62d92227f54
7
- data.tar.gz: ed08a559fb6f89a85bf41f19822316195653acac6a0dda68c6f261e4e3e53c6677fe84b5525207c5a5fea3c01f2db64ef5faf547fd607db338d6adba21d9b4e0
6
+ metadata.gz: ee8dd6c7cadff2bfa91694caadfa0996147b3827f2cc820122efa80caeba216ff2bff74607a94c168e90a4b1adbdd545f37a5bd2f8bad2bc774d4485e51ce328
7
+ data.tar.gz: 3fcfb4f21a033882f4c508f94cc149c28126060b53eb60f119d62f102a7f18721f02005c67ec92f1fea0c94456549559d30cc3dd98881697e289ec52ef67211a
data/.travis.yml CHANGED
@@ -1,6 +1,8 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  cache: bundler
4
+ services:
5
+ - mongodb
4
6
  rvm:
5
7
  - 2.1.0
6
8
  - 2.1.1
data/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## 0.8.27 - WIP
4
4
  ### Added
5
+ - _'s are no longer required for route constraints (so just use ```controller: 'main', action: 'index'``` now)
6
+ - fixed generated component code
7
+ - added .order for sorting on the data store (since .sort is a ruby Enum method)
8
+ - changed .find to .where to not conflict with ruby Enum's .find
9
+ - added .fetch and .fetch_first for waiting on store model loads
10
+ - added .sync for synchronusly waiting on promises on the server only
11
+ - added the ability to pass content into tags: (https://github.com/voltrb/docs/blob/master/en/docs/yield_binding.md)
5
12
  - the {action}_remove method had been changed to before_{action}_remove and after_{action}_remove to provide more hooks and a clearer understanding of when it is happening.
6
13
  - Changed it so content bindings escape all html (for CSRF - thanks @ChaosData)
7
14
  - Added formats, email, phone validators (thanks @lexun and @kxcrl)
@@ -10,6 +17,10 @@
10
17
  - fixed bug appending existing models to a collection
11
18
  - refactored TemplateBinding, moved code into ViewLookupForPath (SRP)
12
19
  - reserved fields now get a warning in models
20
+ - bindings will now resolve any values that are promises. (currently only content and attribute, if, each, and template coming soon)
21
+
22
+ ### Changed
23
+ - the underlying way queries are normalized and passed to the server has changed (no external api changes)
13
24
 
14
25
  ## 0.8.24 - 2014-12-05
15
26
  ### Added
data/CONTRIBUTING.md CHANGED
@@ -28,6 +28,7 @@ git checkout -b my-feature-branch
28
28
  ```
29
29
 
30
30
  #### Bundle Install and Test
31
+
31
32
  Ensure that PhantomJS is installed on your computer.
32
33
 
33
34
  ```
@@ -65,7 +66,7 @@ We definitely appreciate pull requests that highlight or reproduce a problem, ev
65
66
 
66
67
  Implement your feature or bug fix.
67
68
 
68
- Ruby style is enforced with [Rubocop](https://github.com/bbatsov/rubocop), run `bundle exec rubocop` and fix any style issues highlighted.
69
+ Ruby style is enforced with [RuboCop](https://github.com/bbatsov/rubocop), run `bundle exec rubocop` and fix any style issues highlighted.
69
70
 
70
71
  Make sure that `bundle exec rake` completes without errors.
71
72
 
@@ -130,4 +131,4 @@ Go back to your pull request after a few minutes and see whether it passed muste
130
131
 
131
132
  #### Thank You
132
133
 
133
- Please do know that we really appreciate and value your time and work. We love you, really.
134
+ Please do know that we really appreciate and value your time and work. We love you, really.
@@ -2,38 +2,35 @@
2
2
  [![Code Climate](https://codeclimate.com/github/voltrb/volt/badges/gpa.svg)](https://codeclimate.com/github/voltrb/volt)
3
3
  [![Build Status](http://img.shields.io/travis/voltrb/volt/master.svg?style=flat)](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
- [![Volt Chat](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/voltrb/volt)
6
5
 
7
- ** For the current status of volt, read: http://voltframework.com/blog
6
+ For the current status of Volt, read: http://voltframework.com/blog
8
7
 
9
- # Volt
10
-
11
- Volt is a Ruby web framework where your ruby code runs on both the server and the client (via [opal](https://github.com/opal/opal)). The DOM automatically updates as the user interacts with the page. Page state can be stored in the URL. If the user hits a URL directly, the HTML will first be rendered on the server for faster load times and easier indexing by search engines.
8
+ Volt is a Ruby web framework where your Ruby code runs on both the server and the client (via [Opal](https://github.com/opal/opal)). The DOM automatically updates as the user interacts with the page. Page state can be stored in the URL. If the user hits a URL directly, the HTML will first be rendered on the server for faster load times and easier indexing by search engines.
12
9
 
13
10
  Instead of syncing data between the client and server via HTTP, Volt uses a persistent connection between the client and server. When data is updated on one client, it is updated in the database and any other listening clients (with almost no setup code needed).
14
11
 
15
- Pages HTML is written in a template language where you can put ruby between ```{{``` and ```}}```. Volt uses data flow/reactive programming to automatically and intelligently propagate changes to the DOM (or any other code wanting to know when a value updates). When something in the DOM changes, Volt intelligently updates only the nodes that need to be changed.
12
+ Pages HTML is written in a template language where you can put Ruby between `{{` and `}}`. Volt uses data flow/reactive programming to automatically and intelligently propagate changes to the DOM (or any other code wanting to know when a value updates). When something in the DOM changes, Volt intelligently updates only the nodes that need to be changed.
16
13
 
17
14
  See some demo videos here:
18
15
  - [Volt Todos Example](https://www.youtube.com/watch?v=Tg-EtRnMz7o)
19
16
  - [Pagination Example](https://www.youtube.com/watch?v=1uanfzMLP9g)
20
17
  - [Routes and Templates](https://www.youtube.com/watch?v=1yNMP3XR6jU)
21
- - [Isomorphic App Development - Rubyconf 2014](https://www.youtube.com/watch?v=7i6AL7Walc4)
22
- - [Build a Blog with Volt](https://www.youtube.com/watch?v=c478sMlhx1o) ** Note: The blog video is outdated, expect an updated version soon.
18
+ - [Isomorphic App Development - RubyConf 2014](https://www.youtube.com/watch?v=7i6AL7Walc4)
19
+ - [Build a Blog with Volt](https://www.youtube.com/watch?v=c478sMlhx1o)
20
+ **Note:** The blog video is outdated, expect an updated version soon.
23
21
 
24
22
  Check out demo apps:
25
23
  - https://github.com/voltrb/todos3
26
24
  - https://github.com/voltrb/contactsdemo
27
25
 
28
-
29
26
  # Docs
30
27
 
31
28
  Read the [full docs on Volt here](http://voltframework.com/docs)
32
29
 
30
+ There is also a [work in progress tutorial](https://github.com/rhgraysonii/volt_tutorial) by @rhgraysonii
31
+
33
32
  # Contributing
34
33
 
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.
34
+ 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.
36
35
 
37
36
  [![Pledgie](https://pledgie.com/campaigns/26731.png?skin_name=chrome)](https://pledgie.com/campaigns/26731)
38
-
39
-
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'bundler'
2
2
  require 'bundler/gem_tasks'
3
- Bundler.require(:development)
3
+ require 'bundler/setup'
4
4
  require 'rubocop/rake_task'
5
5
  require 'opal'
6
6
 
@@ -10,14 +10,7 @@ Opal.append_path(File.expand_path('../lib', __FILE__))
10
10
  require 'opal/rspec/rake_task'
11
11
 
12
12
  task :docs do
13
- `bundle exec yardoc 'lib/**/*.rb' - Readme.md docs/*`
14
- # require 'yard'
15
- # require 'yard-docco'
16
- #
17
- # YARD::Rake::YardocTask.new do |t|
18
- # t.files = ['lib/**/*.rb']
19
- # # t.options = ['--any', '--extra', '--opts'] # optional
20
- # end
13
+ `bundle exec yardoc -r Readme.md --markup-provider=redcarpet --markup=markdown 'lib/**/*.rb' - Readme.md docs/*.md`
21
14
  end
22
15
 
23
16
  # Setup the opal:rspec task
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.27.beta3
1
+ 0.8.27.beta4
@@ -16,6 +16,14 @@ module Volt
16
16
  validate login_field, unique: true, length: 8
17
17
  validate :email, email: true
18
18
 
19
+ permissions(:read) do
20
+ # Never pass the hashed_password to the client
21
+ deny :hashed_password
22
+
23
+ # Deny all if this isn't the owner
24
+ deny if !_id == Volt.user_id && !new?
25
+ end
26
+
19
27
  if RUBY_PLATFORM == 'opal'
20
28
  # Don't validate on the server
21
29
  validate :password, length: 8
@@ -9,12 +9,19 @@ class DataStore
9
9
  end
10
10
 
11
11
  def query(collection, query)
12
- # Extract query parts
13
- query, skip, limit = query
12
+ allowed_methods = ['find', 'skip', 'limit']
14
13
 
15
- cursor = db[collection].find(query)
16
- cursor = cursor.skip(skip) if skip
17
- cursor = cursor.limit(limit) if limit
14
+ cursor = db[collection]
15
+
16
+ query.each do |query_part|
17
+ method_name, *args = query_part
18
+
19
+ unless allowed_methods.include?(method_name.to_s)
20
+ raise "`#{method_name}` is not part of a valid query"
21
+ end
22
+
23
+ cursor = cursor.send(method_name, *args)
24
+ end
18
25
 
19
26
  cursor.to_a
20
27
  end
@@ -22,4 +29,5 @@ class DataStore
22
29
  def drop_database
23
30
  db.connection.drop_database(Volt.config.db_name)
24
31
  end
32
+
25
33
  end
@@ -31,8 +31,16 @@ class LiveQuery
31
31
 
32
32
  def notify_added(index, data, skip_channel)
33
33
  # puts "Added: #{index} - #{data.inspect}"
34
+ # Make model for testing permissions against
35
+ model = model_for_filter(data)
36
+
34
37
  notify! do |channel|
35
- channel.send_message('added', nil, @collection, @query, index, data)
38
+ filtered_data = nil
39
+ Volt.as_user(channel.user_id) do
40
+ filtered_data = model.filtered_attributes
41
+ end
42
+
43
+ channel.send_message('added', nil, @collection, @query, index, filtered_data)
36
44
  end
37
45
  end
38
46
 
@@ -44,9 +52,15 @@ class LiveQuery
44
52
  end
45
53
 
46
54
  def notify_changed(id, data, skip_channel)
47
- # puts "Changed: #{id}, #{data}"
55
+ model = model_for_filter(data)
56
+
48
57
  notify!(skip_channel) do |channel|
49
- channel.send_message('changed', nil, @collection, @query, id, data)
58
+ filtered_data = nil
59
+ Volt.as_user(channel.user_id) do
60
+ filtered_data = model.filtered_attributes
61
+ end
62
+ # puts "Changed: #{id}, #{data} to #{channel.inspect}"
63
+ channel.send_message('changed', nil, @collection, @query, id, filtered_data)
50
64
  end
51
65
  end
52
66
 
@@ -55,10 +69,17 @@ class LiveQuery
55
69
  @query_tracker.results.map.with_index do |data, index|
56
70
  data = data.dup
57
71
  data['_id'] = data['_id'].to_s
72
+
58
73
  [index, data]
59
74
  end
60
75
  end
61
76
 
77
+ # Lookup the model class
78
+ def model_class
79
+ # recreate the "path" from the collection
80
+ Volt::Model.class_at_path([collection, :[]])
81
+ end
82
+
62
83
  def add_channel(channel)
63
84
  @channels << channel
64
85
  end
@@ -83,4 +104,25 @@ class LiveQuery
83
104
  yield(channel)
84
105
  end
85
106
  end
107
+
108
+ # Takes in data to be sent to the client and sets up a model to test
109
+ # field permissions against
110
+ def model_for_filter(data)
111
+ klass = Volt::Model.class_at_path([@collection])
112
+ model = nil
113
+
114
+ # Skip read validations when loading the model, no need to check read when checking
115
+ # permissions.
116
+ # TODO: We should probably document the possibility of data leak here, though really you
117
+ # shouldn't be storing anything inside of the permissions block.
118
+ Volt::Model.no_validate do
119
+ model = klass.new(data, {}, :loaded)
120
+ end
121
+
122
+ model
123
+ end
124
+
125
+ def inspect
126
+ "<#{self.class.to_s} #{@collection}: #{@query.inspect}>"
127
+ end
86
128
  end
@@ -1,20 +1,27 @@
1
1
  require_relative 'live_query'
2
2
  require 'volt/utils/generic_pool'
3
3
 
4
+ # LiveQueryPool runs on the server and keeps track of all outstanding
5
+ # queries.
6
+
4
7
  class LiveQueryPool < Volt::GenericPool
5
8
  def initialize(data_store)
6
- super()
7
9
  @data_store = data_store
10
+ super()
8
11
  end
9
12
 
10
13
  def lookup(collection, query)
14
+ # collection = collection.to_sym
11
15
  query = normalize_query(query)
12
16
 
13
17
  super(collection, query)
14
18
  end
15
19
 
16
20
  def updated_collection(collection, skip_channel)
21
+ # collection = collection.to_sym
22
+ # puts "RUN UPDATE FOR #{collection.inspect} - #{@pool.inspect}"
17
23
  lookup_all(collection).each do |live_query|
24
+ # puts "UPDATE COLLECTION: #{collection} - #{live_query.inspect}"
18
25
  live_query.run(skip_channel)
19
26
  end
20
27
  end
@@ -24,6 +31,7 @@ class LiveQueryPool < Volt::GenericPool
24
31
  # Creates the live query if it doesn't exist, and stores it so it
25
32
  # can be found later.
26
33
  def create(collection, query = {})
34
+ # collection = collection.to_sym
27
35
  # If not already setup, create a new one for this collection/query
28
36
  LiveQuery.new(self, @data_store, collection, query)
29
37
  end
@@ -19,8 +19,14 @@ class QueryTasks < Volt::TaskHandler
19
19
  live_query = @@live_query_pool.lookup(collection, query)
20
20
  track_channel_in_live_query(live_query)
21
21
 
22
- # puts "Load data on #{collection.inspect} - #{query.inspect}"
23
- live_query.add_channel(@channel)
22
+ if @channel
23
+ # For requests from the client (with @channel), we track the channel
24
+ # so we can send the results back. Server side requests don't stay live,
25
+ # they simply return to :dirty once the query is issued.
26
+ @channel.user_id = Volt.user_id
27
+
28
+ live_query.add_channel(@channel)
29
+ end
24
30
 
25
31
  errors = {}
26
32
 
@@ -32,6 +38,15 @@ class QueryTasks < Volt::TaskHandler
32
38
  error = { error: exception.message }
33
39
  end
34
40
 
41
+ if initial_data
42
+ # Only send the filtered attributes for this user
43
+ initial_data.map! do |data|
44
+ [data[0], live_query.model_for_filter(data[1]).filtered_attributes]
45
+ end
46
+ end
47
+
48
+ # @@live_query_pool.print
49
+
35
50
  [initial_data, error]
36
51
  end
37
52
 
@@ -47,6 +62,9 @@ class QueryTasks < Volt::TaskHandler
47
62
  def remove_listener(collection, query)
48
63
  live_query = @@live_query_pool.lookup(collection, query)
49
64
  live_query.remove_channel(@channel)
65
+ #
66
+ # puts "REMOVE LIST1"
67
+ # @@live_query_pool.print
50
68
  end
51
69
 
52
70
  # Removes a channel from all associated live queries
@@ -18,25 +18,25 @@ class StoreTasks < Volt::TaskHandler
18
18
  collection = store.send(:"_#{path[-2]}")
19
19
 
20
20
  # See if the model has already been made
21
- model = collection.find_one(_id: data[:_id])
21
+ collection.find(_id: data[:_id]).fetch_first do |model|
22
+ # Otherwise assign to the collection
23
+ model ||= collection
22
24
 
23
- # Otherwise assign to the collection
24
- model ||= collection
25
+ # Create a buffer
26
+ buffer = model.buffer
25
27
 
26
- # Create a buffer
27
- buffer = model.buffer
28
+ # Assign the data
29
+ buffer.assign_attributes(data, true)
28
30
 
29
- # Assign the data
30
- buffer.attributes = data
31
-
32
- buffer
31
+ buffer
32
+ end
33
33
  end
34
34
 
35
35
  def save(collection, path, data)
36
36
  data = data.symbolize_keys
37
- model = nil
38
- Volt::Model.nosave do
39
- model = load_model(collection, path, data)
37
+ promise = nil
38
+ Volt::Model.no_validate do
39
+ promise = load_model(collection, path, data)
40
40
  end
41
41
 
42
42
  # On the backend, the promise is resolved before its returned, so we can
@@ -44,19 +44,36 @@ class StoreTasks < Volt::TaskHandler
44
44
  #
45
45
  # Pass the channel as a thread-local so that we don't update the client
46
46
  # who sent the update.
47
- Thread.current['in_channel'] = @channel
48
- promise = model.save!.then do |result|
49
- return nil
50
- end
47
+ #
48
+ # return another promise
49
+ return promise.then do |model|
50
+ Thread.current['in_channel'] = @channel
51
+ save_promise = model.save!.then do |result|
51
52
 
52
- Thread.current['in_channel'] = nil
53
+ next nil
54
+ end
53
55
 
54
- promise
56
+ Thread.current['in_channel'] = nil
57
+
58
+ puts "DONE--"
59
+ next save_promise
60
+ end
55
61
  end
56
62
 
57
63
  def delete(collection, id)
58
- db[collection].remove('_id' => id)
64
+ # Load the model, then call .destroy on it
65
+ store.send(:"_#{collection}").where(_id: id).fetch_first do |model|
66
+ if model
67
+ if model.can_delete?
68
+ db[collection].remove('_id' => id)
69
+ else
70
+ raise "Permissions did not allow #{collection} #{id} to be deleted."
71
+ end
59
72
 
60
- QueryTasks.live_query_pool.updated_collection(collection, @channel)
73
+ QueryTasks.live_query_pool.updated_collection(collection, @channel)
74
+ else
75
+ raise "Could not find #{id} in #{collection}"
76
+ end
77
+ end
61
78
  end
62
79
  end
@@ -4,27 +4,29 @@ class UserTasks < Volt::TaskHandler
4
4
  def login(login, password)
5
5
  query = { User.login_field => login }
6
6
 
7
- store._users.find(query).then do |users|
8
- user = users.first
9
- fail 'User could not be found' unless user
7
+ # During login we need access to the user's info even though we aren't the user
8
+ Volt.skip_permissions do
9
+ store._users.find(query).fetch_first do |user|
10
+ fail 'User could not be found' unless user
10
11
 
11
- match_pass = BCrypt::Password.new(user._hashed_password)
12
- fail 'Password did not match' unless match_pass == password
13
- fail 'app_secret is not configured' unless Volt.config.app_secret
12
+ match_pass = BCrypt::Password.new(user._hashed_password)
13
+ fail 'Password did not match' unless match_pass == password
14
+ fail 'app_secret is not configured' unless Volt.config.app_secret
14
15
 
15
- # TODO: returning here should be possible, but causes some issues
16
- # Salt the user id with the app_secret so the end user can't
17
- # tamper with the cookie
18
- signature = BCrypt::Password.create(salty_password(user._id))
16
+ # TODO: returning here should be possible, but causes some issues
17
+ # Salt the user id with the app_secret so the end user can't
18
+ # tamper with the cookie
19
+ signature = Digest::SHA256.hexdigest(salty_user_id(user._id))
19
20
 
20
- # Return user_id:hash on user id
21
- next "#{user._id}:#{signature}"
21
+ # Return user_id:hash on user id
22
+ next "#{user._id}:#{signature}"
23
+ end
22
24
  end
23
25
  end
24
26
 
25
27
  private
26
28
 
27
- def salty_password(user_id)
29
+ def salty_user_id(user_id)
28
30
  "#{Volt.config.app_secret}::#{user_id}"
29
31
  end
30
32
  end
data/lib/volt/boot.rb CHANGED
@@ -9,10 +9,12 @@ end
9
9
  module Volt
10
10
  def self.boot(app_path)
11
11
  # Run the app config to load all users config files
12
- Volt.run_files_in_config_folder
12
+ unless RUBY_PLATFORM == 'opal'
13
+ Volt.run_files_in_config_folder
13
14
 
14
- if Volt.server?
15
- $page = Page.new
15
+ if Volt.server?
16
+ $page = Page.new
17
+ end
16
18
  end
17
19
 
18
20
  component_paths = ComponentPaths.new(app_path)
@@ -21,6 +21,7 @@ class Pry
21
21
 
22
22
  # Flush after each line
23
23
  Volt::Computation.flush!
24
+ Volt::Timers.flush_next_tick_timers!
24
25
  end
25
26
  end
26
27
  end
@@ -18,6 +18,21 @@ class Generate < Thor
18
18
  directory('component', component_folder, component_name: name)
19
19
  end
20
20
 
21
+
22
+ desc 'gem GEM', 'Creates a component gem where you can share a component'
23
+ method_option :bin, type: :boolean, default: false, aliases: '-b', banner: 'Generate a binary for your library.'
24
+ method_option :test, type: :string, lazy_default: 'rspec', aliases: '-t', banner: "Generate a test directory for your library: 'rspec' is the default, but 'minitest' is also supported."
25
+ method_option :edit, type: :string, aliases: '-e',
26
+ lazy_default: [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find { |e| !e.nil? && !e.empty? },
27
+ required: false, banner: '/path/to/your/editor',
28
+ desc: 'Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)'
29
+
30
+ def gem(name)
31
+ require 'volt/cli/new_gem'
32
+
33
+ NewGem.new(self, name, options)
34
+ end
35
+
21
36
  def self.source_root
22
37
  File.expand_path(File.join(File.dirname(__FILE__), '../../../templates'))
23
38
  end
data/lib/volt/cli.rb CHANGED
@@ -79,18 +79,25 @@ module Volt
79
79
  Volt::CLI::Runner.run_file(file_path)
80
80
  end
81
81
 
82
- desc 'gem GEM', 'Creates a component gem where you can share a component'
83
- method_option :bin, type: :boolean, default: false, aliases: '-b', banner: 'Generate a binary for your library.'
84
- method_option :test, type: :string, lazy_default: 'rspec', aliases: '-t', banner: "Generate a test directory for your library: 'rspec' is the default, but 'minitest' is also supported."
85
- method_option :edit, type: :string, aliases: '-e',
86
- lazy_default: [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find { |e| !e.nil? && !e.empty? },
87
- required: false, banner: '/path/to/your/editor',
88
- desc: 'Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)'
89
-
90
- def gem(name)
91
- require 'volt/cli/new_gem'
92
-
93
- NewGem.new(self, name, options)
82
+ desc 'drop_collection NAME', 'Drop a Collection in your MongoDB'
83
+
84
+ def drop_collection(collection)
85
+ ENV['SERVER'] = 'true'
86
+ require 'mongo'
87
+ require 'volt/boot'
88
+
89
+ Volt.boot(Dir.pwd)
90
+
91
+ host = Volt.config.db_host || 'localhost'
92
+ port = Volt.config.db_port || Mongo::MongoClient::DEFAULT_PORT
93
+ name = Volt.config.db_name
94
+
95
+ say("Connecting to #{host}:#{port}", :yellow)
96
+ db = Mongo::MongoClient.new(host, port).db(name)
97
+ drop = db.drop_collection(collection)
98
+
99
+ say("Collection #{collection} on #{name} couldn't be dropped", :red) if drop == false
100
+ say("Collection #{collection} on #{name} dropped", :green) if drop == true
94
101
  end
95
102
 
96
103
  def self.source_root
data/lib/volt/config.rb CHANGED
@@ -48,7 +48,7 @@ else
48
48
  app_name = File.basename(Dir.pwd)
49
49
  {
50
50
  app_name: app_name,
51
- db_name: ENV['DB_NAME'] || (app_name + '_' + Volt.env.to_s),
51
+ db_name: (ENV['DB_NAME'] || (app_name + '_' + Volt.env.to_s)).gsub('.', '_'),
52
52
  db_host: ENV['DB_HOST'] || 'localhost',
53
53
  db_port: (ENV['DB_PORT'] || 27_017).to_i,
54
54
  db_driver: ENV['DB_DRIVER'] || 'mongo'
@@ -10,7 +10,6 @@ module Volt
10
10
  # the dom for the controllers view.
11
11
  attr_accessor :section
12
12
 
13
-
14
13
  # Container returns the node that is parent to all nodes in the section.
15
14
  def container
16
15
  section.container_node
@@ -20,7 +19,18 @@ module Volt
20
19
  section.range
21
20
  end
22
21
 
23
-
22
+ # yield_html renders the content passed into a tag as a string. You can ```.watch!```
23
+ # ```yield_html``` and it will be run again when anything in the template changes.
24
+ def yield_html
25
+ if (template_path = attrs.content_template_path)
26
+ # TODO: Don't use $page global
27
+ @yield_renderer ||= StringTemplateRender.new($page, self, template_path)
28
+ @yield_renderer.html
29
+ else
30
+ # no template, empty string
31
+ ''
32
+ end
33
+ end
24
34
 
25
35
  def self.model(val)
26
36
  @default_model = val
@@ -133,7 +143,7 @@ module Volt
133
143
  end
134
144
 
135
145
  def loaded?
136
- respond_to?(:state) && state == :loaded
146
+ self.model.respond_to?(:loaded?) && self.model.loaded?
137
147
  end
138
148
 
139
149
  # Check if this controller responds_to method, or the model
@@ -5,6 +5,7 @@ require 'volt/extra_core/blank'
5
5
  require 'volt/extra_core/stringify_keys'
6
6
  require 'volt/extra_core/string'
7
7
  require 'volt/extra_core/true_false'
8
+ require 'volt/extra_core/hash'
8
9
  require 'volt/extra_core/class'
9
10
  if RUBY_PLATFORM == 'opal'
10
11
  # TODO: != does not work with opal for some reason
@@ -0,0 +1,26 @@
1
+ # module Hash
2
+ # class Indifferent < Hash
3
+ # def []=(key, value)
4
+ # super(convert_key(key), value)
5
+ # end
6
+ #
7
+ # def [](key)
8
+ # super(convert_key(key))
9
+ # end
10
+ #
11
+ # def key?(key)
12
+ # super(convert_key(key))
13
+ # end
14
+ #
15
+ # def fetch(key, *args)
16
+ # super(convert_key(key), *args)
17
+ # end
18
+ #
19
+ # private
20
+ #
21
+ # # Converts all keys to symbols for assignments
22
+ # def convert_key(key)
23
+ # key.is_a?(String) ? key : key.to_sym
24
+ # end
25
+ # end
26
+ # end