volt 0.9.5.pre3 → 0.9.5.pre4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/app/volt/models/active_volt_instance.rb +6 -0
- data/app/volt/tasks/live_query/live_query.rb +2 -2
- data/app/volt/tasks/query_tasks.rb +1 -1
- data/lib/volt/models.rb +9 -11
- data/lib/volt/models/array_model.rb +80 -36
- data/lib/volt/models/field_helpers.rb +5 -1
- data/lib/volt/models/permissions.rb +81 -72
- data/lib/volt/models/persistors/array_store.rb +5 -25
- data/lib/volt/models/root_models/store_root.rb +0 -1
- data/lib/volt/reactive/reactive_array.rb +4 -1
- data/lib/volt/server/message_bus/peer_to_peer.rb +2 -2
- data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +1 -1
- data/lib/volt/server/middleware/default_middleware_stack.rb +3 -2
- data/lib/volt/server/rack/asset_files.rb +18 -0
- data/lib/volt/server/rack/component_code.rb +1 -1
- data/lib/volt/server/rack/index_files.rb +2 -2
- data/lib/volt/server/rack/opal_files.rb +3 -0
- data/lib/volt/spec/setup.rb +0 -2
- data/lib/volt/utils/promise_extensions.rb +4 -3
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +5 -0
- data/lib/volt/volt/users.rb +2 -2
- data/spec/extra_core/class_spec.rb +11 -0
- data/spec/integration/bindings_spec.rb +0 -1
- data/spec/models/array_model_spec.rb +16 -0
- data/spec/models/field_helpers_spec.rb +8 -1
- data/spec/models/permissions_spec.rb +25 -0
- data/volt.gemspec +1 -1
- metadata +5 -5
- data/lib/volt/utils/promise.rb +0 -429
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 237e158164bd2f394e80998fa3314f52024fbebc
|
4
|
+
data.tar.gz: abd1d018ffd679afba929518326f0139cce4deb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48b5fa1a075beab16bbe901532b11e46e6ee4aa065d21db169bc425efc9def807d2d286f5ea4a2f6b569458b05fcb7052721ceb3df26aa7d585990a1d4fe6ffb
|
7
|
+
data.tar.gz: d18c325ef1f789d19a6ceaf2327452fd5b06dcefda74d0eeeca8e66b070599544ebf5176779d465a67bf641987f9af40de34a3eb7bbcffcd4b8d469910b17aea
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
- You can now disable auto-import of JS/CSS with ```disable_auto_import``` in a dependencies.rb file
|
6
6
|
- Opal was upgraded to 0.8, which brings sourcemaps back (yah!)
|
7
7
|
- Page load performance was improved, and more of sprockets was used for component loading.
|
8
|
+
- You can now return promises in permissions blocks. Also, can_read?, can_create?, and .can_delete?
|
9
|
+
- Anything in /public is now served via Rack::Static in the default middleware stack. (So you can put user uploaded images in there)
|
8
10
|
|
9
11
|
## 0.9.4
|
10
12
|
### Lingo Change
|
@@ -37,7 +37,7 @@ class LiveQuery
|
|
37
37
|
notify! do |channel|
|
38
38
|
filtered_data = nil
|
39
39
|
Volt.as_user(channel.user_id) do
|
40
|
-
filtered_data = model.filtered_attributes
|
40
|
+
filtered_data = model.filtered_attributes.sync
|
41
41
|
end
|
42
42
|
|
43
43
|
channel.send_message('added', nil, @collection, @query, index, filtered_data)
|
@@ -57,7 +57,7 @@ class LiveQuery
|
|
57
57
|
notify!(skip_channel) do |channel|
|
58
58
|
filtered_data = nil
|
59
59
|
Volt.as_user(channel.user_id) do
|
60
|
-
filtered_data = model.filtered_attributes
|
60
|
+
filtered_data = model.filtered_attributes.sync
|
61
61
|
end
|
62
62
|
# puts "Changed: #{id}, #{data} to #{channel.inspect}"
|
63
63
|
channel.send_message('changed', nil, @collection, @query, id, filtered_data)
|
@@ -26,7 +26,7 @@ class QueryTasks < Volt::Task
|
|
26
26
|
if initial_data
|
27
27
|
# Only send the filtered attributes for this user
|
28
28
|
initial_data.map! do |data|
|
29
|
-
[data[0], live_query.model_for_filter(data[1]).filtered_attributes]
|
29
|
+
[data[0], live_query.model_for_filter(data[1]).filtered_attributes.sync]
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
data/lib/volt/models.rb
CHANGED
@@ -12,16 +12,14 @@ require 'volt/models/persistors/local_store'
|
|
12
12
|
require 'volt/models/root_models/root_models'
|
13
13
|
# require 'volt/models/root_models/store_root'
|
14
14
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
#
|
20
|
-
|
21
|
-
# require 'opal'
|
15
|
+
# Requrie in opal's promise library
|
16
|
+
if RUBY_PLATFORM == 'opal'
|
17
|
+
require 'promise'
|
18
|
+
else
|
19
|
+
# Opal doesn't expose its promise library directly
|
20
|
+
require 'opal'
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
# TODO: remove once https://github.com/opal/opal/pull/725 is released.
|
22
|
+
gem_dir = File.join(Opal.gem_dir, '..')
|
23
|
+
require(gem_dir + '/stdlib/promise')
|
24
|
+
end
|
27
25
|
require 'volt/utils/promise_extensions'
|
@@ -22,21 +22,50 @@ module Volt
|
|
22
22
|
# of immediately returning). To accomplish this, we call the
|
23
23
|
# #run_once_loaded method on the persistor.
|
24
24
|
def self.proxy_with_load(*method_names)
|
25
|
+
imethods = instance_methods(false)
|
25
26
|
method_names.each do |method_name|
|
26
|
-
|
27
|
-
|
27
|
+
# Sometimes we want to alias a method_missing method, so we use super
|
28
|
+
# instead to call it, if its not defined locally.
|
29
|
+
if imethods.include?(method_name)
|
30
|
+
imethod = true
|
31
|
+
old_method_name = :"__old_#{method_name}"
|
32
|
+
alias_method(old_method_name, method_name)
|
33
|
+
else
|
34
|
+
imethod = false
|
35
|
+
end
|
28
36
|
|
29
37
|
define_method(method_name) do |*args, &block|
|
38
|
+
if imethod
|
39
|
+
call_orig = proc do |*args|
|
40
|
+
send(old_method_name, *args)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
call_orig = proc do |*args|
|
44
|
+
super(*args)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
30
48
|
# track on the root dep
|
31
49
|
persistor.try(:root_dep).try(:depend)
|
32
50
|
|
33
51
|
if persistor.respond_to?(:run_once_loaded) &&
|
34
52
|
!Volt.in_mode?(:no_model_promises)
|
35
|
-
persistor.run_once_loaded
|
36
|
-
|
53
|
+
promise = persistor.run_once_loaded.then do
|
54
|
+
# We are already loaded and the result is going to be wrapped
|
55
|
+
Volt.run_in_mode(:no_model_promises) do
|
56
|
+
call_orig.call(*args)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if block
|
61
|
+
promise = promise.then do |val|
|
62
|
+
block.call(val)
|
63
|
+
end
|
37
64
|
end
|
65
|
+
|
66
|
+
promise
|
38
67
|
else
|
39
|
-
|
68
|
+
call_orig.call(*args)
|
40
69
|
end
|
41
70
|
end
|
42
71
|
end
|
@@ -119,11 +148,17 @@ module Volt
|
|
119
148
|
|
120
149
|
def delete(val)
|
121
150
|
# Check to make sure the models are allowed to be deleted
|
122
|
-
if !val.is_a?(Model)
|
123
|
-
|
124
|
-
|
151
|
+
if !val.is_a?(Model)
|
152
|
+
# Not a model, return as a Promise
|
153
|
+
super(val).then
|
125
154
|
else
|
126
|
-
|
155
|
+
val.can_delete?.then do |can_delete|
|
156
|
+
if can_delete
|
157
|
+
super(val)
|
158
|
+
else
|
159
|
+
Promise.new.reject("permissions did not allow delete for #{val.inspect}.")
|
160
|
+
end
|
161
|
+
end
|
127
162
|
end
|
128
163
|
end
|
129
164
|
|
@@ -199,6 +234,10 @@ module Volt
|
|
199
234
|
super(*args)
|
200
235
|
end
|
201
236
|
|
237
|
+
def flatten(*args)
|
238
|
+
wrap_values(to_a.flatten(*args))
|
239
|
+
end
|
240
|
+
|
202
241
|
def new_model(*args)
|
203
242
|
Volt::Model.class_at_path(options[:path]).new(*args)
|
204
243
|
end
|
@@ -285,34 +324,39 @@ module Volt
|
|
285
324
|
|
286
325
|
|
287
326
|
if model.is_a?(Model)
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
reactive_array_append(model)
|
294
|
-
|
295
|
-
@persistor.added(model, @array.size - 1)
|
296
|
-
|
297
|
-
# Validate and save
|
298
|
-
promise = model.run_changed.then do
|
299
|
-
# Mark the model as not new
|
300
|
-
model.instance_variable_set('@new', false)
|
301
|
-
|
302
|
-
# Mark the model as loaded
|
303
|
-
model.change_state_to(:loaded_state, :loaded)
|
304
|
-
|
305
|
-
end.fail do |err|
|
306
|
-
# remove from the collection because it failed to save on the server
|
307
|
-
# we don't need to call delete on the server.
|
308
|
-
index = @array.index(model)
|
309
|
-
delete_at(index, true)
|
327
|
+
promise = model.can_create?.then do |can_create|
|
328
|
+
unless can_create
|
329
|
+
fail "permissions did not allow create for #{model.inspect}"
|
330
|
+
end
|
331
|
+
end.then do
|
310
332
|
|
311
|
-
#
|
312
|
-
|
333
|
+
# Add it to the array and trigger any watches or on events.
|
334
|
+
reactive_array_append(model)
|
313
335
|
|
314
|
-
|
315
|
-
|
336
|
+
@persistor.added(model, @array.size - 1)
|
337
|
+
end.then do
|
338
|
+
nil.then do
|
339
|
+
# Validate and save
|
340
|
+
model.run_changed
|
341
|
+
end.then do
|
342
|
+
# Mark the model as not new
|
343
|
+
model.instance_variable_set('@new', false)
|
344
|
+
|
345
|
+
# Mark the model as loaded
|
346
|
+
model.change_state_to(:loaded_state, :loaded)
|
347
|
+
|
348
|
+
end.fail do |err|
|
349
|
+
# remove from the collection because it failed to save on the server
|
350
|
+
# we don't need to call delete on the server.
|
351
|
+
index = @array.index(model)
|
352
|
+
delete_at(index, true)
|
353
|
+
|
354
|
+
# remove from the id list
|
355
|
+
@persistor.try(:remove_tracking_id, model)
|
356
|
+
|
357
|
+
# re-raise, err might not be an Error object, so we use a rejected promise to re-raise
|
358
|
+
Promise.new.reject(err)
|
359
|
+
end
|
316
360
|
end
|
317
361
|
else
|
318
362
|
promise = nil.then do
|
@@ -337,7 +381,7 @@ module Volt
|
|
337
381
|
end
|
338
382
|
|
339
383
|
# We need to setup the proxy methods below where they are defined.
|
340
|
-
proxy_with_load :[], :size, :last, :reverse, :all, :to_a, :empty?
|
384
|
+
proxy_with_load :[], :size, :last, :reverse, :all, :to_a, :empty?, :present?, :blank?
|
341
385
|
|
342
386
|
end
|
343
387
|
end
|
@@ -36,13 +36,16 @@ module FieldHelpers
|
|
36
36
|
module ClassMethods
|
37
37
|
# field lets you declare your fields instead of using the underscore syntax.
|
38
38
|
# An optional class restriction can be passed in.
|
39
|
-
def field(name, klass = nil,
|
39
|
+
def field(name, klass = nil, options = {})
|
40
40
|
if klass && !VALID_FIELD_CLASSES.include?(klass)
|
41
41
|
klass_names = VALID_FIELD_CLASSES.map(&:to_s).join(', ')
|
42
42
|
msg = "valid field types is currently limited to #{klass_names}"
|
43
43
|
fail FieldHelpers::InvalidFieldClass, msg
|
44
44
|
end
|
45
45
|
|
46
|
+
self.fields_data ||= {}
|
47
|
+
self.fields_data[name] = [klass, options]
|
48
|
+
|
46
49
|
if klass
|
47
50
|
# Add type validation, execpt for String, since anything can be a string.
|
48
51
|
unless klass == String
|
@@ -69,6 +72,7 @@ module FieldHelpers
|
|
69
72
|
end
|
70
73
|
|
71
74
|
def self.included(base)
|
75
|
+
base.class_attribute :fields_data
|
72
76
|
base.send :extend, ClassMethods
|
73
77
|
end
|
74
78
|
end
|
@@ -106,41 +106,37 @@ module Volt
|
|
106
106
|
!owner_id.nil? && owner_id == Volt.current_user_id
|
107
107
|
end
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
def can_read?
|
116
|
-
action_allowed?(:read)
|
117
|
-
end
|
118
|
-
|
119
|
-
def can_create?
|
120
|
-
action_allowed?(:create)
|
109
|
+
[:create, :update, :read, :delete].each do |action|
|
110
|
+
# Each can_action? (can_delete? for example) returns a promise that
|
111
|
+
# resolves to true or false if the user
|
112
|
+
define_method(:"can_#{action}?") do
|
113
|
+
action_allowed?(action)
|
114
|
+
end
|
121
115
|
end
|
122
116
|
|
123
117
|
# Checks if any denies are in place for an action (read or delete)
|
124
118
|
def action_allowed?(action_name)
|
125
119
|
# TODO: this does some unnecessary work
|
126
|
-
compute_allow_and_deny(action_name)
|
120
|
+
compute_allow_and_deny(action_name).then do
|
127
121
|
|
128
|
-
|
122
|
+
deny = @__deny_fields == true || (@__deny_fields && @__deny_fields.size > 0)
|
129
123
|
|
130
|
-
|
124
|
+
clear_allow_and_deny
|
131
125
|
|
132
|
-
|
126
|
+
!deny
|
127
|
+
end
|
133
128
|
end
|
134
129
|
|
135
130
|
# Return the list of allowed fields
|
136
131
|
def allow_and_deny_fields(action_name)
|
137
|
-
compute_allow_and_deny(action_name)
|
132
|
+
compute_allow_and_deny(action_name).then do
|
138
133
|
|
139
|
-
|
134
|
+
result = [@__allow_fields, @__deny_fields]
|
140
135
|
|
141
|
-
|
136
|
+
clear_allow_and_deny
|
142
137
|
|
143
|
-
|
138
|
+
result
|
139
|
+
end
|
144
140
|
end
|
145
141
|
|
146
142
|
# Filter fields returns the attributes with any denied or not allowed fields
|
@@ -149,70 +145,80 @@ module Volt
|
|
149
145
|
# Run with Volt.as_user(...) to change the user
|
150
146
|
def filtered_attributes
|
151
147
|
# Run the read permission check
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
172
|
-
|
173
|
-
# Deeply filter any nested models
|
174
|
-
return result.map do |key, value|
|
175
|
-
if value.is_a?(Model)
|
176
|
-
value = value.filtered_attributes
|
148
|
+
allow_and_deny_fields(:read).then do |allow, deny|
|
149
|
+
|
150
|
+
result = nil
|
151
|
+
|
152
|
+
if allow && allow != true && allow.size > 0
|
153
|
+
# always keep id
|
154
|
+
allow << :id
|
155
|
+
|
156
|
+
# Only keep fields in the allow list
|
157
|
+
result = @attributes.select { |key| allow.include?(key) }
|
158
|
+
elsif deny == true
|
159
|
+
# Only keep id
|
160
|
+
# TODO: Should this be a full reject?
|
161
|
+
result = @attributes.reject { |key| key != :id }
|
162
|
+
elsif deny && deny.size > 0
|
163
|
+
# Reject any in the deny list
|
164
|
+
result = @attributes.reject { |key| deny.include?(key) }
|
165
|
+
else
|
166
|
+
result = @attributes
|
177
167
|
end
|
178
168
|
|
179
|
-
|
180
|
-
|
169
|
+
# Deeply filter any nested models
|
170
|
+
result.then do |res|
|
171
|
+
keys = []
|
172
|
+
values = []
|
173
|
+
res.each do |key, value|
|
174
|
+
if value.is_a?(Model)
|
175
|
+
value = value.filtered_attributes
|
176
|
+
end
|
177
|
+
keys << key
|
178
|
+
values << value
|
179
|
+
end
|
180
|
+
|
181
|
+
Promise.when(*values).then do |values|
|
182
|
+
keys.zip(values).to_h
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
181
186
|
end
|
182
187
|
|
183
188
|
private
|
184
189
|
|
185
190
|
def run_permissions(action_name = nil)
|
186
|
-
compute_allow_and_deny(action_name)
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
191
|
+
compute_allow_and_deny(action_name).then do
|
192
|
+
|
193
|
+
errors = {}
|
194
|
+
|
195
|
+
if @__allow_fields == true
|
196
|
+
# Allow all fields
|
197
|
+
elsif @__allow_fields && @__allow_fields.size > 0
|
198
|
+
# Deny all not specified in the allow list
|
199
|
+
changed_attributes.keys.each do |field_name|
|
200
|
+
unless @__allow_fields.include?(field_name)
|
201
|
+
add_error_if_changed(errors, field_name)
|
202
|
+
end
|
197
203
|
end
|
198
204
|
end
|
199
|
-
end
|
200
205
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
206
|
+
if @__deny_fields == true
|
207
|
+
# Don't allow any field changes
|
208
|
+
changed_attributes.keys.each do |field_name|
|
209
|
+
add_error_if_changed(errors, field_name)
|
210
|
+
end
|
211
|
+
elsif @__deny_fields
|
212
|
+
# Allow all except the denied
|
213
|
+
@__deny_fields.each do |field_name|
|
214
|
+
add_error_if_changed(errors, field_name) if changed?(field_name)
|
215
|
+
end
|
210
216
|
end
|
211
|
-
end
|
212
217
|
|
213
|
-
|
218
|
+
clear_allow_and_deny
|
214
219
|
|
215
|
-
|
220
|
+
errors
|
221
|
+
end
|
216
222
|
end
|
217
223
|
|
218
224
|
def clear_allow_and_deny
|
@@ -235,10 +241,13 @@ module Volt
|
|
235
241
|
# Run each of the permission blocks for this action
|
236
242
|
permissions = self.class.__permissions__
|
237
243
|
if permissions && (blocks = permissions[action_name])
|
238
|
-
blocks.
|
244
|
+
results = blocks.map do |block|
|
239
245
|
# Call the block, pass the action name
|
240
246
|
instance_exec(action_name, &block)
|
241
247
|
end
|
248
|
+
|
249
|
+
# Wait for any promises returned
|
250
|
+
Promise.when(*results)
|
242
251
|
end
|
243
252
|
end
|
244
253
|
|