volt 0.9.3.pre2 → 0.9.3.pre3

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/app/volt/tasks/query_tasks.rb +0 -7
  4. data/app/volt/tasks/store_tasks.rb +0 -6
  5. data/docs/UPGRADE_GUIDE.md +2 -0
  6. data/lib/volt/cli/asset_compile.rb +2 -2
  7. data/lib/volt/cli/console.rb +21 -0
  8. data/lib/volt/config.rb +0 -10
  9. data/lib/volt/controllers/collection_helpers.rb +18 -0
  10. data/lib/volt/controllers/model_controller.rb +2 -12
  11. data/lib/volt/extra_core/object.rb +19 -0
  12. data/lib/volt/models.rb +14 -9
  13. data/lib/volt/models/array_model.rb +62 -22
  14. data/lib/volt/models/associations.rb +16 -1
  15. data/lib/volt/models/model.rb +27 -15
  16. data/lib/volt/models/model_helpers/model_helpers.rb +29 -0
  17. data/lib/volt/models/permissions.rb +15 -4
  18. data/lib/volt/models/persistors/array_store.rb +40 -0
  19. data/lib/volt/models/persistors/model_store.rb +2 -2
  20. data/lib/volt/models/persistors/query/query_listener.rb +3 -1
  21. data/lib/volt/models/persistors/store.rb +2 -1
  22. data/lib/volt/models/root_models/root_models.rb +31 -0
  23. data/lib/volt/models/root_models/store_root.rb +36 -0
  24. data/lib/volt/models/validators/unique_validator.rb +1 -1
  25. data/lib/volt/page/bindings/each_binding.rb +56 -47
  26. data/lib/volt/page/page.rb +5 -5
  27. data/lib/volt/reactive/reactive_array.rb +9 -6
  28. data/lib/volt/server.rb +2 -2
  29. data/lib/volt/server/component_templates.rb +7 -4
  30. data/lib/volt/server/message_bus/message_encoder.rb +9 -1
  31. data/lib/volt/server/rack/component_code.rb +8 -1
  32. data/lib/volt/server/rack/index_files.rb +5 -2
  33. data/lib/volt/tasks/{task_handler.rb → task.rb} +6 -6
  34. data/lib/volt/utils/promise.rb +429 -0
  35. data/lib/volt/utils/promise_extensions.rb +79 -0
  36. data/lib/volt/version.rb +1 -1
  37. data/lib/volt/volt/app.rb +5 -2
  38. data/lib/volt/volt/server_setup/app.rb +28 -7
  39. data/spec/apps/kitchen_sink/app/main/controllers/server/simple_http_controller.rb +1 -1
  40. data/spec/apps/kitchen_sink/app/main/views/main/store.html +3 -0
  41. data/spec/extra_core/object_spec.rb +13 -0
  42. data/spec/integration/store_spec.rb +10 -0
  43. data/spec/models/associations_spec.rb +48 -26
  44. data/spec/models/model_spec.rb +23 -7
  45. data/spec/models/persistors/store_spec.rb +28 -0
  46. data/spec/models/validators/unique_validator_spec.rb +1 -1
  47. data/spec/spec_helper.rb +4 -1
  48. data/spec/utils/promise_extensions_spec.rb +42 -0
  49. data/templates/component/config/initializers/boot.rb +10 -0
  50. data/templates/{project/app → component/config/initializers/client}/.empty_directory +0 -0
  51. data/templates/component/config/initializers/server/.empty_directory +0 -0
  52. data/templates/newgem/app/newgem/config/initializers/client/.empty_directory +0 -0
  53. data/templates/newgem/app/newgem/config/initializers/server/.empty_directory +0 -0
  54. data/templates/project/Gemfile.tt +6 -2
  55. data/templates/project/app/main/config/initializers/boot.rb +10 -0
  56. data/templates/project/app/main/config/initializers/client/.empty_directory +0 -0
  57. data/templates/project/app/main/config/initializers/server/.empty_directory +0 -0
  58. data/templates/project/config/app.rb.tt +3 -0
  59. data/templates/project/config/initializers/client/.empty_directory +0 -0
  60. data/templates/project/config/initializers/server/.empty_directory +0 -0
  61. metadata +22 -5
  62. data/lib/volt/utils/promise_patch.rb +0 -70
