volt 0.4.9 → 0.4.10

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: e0ae38f2f69535b60b4fe850f9c6a9073da98b75
4
- data.tar.gz: a2a08253c6f76f71ac495ec91d13efa4a35f0a98
3
+ metadata.gz: 11fd8a0c37e1e364ca98038498604a3b897aa391
4
+ data.tar.gz: 6aafe7c02f864bc4da06dffe7c380675c92b9931
5
5
  SHA512:
6
- metadata.gz: f39067395499d7b5bd46088b343612d9dc8b79d1fa5b53885777fa9deb5bde1f42215f6ab111264935636e0a7971d8212edd3f235a49df6c2f388aec62a01c44
7
- data.tar.gz: a0713dc5334f151bd05bf9b95e879be1b02ae4cd59ae1e9f8bbdbf90e9474f9ff35286f2b998f66d91f1247cc05ddc07c49f3996fbfc9b7ff672bc97619abbf0
6
+ metadata.gz: 74414b633710710e1d06d2124b7f9101ebee14170eab47e40fa1f7d8733e857f63a19d1d65e08c1c9c8003c2bc317e4437c323f51ccb98d19c953a25cbdd6633
7
+ data.tar.gz: 477753ecfdf1a92b631544c98622f46a686eb3d850fb16f9ff48f811232d51d396c0001de9f05dde62943d4d47d65d3b06ece931dfa256a266a26a6680a9bd8e
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.9
1
+ 0.4.10
@@ -7,7 +7,7 @@ class ChannelTasks
7
7
  @channel = channel
8
8
  end
9
9
 
10
- def add_listener(channel_name)
10
+ def add_listener(channel_name, scope={})
11
11
  # Track every channel that is listening
12
12
  @@listeners[channel_name] ||= []
13
13
  @@listeners[channel_name] << @channel
@@ -18,7 +18,7 @@ class ChannelTasks
18
18
  @@channel_listeners[@channel][channel_name] = true
19
19
  end
20
20
 
21
- def remove_listener(channel_name)
21
+ def remove_listener(channel_name, scope={})
22
22
  if @@listeners[channel_name]
23
23
  @@listeners[channel_name].delete(@channel)
24
24
  if @@channel_listeners[@channel]
data/lib/volt/console.rb CHANGED
@@ -7,7 +7,6 @@ class Console
7
7
 
8
8
  require 'volt/extra_core/extra_core'
9
9
  require 'volt/models'
10
- require 'volt/models/params'
11
10
  require 'volt/server/template_parser'
12
11
  require 'volt'
13
12
  require 'volt/page/page'
@@ -1,6 +1,24 @@
1
1
  class ModelController
2
- def initialize(model=nil)
3
- @model = model
2
+ def initialize
3
+ self.model = @@default_model
4
+ end
5
+
6
+ def self.model(val)
7
+ @@default_model = val
8
+ end
9
+
10
+ # Sets the current model on this controller
11
+ def model=(val)
12
+ if val.is_a?(Symbol) || val.is_a?(String)
13
+ collections = [:page, :store, :params]
14
+ if collections.include?(val.to_sym)
15
+ @model = self.send(val)
16
+ else
17
+ raise "#{val} is not the name of a valid model, choose from: #{collections.join(', ')}"
18
+ end
19
+ else
20
+ @model = model
21
+ end
4
22
  end
5
23
 
6
24
  def page
data/lib/volt/models.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'volt/extra_core/extra_core'
2
2
  require 'volt/reactive/reactive_value'
3
3
  require 'volt/models/model'
4
- require 'volt/models/params'
5
- require 'volt/models/store'
4
+ require 'volt/models/persistors/store_factory'
5
+ require 'volt/models/persistors/array_store'
6
+ require 'volt/models/persistors/model_store'
7
+ require 'volt/models/persistors/params'
6
8
 
@@ -3,15 +3,19 @@ require 'volt/models/model_wrapper'
3
3
  class ArrayModel < ReactiveArray
4
4
  include ModelWrapper
5
5
 
6
- attr_reader :parent, :path
6
+ attr_reader :parent, :path, :persistor, :options
7
7
 
8
- def initialize(array=[], parent=nil, path=nil)
9
- @parent = parent
10
- @path = path || []
8
+ def initialize(array=[], options={})
9
+ @options = options
10
+ @parent = options[:parent]
11
+ @path = options[:path] || []
12
+ @persistor = setup_persistor(options[:persistor])
11
13
 
