volt 0.4.9 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
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