@@ -32,6 +32,35 @@ module Volt
32
32
  end
33
33
 
34
34
 
35
+ # Return the attributes that are only for this model and any hash sub models
36
+ # but not any sub-associations.
37
+ def self_attributes
38
+ # Don't store any sub-models, those will do their own saving.
39
+ attributes.reject { |k, v| v.is_a?(ArrayModel) }.map do |k,v|
40
+ if v.is_a?(Model)
41
+ v = v.self_attributes
42
+ end
43
+
44
+ [k,v]
45
+ end.to_h
46
+ end
47
+
48
+
49
+ # Takes the persistor if there is one and
50
+ def setup_persistor(persistor)
51
+ # Use page as the default persistor
52
+ persistor ||= Persistors::Page
53
+ if persistor.respond_to?(:new)
54
+ @persistor = persistor.new(self)
55
+ else
56
+ # an already initialized persistor was passed in
57
+ @persistor = persistor
58
+ end
59
+ end
60
+
61
+
62
+
63
+
35
64
  module ClassMethods
36
65
  # Gets the class for a model at the specified path.
37
66
  def class_at_path(path)
@@ -151,22 +151,33 @@ module Volt
151
151
  # Run the read permission check
152
152
  allow, deny = allow_and_deny_fields(:read)
153
153
 
154
+ result = nil
155
+
154
156
  if allow && allow != true && allow.size > 0
155
157
  # always keep id
156
158
  allow << :id
157
159
 
158
160
  # Only keep fields in the allow list
159
- return @attributes.select { |key| allow.include?(key) }
161
+ result = @attributes.select { |key| allow.include?(key) }
160
162
  elsif deny == true
161
163
  # Only keep id
162
164
  # TODO: Should this be a full reject?
163
- return @attributes.reject { |key| key != :id }
165
+ result = @attributes.reject { |key| key != :id }
164
166
  elsif deny && deny.size > 0
165
167
  # Reject any in the deny list
166
- return @attributes.reject { |key| deny.include?(key) }
168
+ result = @attributes.reject { |key| deny.include?(key) }
167
169
  else
168
- return @attributes
170
+ result = @attributes
169
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
177
+ end
178
+
179
+ [key, value]
180
+ end.to_h
170
181
  end
171
182
 
172
183
  private
@@ -167,6 +167,46 @@ module Volt
167
167
  Cursor.new([], opts)
168
168
  end
169
169
 
170
+ # Call a method on the model once the model is loaded. Return a promise
171
+ # that will resolve the result of the method, or reject with any
172
+ # Exceptions.
173
+ def run_once_loaded(block)
174
+ promise = Promise.new
175
+
176
+ # call once the method is loaded.
177
+ model_loaded = proc do
178
+ begin
179
+ result = yield
180
+ promise.resolve(result)
181
+ rescue Exception => error
182
+ promise.reject(error)
183
+ end
184
+ end
185
+
186
+ # Run the block after resolve if a block is passed in
187
+ if block
188
+ promise2 = promise.then do |val|
189
+ block.call(val)
190
+ end
191
+ else
192
+ promise2 = promise
193
+ end
194
+
195
+ if @model.loaded_state == :loaded
196
+ model_loaded.call
197
+ else
198
+ proc do |comp|
199
+ if @model.loaded_state == :loaded
200
+ model_loaded.call
201
+
202
+ comp.stop
203
+ end
204
+ end.watch!
205
+ end
206
+
207
+ promise2
208
+ end
209
+
170
210
  # Returns a promise that is resolved/rejected when the query is complete. Any
171
211
  # passed block will be passed to the promises then. Then will be passed the model.
172
212
  def fetch(&block)
@@ -155,8 +155,7 @@ module Volt
155
155
 
156
156
  # Return the attributes that are only for this store, not any sub-associations.
157
157
  def self_attributes
158
- # Don't store any sub-stores, those will do their own saving.
159
- @model.attributes.reject { |k, v| v.is_a?(Model) || v.is_a?(ArrayModel) }
158
+ @model.self_attributes
160
159
  end