12
14
  array = wrap_values(array)
13
15
 
14
16
  super(array)
17
+
18
+ @persistor.loaded if @persistor
15
19
  end
16
20
 
17
21
  def attributes
@@ -23,6 +27,8 @@ class ArrayModel < ReactiveArray
23
27
  args = wrap_values(args)
24
28
 
25
29
  super(*args)
30
+
31
+ @persistor.added(args[0]) if @persistor
26
32
  end
27
33
 
28
34
  # Make sure it gets wrapped
@@ -36,7 +42,7 @@ class ArrayModel < ReactiveArray
36
42
  args = wrap_values(args)
37
43
  super(*args)
38
44
  end
39
-
45
+
40
46
  def new_model(*args)
41
47
  Model.new(*args)
42
48
  end
@@ -44,4 +50,12 @@ class ArrayModel < ReactiveArray
44
50
  def new_array_model(*args)
45
51
  ArrayModel.new(*args)
46
52
  end
53
+
54
+ private
55
+ # Takes the persistor if there is one and
56
+ def setup_persistor(persistor)
57
+ if persistor
58
+ @persistor = persistor.new(self)
59
+ end
60
+ end
47
61
  end
@@ -18,7 +18,7 @@ class Model
18
18
  include ObjectTracking
19
19
 
20
20
  attr_accessor :attributes
21
- attr_reader :parent, :path
21
+ attr_reader :parent, :path, :persistor, :options
22
22
 
23
23
  def nil?
24
24
  attributes.nil?
@@ -32,15 +32,27 @@ class Model
32
32
  attributes.true?
33
33
  end
34
34
 
35
- def initialize(attributes={}, parent=nil, path=nil, class_paths=nil)
36
- @parent = parent
37
- @path = path || []
35
+ def initialize(attributes={}, options={})
36
+ @options = options
37
+ @parent = options[:parent]
38
+ @path = options[:path] || []
39
+ @class_paths = options[:class_paths]
40
+ @persistor = setup_persistor(options[:persistor])
41
+
38
42
  self.attributes = wrap_values(attributes)
43
+
44
+ @persistor.loaded if @persistor
39
45
  end
40
46
 
41
47
  # Pass the comparison through
42
48
  def ==(val)
43
- attributes == val
49
+ if val.is_a?(Model)
50
+ # Use normal comparison for a model
51
+ return super
52
+ else
53
+ # Compare to attributes otherwise
54
+ return attributes == val
55
+ end
44
56
  end
45
57
 
46
58
  # Pass through needed
@@ -48,6 +60,17 @@ class Model
48
60
  !attributes
49
61
  end
50
62
 
63
+ # Pass to the persisotr
64
+ def event_added(event, scope_provider, first)
65
+ @persistor.event_added(event, scope_provider, first) if @persistor
66
+ end
67
+
68
+ # Pass to the persistor
69
+ def event_removed(event, no_more_events)
70
+ @persistor.event_removed(event, no_more_events) if @persistor
71
+ end
72
+
73
+
51
74
  tag_method(:delete) do
52
75
  destructive!
53
76
  end
@@ -88,6 +111,9 @@ class Model
88
111
 
89
112
  attributes[attribute_name] = wrap_value(value, [attribute_name])
90
113
  trigger_by_attribute!('changed', attribute_name)
114
+
115
+ # Let the persistor know something changed
116
+ @persistor.changed(attribute_name) if @persistor
91
117
  end
92
118
 
93
119
  # When reading an attribute, we need to handle reading on:
@@ -112,7 +138,11 @@ class Model
112
138
 
113
139
  # Get a new model, make it easy to override
114
140
  def read_new_model(method_name)
115
- return new_model(nil, self, path + [method_name])
141
+ if @persistor && @persistor.respond_to?(:read_new_model)
142
+ @persistor.read_new_model(method_name)
143
+ else
144
+ return new_model(nil, @options.merge(parent: self, path: path + [method_name]))
145
+ end
116
146
  end
117
147
 
118
148
  def return_undefined_method(method_name)
@@ -168,7 +198,11 @@ class Model
168
198
  end
169
199
  # Initialize an empty array and append to it
170
200
  def <<(value)
171
- @parent.expand!
201
+ if @parent
202
+ @parent.expand!
203
+ else
204
+ raise "Model data should be stored in sub collections."
205
+ end
172
206
 
173
207
  # Grab the last section of the path, so we can do the assign on the parent
