volt 0.9.5.pre3 → 0.9.5.pre4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 552337e005627aa06bff5950bcd94cbb5ab58684
4
- data.tar.gz: cf7e89e6862c51796186c9034a62e378e27abcfc
3
+ metadata.gz: 237e158164bd2f394e80998fa3314f52024fbebc
4
+ data.tar.gz: abd1d018ffd679afba929518326f0139cce4deb0
5
5
  SHA512:
6
- metadata.gz: 9c42d896c2c9cb881778a8a90dee2c2ff398213540bbaa4183f13c748106b7f8d0724cf50327ce090e7e1c30dcb7336d758f4a5d5ec61521420b53a33478593d
7
- data.tar.gz: ea0d96fa24038a6d45d99a3adb3ee59cd072adef219a48aa7c5543e63de9f52f7abf16fd6564aa4ed740e7cc137fcbd2c65220e38a76cf1e8ea7a664fb7ac868
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
@@ -0,0 +1,6 @@
1
+ class ActiveVoltInstance < Volt::Model
2
+ field :server_id, String
3
+ field :ips, String
4
+ field :port, Fixnum
5
+ field :time, Time
6
+ end
@@ -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
- # Fow now, we're keeping a volt copy of the promise library from opal 0.8,
16
- # since opal 0.7.x's version has some bugs.
17
- # if RUBY_PLATFORM == 'opal'
18
- # require 'promise'
19
- # else
20
- # # Opal doesn't expose its promise library directly
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
- # gem_dir = File.join(Opal.gem_dir, '..')
24
- # require(gem_dir + '/stdlib/promise')
25
- # end
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
- old_method_name = :"__old_#{method_name}"
27
- alias_method(old_method_name, method_name)
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(block) do
36
- send(old_method_name, *args)
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
- send(old_method_name, *args)
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) || val.can_delete?
123
- result = super
124
- Promise.new.resolve(result)
151
+ if !val.is_a?(Model)
152
+ # Not a model, return as a Promise
153
+ super(val).then
125
154
  else
126
- Promise.new.reject("permissions did not allow delete for #{val.inspect}.")
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
- if !model.can_create?
289
- fail "permissions did not allow create for #{model.inspect}"
290
- end
291
-
292
- # Add it to the array and trigger any watches or on events.
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
- # remove from the id list
312
- @persistor.try(:remove_tracking_id, model)
333
+ # Add it to the array and trigger any watches or on events.
334
+ reactive_array_append(model)
313
335
 
314
- # re-raise, err might not be an Error object, so we use a rejected promise to re-raise
315
- Promise.new.reject(err)
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, auto_cast = true)
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
- # Returns boolean if the model can be deleted
110
- def can_delete?
111
- action_allowed?(:delete)
112
- end
113
-
114
- # Checks the read permissions
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
- deny = @__deny_fields == true || (@__deny_fields && @__deny_fields.size > 0)
122
+ deny = @__deny_fields == true || (@__deny_fields && @__deny_fields.size > 0)
129
123
 
130
- clear_allow_and_deny
124
+ clear_allow_and_deny
131
125
 
132
- !deny
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
- result = [@__allow_fields, @__deny_fields]
134
+ result = [@__allow_fields, @__deny_fields]
140
135
 
141
- clear_allow_and_deny
136
+ clear_allow_and_deny
142
137
 
143
- result
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
- allow, deny = allow_and_deny_fields(:read)
153
-
154
- result = nil
155
-
156
- if allow && allow != true && allow.size > 0
157
- # always keep id
158
- allow << :id
159
-
160
- # Only keep fields in the allow list
161
- result = @attributes.select { |key| allow.include?(key) }
162
- elsif deny == true
163
- # Only keep id
164
- # TODO: Should this be a full reject?
165
- result = @attributes.reject { |key| key != :id }
166
- elsif deny && deny.size > 0
167
- # Reject any in the deny list
168
- result = @attributes.reject { |key| deny.include?(key) }
169
- else
170
- result = @attributes
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
- [key, value]
180
- end.to_h
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
- errors = {}
189
-
190
- if @__allow_fields == true
191
- # Allow all fields
192
- elsif @__allow_fields && @__allow_fields.size > 0
193
- # Deny all not specified in the allow list
194
- changed_attributes.keys.each do |field_name|
195
- unless @__allow_fields.include?(field_name)
196
- add_error_if_changed(errors, field_name)
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
- if @__deny_fields == true
202
- # Don't allow any field changes
203
- changed_attributes.keys.each do |field_name|
204
- add_error_if_changed(errors, field_name)
205
- end
206
- elsif @__deny_fields
207
- # Allow all except the denied
208
- @__deny_fields.each do |field_name|
209
- add_error_if_changed(errors, field_name) if changed?(field_name)
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
- clear_allow_and_deny
218
+ clear_allow_and_deny
214
219
 
215
- errors
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.each do |block|
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