161
160
 
162
161
  def collection
@@ -170,6 +169,7 @@ module Volt
170
169
 
171
170
  # Do the actual writing of data to the database, only runs on the backend.
172
171
  def save_to_db!(values)
172
+ # puts "SAVE TO DB: #{values.inspect}"
173
173
  # Check to make sure the model has no validation errors.
174
174
  errors = @model.errors
175
175
  return errors if errors.present?
@@ -26,7 +26,9 @@ module Volt
26
26
  # When the initial data comes back, add it into the stores.
27
27
  @stores.dup.each do |store|
28
28
  # Clear if there are existing items
29
- store.model.clear if store.model.size > 0
29
+ Volt.run_in_mode(:no_model_promises) do
30
+ store.model.clear if store.model.size > 0
31
+ end
30
32
 
31
33
  results.each do |index, data|
32
34
  store.add(index, data)
@@ -26,7 +26,8 @@ module Volt
26
26
  if method_name.plural?
27
27
  model = @model.new_array_model([], options)
28
28
  else
29
- model = @model.new_model(nil, options)
29
+ options[:persistor] = @model.persistor
30
+ model= @model.new_model(nil, options)
30
31
 
31
32
  # TODO: Might not need to assign this
32
33
  @model.attributes ||= {}
@@ -0,0 +1,31 @@
1
+ # When you get the root of a collection (.store, .page, etc...), it gives you
2
+ # back a unique class depending on the collection. This allows you to add
3
+ # things to the root easily.
4
+ #
5
+ # The name of the model will be {CollectionName}Root. All root model classes
6
+ # inherit from BaseRootModel, which provides methods to access the model's
7
+ # from the root. (store.items if you have an Item model for example)
8
+
9
+ # Create a model that is above all of the root models.
10
+ class BaseRootModel < Volt::Model
11
+ end
12
+
13
+
14
+ ROOT_MODEL_NAMES = [:Store, :Page, :Params, :Cookies, :LocalStore, :Flash]
15
+
16
+ ROOT_MODEL_NAMES.each do |base_name|
17
+ Object.const_set("#{base_name}Root", Class.new(BaseRootModel))
18
+ end
19
+
20
+ module Volt
21
+ class RootModels
22
+ def self.add_model_class(klass)
23
+ method_name = klass.to_s.underscore.pluralize
24
+
25
+ # Create a getter for each model class off of root.
26
+ BaseRootModel.send(:define_method, method_name) do
27
+ get(method_name)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ # StoreRoot should already be setup when this class is already loaded. It is a
2
+ # Volt::Model that is loaded as the base class for the root of store.
3
+ #
4
+ # In order to support setting properties directly on store, we create a table
5
+ # called "root_store_models", and create a single
6
+
7
+ require 'volt/models/root_models/root_models'
8
+
9
+ module Volt
10
+ module StoreRootHelpers
11
+ def model_for_root
12
+ root = get(:root_store_models).first_or_create
13
+
14
+ root
15
+ end
16
+
17
+
18
+ def get(attr_name, expand = false)
19
+ if attr_name.singular? && attr_name.to_sym != :id
20
+ model_for_root.get(attr_name, expand)
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def set(attr_name, value, &block)
27
+ if attr_name.singular? && attr_name.to_sym != :id
28
+ model_for_root.set(attr_name, value, &block)
29
+ else
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ StoreRoot.send(:include, Volt::StoreRootHelpers)
@@ -12,7 +12,7 @@ module Volt
12
12
 
13
13
  # Check if the value is taken
14
14
  # TODO: need a way to handle scope for unique
15
- return $page.store.get(model.path[-2]).where(query).fetch_first do |item|
15
+ return $page.store.get(model.path[-2]).where(query).first do |item|
16
16
  if item
17
17
  message = (args.is_a?(Hash) && args[:message]) || 'is already taken'
18
18
 
@@ -45,8 +45,13 @@ module Volt
45
45
  @removed_listener = @value.on('removed') { |position| item_removed(position) }
46
46
  end
47
47
 