174
208
  path = @path.last
@@ -176,7 +210,7 @@ class Model
176
210
 
177
211
  if result.nil?
178
212
  # If this isn't a model yet, instantiate it
179
- @parent.send(:"#{path}=", new_array_model([], @parent, @path))
213
+ @parent.send(:"#{path}=", new_array_model([], @options))
180
214
  result = @parent.send(path)
181
215
  end
182
216
 
@@ -187,7 +221,7 @@ class Model
187
221
  end
188
222
 
189
223
  def inspect
190
- "<#{self.class.to_s} #{attributes.inspect}>"
224
+ "<#{self.class.to_s}:#{object_id} #{attributes.inspect}>"
191
225
  end
192
226
 
193
227
 
@@ -214,4 +248,12 @@ class Model
214
248
  trigger_by_attribute!(event, key, *args)
215
249
  end
216
250
  end
251
+
252
+ # Takes the persistor if there is one and
253
+ def setup_persistor(persistor)
254
+ if persistor
255
+ @persistor = persistor.new(self)
256
+ end
257
+ end
258
+
217
259
  end
@@ -3,9 +3,9 @@ module ModelWrapper
3
3
  # model.
4
4
  def wrap_value(value, lookup)
5
5
  if value.cur.is_a?(Array)
6
- value = new_array_model(value, self, path + lookup)
6
+ value = new_array_model(value, @options.merge(parent: self, path: path + lookup))
7
7
  elsif value.cur.is_a?(Hash)
8
- value = new_model(value, self, path + lookup)
8
+ value = new_model(value, @options.merge(parent: self, path: path + lookup))
9
9
  end
10
10
 
11
11
  return value
