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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +3 -2
- data/{Readme.md → README.md} +9 -12
- data/Rakefile +2 -9
- data/VERSION +1 -1
- data/app/volt/models/user.rb +8 -0
- data/app/volt/tasks/live_query/data_store.rb +13 -5
- data/app/volt/tasks/live_query/live_query.rb +45 -3
- data/app/volt/tasks/live_query/live_query_pool.rb +9 -1
- data/app/volt/tasks/query_tasks.rb +20 -2
- data/app/volt/tasks/store_tasks.rb +37 -20
- data/app/volt/tasks/user_tasks.rb +15 -13
- data/lib/volt/boot.rb +5 -3
- data/lib/volt/cli/console.rb +1 -0
- data/lib/volt/cli/generate.rb +15 -0
- data/lib/volt/cli.rb +19 -12
- data/lib/volt/config.rb +1 -1
- data/lib/volt/controllers/model_controller.rb +13 -3
- data/lib/volt/extra_core/extra_core.rb +1 -0
- data/lib/volt/extra_core/hash.rb +26 -0
- data/lib/volt/extra_core/object.rb +5 -1
- data/lib/volt/models/array_model.rb +86 -35
- data/lib/volt/models/associations.rb +53 -0
- data/lib/volt/models/buffer.rb +22 -10
- data/lib/volt/models/dirty.rb +88 -0
- data/lib/volt/models/errors.rb +21 -0
- data/lib/volt/models/field_helpers.rb +2 -2
- data/lib/volt/models/listener_tracker.rb +17 -0
- data/lib/volt/models/model.rb +213 -69
- data/lib/volt/models/model_helpers.rb +27 -17
- data/lib/volt/models/permissions.rb +246 -0
- data/lib/volt/models/persistors/array_store.rb +149 -81
- data/lib/volt/models/persistors/base.rb +16 -0
- data/lib/volt/models/persistors/cookies.rb +14 -9
- data/lib/volt/models/persistors/flash.rb +3 -0
- data/lib/volt/models/persistors/local_store.rb +0 -16
- data/lib/volt/models/persistors/model_store.rb +1 -2
- data/lib/volt/models/persistors/query/normalizer.rb +51 -0
- data/lib/volt/models/persistors/query/query_listener.rb +21 -5
- data/lib/volt/models/persistors/query/query_listener_pool.rb +0 -9
- data/lib/volt/models/persistors/store.rb +8 -0
- data/lib/volt/models/persistors/store_state.rb +4 -27
- data/lib/volt/models/state_helpers.rb +11 -0
- data/lib/volt/models/state_manager.rb +43 -0
- data/lib/volt/models/url.rb +5 -5
- data/lib/volt/models/validations.rb +38 -41
- data/lib/volt/models/validators/email_validator.rb +4 -9
- data/lib/volt/models/validators/format_validator.rb +23 -8
- data/lib/volt/models/validators/length_validator.rb +2 -2
- data/lib/volt/models/validators/numericality_validator.rb +7 -3
- data/lib/volt/models/validators/phone_number_validator.rb +4 -9
- data/lib/volt/models/validators/presence_validator.rb +2 -2
- data/lib/volt/models/validators/unique_validator.rb +2 -2
- data/lib/volt/models/validators/user_validation.rb +6 -0
- data/lib/volt/models.rb +8 -3
- data/lib/volt/page/bindings/attribute_binding.rb +10 -4
- data/lib/volt/page/bindings/content_binding.rb +9 -5
- data/lib/volt/page/bindings/if_binding.rb +25 -2
- data/lib/volt/page/bindings/template_binding.rb +19 -1
- data/lib/volt/page/bindings/yield_binding.rb +31 -0
- data/lib/volt/page/page.rb +11 -16
- data/lib/volt/reactive/class_eventable.rb +71 -0
- data/lib/volt/reactive/computation.rb +79 -10
- data/lib/volt/reactive/dependency.rb +27 -8
- data/lib/volt/reactive/eventable.rb +36 -22
- data/lib/volt/reactive/reactive_array.rb +2 -3
- data/lib/volt/reactive/reactive_hash.rb +8 -3
- data/lib/volt/router/routes.rb +2 -1
- data/lib/volt/server/component_templates.rb +0 -2
- data/lib/volt/server/html_parser/component_view_scope.rb +59 -0
- data/lib/volt/server/html_parser/view_handler.rb +3 -0
- data/lib/volt/server/html_parser/view_parser.rb +1 -0
- data/lib/volt/server/html_parser/view_scope.rb +17 -41
- data/lib/volt/server/rack/component_paths.rb +1 -10
- data/lib/volt/server/rack/index_files.rb +9 -4
- data/lib/volt/server/rack/opal_files.rb +22 -14
- data/lib/volt/server/rack/quiet_common_logger.rb +1 -1
- data/lib/volt/server/socket_connection_handler.rb +4 -0
- data/lib/volt/spec/setup.rb +26 -0
- data/lib/volt/tasks/dispatcher.rb +11 -0
- data/lib/volt/utils/event_counter.rb +29 -0
- data/lib/volt/utils/generic_pool.rb +12 -0
- data/lib/volt/utils/modes.rb +40 -0
- data/lib/volt/utils/promise_patch.rb +66 -0
- data/lib/volt/utils/timers.rb +33 -0
- data/lib/volt/volt/users.rb +48 -5
- data/lib/volt.rb +4 -0
- data/spec/apps/kitchen_sink/Gemfile +3 -1
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +9 -8
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +9 -0
- data/spec/apps/kitchen_sink/app/main/controllers/yield_component_controller.rb +5 -0
- data/spec/apps/kitchen_sink/app/main/views/main/cookie_test.html +1 -1
- data/spec/apps/kitchen_sink/app/main/views/main/index.html +1 -1
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +2 -1
- data/spec/apps/kitchen_sink/app/main/views/main/yield.html +18 -0
- data/spec/apps/kitchen_sink/app/main/views/yield-component/index.html +4 -0
- data/spec/extra_core/logger_spec.rb +4 -2
- data/spec/integration/user_spec.rb +42 -42
- data/spec/integration/yield_spec.rb +18 -0
- data/spec/models/associations_spec.rb +37 -0
- data/spec/models/dirty_spec.rb +102 -0
- data/spec/models/model_spec.rb +64 -8
- data/spec/models/model_state_spec.rb +24 -0
- data/spec/models/permissions_spec.rb +96 -0
- data/spec/models/user_spec.rb +8 -5
- data/spec/models/user_validation_spec.rb +24 -0
- data/spec/models/validations_spec.rb +44 -5
- data/spec/models/validators/email_validator_spec.rb +109 -82
- data/spec/models/validators/format_validator_spec.rb +4 -107
- data/spec/models/validators/length_validator_spec.rb +9 -9
- data/spec/models/validators/phone_number_validator_spec.rb +60 -103
- data/spec/models/validators/shared_examples_for_validators.rb +123 -0
- data/spec/reactive/class_eventable_spec.rb +37 -0
- data/spec/reactive/computation_spec.rb +68 -3
- data/spec/reactive/dependency_spec.rb +71 -0
- data/spec/reactive/eventable_spec.rb +21 -0
- data/spec/reactive/reactive_hash_spec.rb +12 -0
- data/spec/router/routes_spec.rb +50 -50
- data/spec/server/html_parser/view_parser_spec.rb +0 -3
- data/spec/server/rack/component_paths_spec.rb +11 -0
- data/spec/server/rack/quite_common_logger_spec.rb +3 -4
- data/spec/spec_helper.rb +7 -3
- data/templates/component/config/dependencies.rb +1 -7
- data/templates/component/config/routes.rb +1 -1
- data/templates/component/controllers/main_controller.rb.tt +20 -0
- data/templates/component/views/{index → main}/index.html.tt +0 -0
- data/templates/newgem/lib/newgem.rb.tt +1 -3
- data/templates/project/app/main/config/routes.rb +3 -3
- data/templates/project/app/main/views/main/main.html.tt +4 -4
- data/templates/project/config/app.rb.tt +6 -0
- data/volt.gemspec +11 -7
- metadata +96 -42
- data/lib/volt/models/model_state.rb +0 -21
- data/templates/component/controllers/main_controller.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 385a2c074963c20e45ace62abbc680bfd52b54c0
|
4
|
+
data.tar.gz: 490a62ce8f645fa02310e1af96ef45026849c64e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee8dd6c7cadff2bfa91694caadfa0996147b3827f2cc820122efa80caeba216ff2bff74607a94c168e90a4b1adbdd545f37a5bd2f8bad2bc774d4485e51ce328
|
7
|
+
data.tar.gz: 3fcfb4f21a033882f4c508f94cc149c28126060b53eb60f119d62f102a7f18721f02005c67ec92f1fea0c94456549559d30cc3dd98881697e289ec52ef67211a
|
data/.travis.yml
CHANGED
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 [
|
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.
|
data/{Readme.md → README.md}
RENAMED
@@ -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
|
-
|
6
|
+
For the current status of Volt, read: http://voltframework.com/blog
|
8
7
|
|
9
|
-
|
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
|
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 -
|
22
|
-
- [Build a Blog with Volt](https://www.youtube.com/watch?v=c478sMlhx1o)
|
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?
|
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
|
-
|
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.
|
1
|
+
0.8.27.beta4
|
data/app/volt/models/user.rb
CHANGED
@@ -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
|
-
|
13
|
-
query, skip, limit = query
|
12
|
+
allowed_methods = ['find', 'skip', 'limit']
|
14
13
|
|
15
|
-
cursor = db[collection]
|
16
|
-
|
17
|
-
|
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
|
-
|
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
|
-
|
55
|
+
model = model_for_filter(data)
|
56
|
+
|
48
57
|
notify!(skip_channel) do |channel|
|
49
|
-
|
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
|
-
|
23
|
-
|
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
|
-
|
21
|
+
collection.find(_id: data[:_id]).fetch_first do |model|
|
22
|
+
# Otherwise assign to the collection
|
23
|
+
model ||= collection
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
# Create a buffer
|
26
|
+
buffer = model.buffer
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
+
# Assign the data
|
29
|
+
buffer.assign_attributes(data, true)
|
28
30
|
|
29
|
-
|
30
|
-
|
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
|
-
|
38
|
-
Volt::Model.
|
39
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
53
|
+
next nil
|
54
|
+
end
|
53
55
|
|
54
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
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
|
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
|
-
|
12
|
+
unless RUBY_PLATFORM == 'opal'
|
13
|
+
Volt.run_files_in_config_folder
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
if Volt.server?
|
16
|
+
$page = Page.new
|
17
|
+
end
|
16
18
|
end
|
17
19
|
|
18
20
|
component_paths = ComponentPaths.new(app_path)
|
data/lib/volt/cli/console.rb
CHANGED
data/lib/volt/cli/generate.rb
CHANGED
@@ -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 '
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
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?(:
|
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
|