48
- templates_size = @templates.size
49
- values_size = values.size
48
+ templates_size = nil
49
+ values_size = nil
50
+
51
+ Volt.run_in_mode(:no_model_promises) do
52
+ templates_size = @templates.size
53
+ values_size = values.size
54
+ end
50
55
 
51
56
  # Start over, re-create all nodes
52
57
  (templates_size - 1).downto(0) do |index|
@@ -59,65 +64,69 @@ module Volt
59
64
  end
60
65
 
61
66
  def item_removed(position)
62
- # Remove dependency
63
- @templates[position].context.locals[:_index_dependency].remove
64
- @templates[position].context.locals["_#{@item_name}_dependency".to_sym].remove
67
+ Volt.run_in_mode(:no_model_promises) do
68
+ # Remove dependency
69
+ @templates[position].context.locals[:_index_dependency].remove
70
+ @templates[position].context.locals["_#{@item_name}_dependency".to_sym].remove
65
71
 
66
- @templates[position].remove_anchors
67
- @templates[position].remove
68
- @templates.delete_at(position)
72
+ @templates[position].remove_anchors
73
+ @templates[position].remove
74
+ @templates.delete_at(position)
69
75
 
70
- # Removed at the position, update context for every item after this position
71
- update_indexes_after(position)
76
+ # Removed at the position, update context for every item after this position
77
+ update_indexes_after(position)
78
+ end
72
79
  end
73
80
 
74
81
  def item_added(position)
75
- binding_name = @@binding_number
76
- @@binding_number += 1
77
-
78
- if position >= @templates.size
79
- # Setup new bindings in the spot we want to insert the item
80
- dom_section.insert_anchor_before_end(binding_name)
81
- else
82
- # Insert the item before an existing item
83
- dom_section.insert_anchor_before(binding_name, @templates[position].binding_name)
84
- end
82
+ Volt.run_in_mode(:no_model_promises) do
83
+ binding_name = @@binding_number
84
+ @@binding_number += 1
85
+
86
+ if position >= @templates.size
87
+ # Setup new bindings in the spot we want to insert the item
88
+ dom_section.insert_anchor_before_end(binding_name)
89
+ else
90
+ # Insert the item before an existing item
91
+ dom_section.insert_anchor_before(binding_name, @templates[position].binding_name)
92
+ end
85
93
 
86
- # TODORW: :parent => @value may change
87
- item_context = SubContext.new({ _index_value: position, parent: @value }, @context)
88
- item_context.locals[@item_name.to_sym] = proc { @value[item_context.locals[:_index_value]] }
94
+ # TODORW: :parent => @value may change
95
+ item_context = SubContext.new({ _index_value: position, parent: @value }, @context)
96
+ item_context.locals[@item_name.to_sym] = proc { @value[item_context.locals[:_index_value]] }
89
97
 
90
- position_dependency = Dependency.new
91
- item_context.locals[:_index_dependency] = position_dependency
98
+ position_dependency = Dependency.new
99
+ item_context.locals[:_index_dependency] = position_dependency
92
100
 
93
- # Get and set index
94
- item_context.locals[:_index=] = proc do |val|
95
- position_dependency.changed!
96
- item_context.locals[:_index_value] = val
97
- end
101
+ # Get and set index
102
+ item_context.locals[:_index=] = proc do |val|
103
+ position_dependency.changed!
104
+ item_context.locals[:_index_value] = val
105
+ end
98
106
 
99
- # Get and set value
100
- value_dependency = Dependency.new
101
- item_context.locals["_#{@item_name}_dependency".to_sym] = value_dependency
107
+ # Get and set value
108
+ value_dependency = Dependency.new
109
+ item_context.locals["_#{@item_name}_dependency".to_sym] = value_dependency
102
110
 
103
- item_context.locals["#{@item_name}=".to_sym] = proc do |val|
104
- value_dependency.changed!
105
- @value[item_context.locals[:_index_value]] = val
106
- end
111
+ item_context.locals["#{@item_name}=".to_sym] = proc do |val|
112
+ value_dependency.changed!
113
+ @value[item_context.locals[:_index_value]] = val
114
+ end
107
115
 