@@ -0,0 +1,79 @@
1
+ require 'volt/models/persistors/store'
2
+
3
+ module Persistors
4
+ class ArrayStore < Store
5
+
6
+ # Called when a collection loads
7
+ def loaded
8
+ scope = {}
9
+
10
+
11
+
12
+ # Scope to the parent
13
+ if @model.path.size > 1
14
+ parent = @model.parent
15
+
16
+ parent.persistor.ensure_setup if parent.persistor
17
+ puts @model.parent.inspect
18
+
19
+ if parent && (attrs = parent.attributes) && attrs[:_id].true?
20
+ scope[:"#{@model.path[-3].singularize}_id"] = attrs[:_id]
21
+ end
22
+ end
23
+
24
+ puts "Load At Scope: #{scope.inspect}"
25
+
26
+ query(scope)
27
+
28
+ change_channel_connection('add', 'added')
29
+ change_channel_connection('add', 'removed')
30
+ end
31
+
32
+ def query(query)
33
+ @tasks.call('StoreTasks', 'find', @model.path.last, query) do |results|
34
+ # TODO: Globals evil, replace
35
+ $loading_models = true
36
+
37
+ new_options = @model.options.merge(path: @model.path + [:[]], parent: @model)
38
+
39
+ results.each do |result|
40
+ @model << Model.new(result, new_options)
41
+ end
42
+ $loading_models = false
43
+ end
44
+ end
45
+
46
+ def channel_name
47
+ @model.path[-1]
48
+ end
49
+
50
+
51
+ # When a model is added to this collection, we call its "changed"
52
+ # method. This should trigger a save.
53
+ def added(model)
54
+ unless $loading_models
55
+ model.persistor.changed
56
+ end
57
+
58
+ if model.persistor
59
+ # Tell the persistor it was added
60
+ model.persistor.add_to_collection
61
+ end
62
+ end
63
+
64
+ def removed(model)
65
+ if model.persistor
66
+ # Tell the persistor it was removed
67
+ model.persistor.remove_from_collection
68
+ end
69
+
70
+ if $loading_models
71
+ return
72
+ else
73
+ puts "delete #{channel_name} - #{model.attributes[:_id]}"
74
+ @tasks.call('StoreTasks', 'delete', channel_name, model.attributes[:_id])
75
+ end
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,24 @@
1
+ module Persistors
2
+ # Implements the base persistor functionality.
3
+ class Base
4
+ def loaded
5
+ end
6
+
7
+ def changed(attribute_name)
8
+ end
9
+
10
+ def added(model)
11
+ end
12
+
13
+ # For removed, the default action is to call changed for it
14
+ def removed(attribute_name)
15
+ changed(attribute_name)
16
+ end
17
+
18
+ def event_added(event, scope_provider, first)
19
+ end
20
+
21
+ def event_removed(event, no_more_events)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,140 @@
1
+ require 'volt/models/persistors/store'
2
+
3
+ module Persistors
4
+ class ModelStore < Store
5
+ ID_CHARS = [('a'..'z'), ('A'..'Z'), ('0'..'9')].map {|v| v.to_a }.flatten
6
+
7
+ @@identity_map = {}
8
+
9
+ attr_reader :model
10
+
11
+ def add_to_collection
12
+ @in_collection = true
13
+ ensure_setup
14
+ changed
15
+ end
16
+
17
+ def remove_from_collection
18
+ @in_collection = false
19
+ stop_listening_for_changes
20
+ end
21
+
22
+ # Called the first time a value is assigned into this model
23
+ def ensure_setup
24
+ if @model.attributes
25
+ @model.attributes[:_id] ||= generate_id
26
+
27
+ if !model_in_identity_map?
28
+ @@identity_map[@model.attributes[:_id]] ||= self
29
+ end
30
+
31
+ # Check to see if we already have listeners setup
32
+ if @model.listeners[:changed]
33
+ listen_for_changes
34
+ end
35
+ end
36
+ end
37
+
38
+ def model_in_identity_map?
39
+ @@identity_map[@model.attributes[:_id]]
40
+ end
41
+
42
+ # Create a random unique id that can be used as the mongo id as well
43
+ def generate_id
44
+ id = []
45
+ 12.times { id << ID_CHARS.sample }
46
+
47
+ return id.join
48
+ end
49
+
50
+ # Called when the model changes
51
+ def changed(attribute_name=nil)
52
+ # puts "CHANGED: #{attribute_name.inspect} - #{@model.inspect}"
53
+ ensure_setup
54
+
55
+ path_size = @model.path.size
56
+ if !(defined?($loading_models) && $loading_models) && @tasks && path_size > 0 && !@model.nil?
57
+ if path_size > 3 && (parent = @model.parent) && source = parent.parent
58
+ @model.attributes[:"#{@model.path[-4].singularize}_id"] = source._id
59
+ end
60
+
61
+ puts "Save: #{collection} - #{self_attributes.inspect} - #{@model.path.inspect}"
62
+ @tasks.call('StoreTasks', 'save', collection, self_attributes)
63
+ end
64
+ end
65
+
66
+ def listen_for_changes
67
+ unless @change_listening
68
+ if @in_collection
69
+ @change_listening = true
70
+ change_channel_connection("add")
71
+ end
72
+ end
73
+ end
74
+
75
+ def stop_listening_for_changes
76
+ if @change_listening
77
+ @change_listening = false
78
+ change_channel_connection("remove")
79
+ end
80
+ end
81
+
82
+ def event_added(event, scope_provider, first)
83
+ if first && event == :changed
84
+ # Start listening
85
+ ensure_setup
86
+ listen_for_changes
87
+ end
88
+ end
89
+
90
+ def event_removed(event, no_more_events)
91
+ if no_more_events && event == :changed
92
+ # Stop listening
93
+ stop_listening_for_changes
94
+ end
95
+ end
96
+
97
+ def channel_name
98
+ @channel_name ||= "#{@model.path[-2]}##{@model.attributes[:_id]}"
99
+ end
100
+
101
+ # Finds the model in its parent collection and deletes it.
102
+ def delete!
103
+ if @model.path.size == 0
104
+ raise "Not in a collection"
105
+ end
106
+
107
+ @model.parent.delete(@model)
108
+ end
109
+
110
+ # Update the models based on the id/identity map. Usually these requests
111
+ # will come from the backend.
112
+ def self.update(model_id, data)
113
+ persistor = @@identity_map[model_id]
114
+
115
+ if persistor
116
+ data.each_pair do |key, value|
117
+ if key != '_id'
118
+ persistor.model.send(:"#{key}=", value)
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ def self.from_id(id)
125
+ @@identity_map[id]
126
+ end
127
+
128
+ private
129
+ # Return the attributes that are only for this store, not any sub-associations.
130
+ def self_attributes
131
+ # Don't store any sub-stores, those will do their own saving.
132
+ @model.attributes.reject {|k,v| v.is_a?(Model) || v.is_a?(ArrayModel) }
133
+ end
134
+
135
+ def collection
136
+ @model.path[-2]
137
+ end
138
+
139
+ end
140
+ end