108
- # If the user provides an each_with_index, we can assign the lookup for the index
109
- # variable here.
110
- if @index_name
111
- item_context.locals[@index_name.to_sym] = proc do
112
- position_dependency.depend
113
- item_context.locals[:_index_value]
116
+ # If the user provides an each_with_index, we can assign the lookup for the index
117
+ # variable here.
118
+ if @index_name
119
+ item_context.locals[@index_name.to_sym] = proc do
120
+ position_dependency.depend
121
+ item_context.locals[:_index_value]
122
+ end
114
123
  end
115
- end
116
124
 
117
- item_template = TemplateRenderer.new(@volt_app, @target, item_context, binding_name, @template_name)
118
- @templates.insert(position, item_template)
125
+ item_template = TemplateRenderer.new(@volt_app, @target, item_context, binding_name, @template_name)
126
+ @templates.insert(position, item_template)
119
127
 
120
- update_indexes_after(position)
128
+ update_indexes_after(position)
129
+ end
121
130
  end
122
131
 
123
132
  # When items are added or removed in the middle of the list, we need
@@ -6,7 +6,7 @@ module Volt
6
6
  def initialize(volt_app)
7
7
  @volt_app = volt_app
8
8
  # Run the code to setup the page
9
- @page = Model.new
9
+ @page = PageRoot.new
10
10
 
11
11
  @url = URL.new
12
12
  @params = @url.params
@@ -47,19 +47,19 @@ module Volt
47
47
  end
48
48
 
49
49
  def flash
50
- @flash ||= Model.new({}, persistor: Persistors::Flash)
50
+ @flash ||= FlashRoot.new({}, persistor: Persistors::Flash)
51
51
  end
52
52
 
53
53
  def store
54
- @store ||= Model.new({}, persistor: Persistors::StoreFactory.new(tasks))
54
+ @store ||= StoreRoot.new({}, persistor: Persistors::StoreFactory.new(tasks))
55
55
  end
56
56
 
57
57
  def local_store
58
- @local_store ||= Model.new({}, persistor: Persistors::LocalStore)
58
+ @local_store ||= LocalStoreRoot.new({}, persistor: Persistors::LocalStore)
59
59
  end
60
60
 
61
61
  def cookies
62
- @cookies ||= Model.new({}, persistor: Persistors::Cookies)
62
+ @cookies ||= CookiesRoot.new({}, persistor: Persistors::Cookies)
63
63
  end
64
64
 
65
65
  def tasks
@@ -39,8 +39,10 @@ module Volt
39
39
  if block
40
40
  count = 0
41
41
 
42
- size.times do |index|
43
- count += 1 if block.call(self[index])
42
+ Volt.run_in_mode(:no_model_promises) do
43
+ size.times do |index|
44
+ count += 1 if block.call(self[index])
45
+ end
44
46
  end
45
47
 
46
48
  count
@@ -90,7 +92,7 @@ module Volt
90
92
  # TODO: Handle a range
91
93
  def [](index)
92
94
  # Handle a negative index, depend on size
93
- index = size + index if index < 0
95
+ index = @array.size + index if index < 0
94
96
 
95
97
  # Get or create the dependency
96
98
  dep = (@array_deps[index] ||= Dependency.new)
@@ -120,6 +122,7 @@ module Volt
120
122
  alias_method :length, :size
121
123
 
122
124
  def delete_at(index)
125
+ size = @array.size
123
126
  # Handle a negative index
124
127
  index = size + index if index < 0
125
128
 
@@ -183,8 +186,8 @@ module Volt
183
186
  def <<(value)
184
187
  result = (@array << value)
185
188
 
186
- trigger_for_index!(size - 1)
187
- trigger_added!(size - 1)
189
+ trigger_for_index!(@array.size - 1)
190
+ trigger_added!(@array.size - 1)
188
191
  trigger_size_change!
189
192
 
190
193
  result
@@ -192,7 +195,7 @@ module Volt
192
195
 
193
196
  def +(array)
194
197
  fail 'not implemented yet'
195
- old_size = size
198
+ old_size = @array.size
196
199
 
197
200
  # TODO: += is funky here, might need to make a .plus! method
198
201
  result = ReactiveArray.new(@array.dup + array)