lib/volt/models/store.rb in volt-0.4.0 vs lib/volt/models/store.rb in volt-0.4.1
- old
+ new
@@ -3,54 +3,51 @@
class Store < Model
ID_CHARS = [('a'..'z'), ('A'..'Z'), ('0'..'9')].map {|v| v.to_a }.flatten
@@identity_map = {}
+ attr_reader :state
+
def initialize(tasks=nil, *args)
@tasks = tasks
+ @state = :not_loaded
super(*args)
- track_in_identity_map if attributes && attributes['_id']
+ track_in_identity_map if attributes && attributes[:_id]
value_updated
end
-
- # def _id
- # return attributes && attributes['_id']
- # end
def event_added(event, scope_provider, first)
if first && event == :changed
# Start listening
ensure_id
- if self.attributes && self.path.size > 1
- channel_name = "#{self.path[-2]}##{self.attributes['_id']}"
- puts "LISTENER ON: #{channel_name.inspect} -- #{self.path.inspect}"
- $page.tasks.call('ChannelTasks', 'add_listener', channel_name)
- end
+ change_channel_connection("add")
end
end
def event_removed(event, no_more_events)
if no_more_events && event == :changed
# Stop listening
- if self.attributes && self.path.size > 1
- channel_name = "#{self.path[-2]}##{self.attributes['_id']}"
- puts "REMOVE LISTENER ON: #{channel_name}"
- $page.tasks.call('ChannelTasks', 'remove_listener', channel_name)
- end
+ change_channel_connection("remove")
end
end
+ def change_channel_connection(add_or_remove)
+ if attributes && path.size > 1
+ channel_name = "#{path[-2]}##{attributes[:_id]}"
+ $page.tasks.call('ChannelTasks', "#{add_or_remove}_listener", channel_name)
+ end
+ end
+
def self.update(model_id, data)
model = @@identity_map[model_id]
if model
data.each_pair do |key, value|
if key != '_id'
- puts "update #{key} with #{value.inspect}"
model.send(:"#{key}=", value)
end
end
end
end
@@ -61,10 +58,15 @@
return id.join
end
def method_missing(method_name, *args, &block)
+ if method_name[-1] == ']'
+ # Load the model
+ self.load!
+ end
+
result = super
if method_name[0] == '_' && method_name[-1] == '='
# Trigger value updated after an assignment
self.value_updated
@@ -72,40 +74,43 @@
return result
end
def track_in_identity_map
- @@identity_map[attributes['_id']] = self
+ @@identity_map[attributes[:_id]] = self
end
# When called, will setup an id if there is not one
def ensure_id
# No id yet, lets create one
- if attributes && !attributes['_id']
- self.attributes['_id'] = generate_id
+ if attributes && !attributes[:_id]
+ self.attributes[:_id] = generate_id
track_in_identity_map
end
end
def value_updated
- # puts "VU: #{@tasks.inspect} = #{path.inspect} - #{attributes.inspect}"
- if (!defined?($loading_models) || !$loading_models) && @tasks && path.size > 0 && !self.nil?
+ path_size = path.size
+ if !(defined?($loading_models) && $loading_models) && @tasks && path_size > 0 && !nil?
ensure_id
- if path.size > 2 && parent && source = parent.parent
- self.attributes[path[-2].to_s.singularize+'_id'] = source._id
+ if path_size > 3 && parent && source = parent.parent
+ self.attributes[:"#{path[-4].singularize}_id"] = source._id
end
- # Don't store any sub-stores, those will do their own saving.
- attrs = attributes.reject {|k,v| v.is_a?(Model) || v.is_a?(ArrayModel) }
-
- puts "Save: #{collection} - #{attrs.inspect}"
- @tasks.call('StoreTasks', 'save', collection, attrs)
+ # puts "Save: #{collection} - #{attrs.inspect}"
+ @tasks.call('StoreTasks', 'save', collection, self_attributes)
end
end
+ # Return the attributes that are only for this store, not any sub-associations.
+ def self_attributes
+ # Don't store any sub-stores, those will do their own saving.
+ attrs = attributes.reject {|k,v| v.is_a?(Model) || v.is_a?(ArrayModel) }
+ end
+
def collection(path=nil)
path ||= self.path
collection_name = path.last
collection_name = path[-2] if collection_name == :[]
@@ -119,44 +124,51 @@
model = new_model(nil, self, path + [method_name])
self.attributes ||= {}
attributes[method_name] = model
+ if model.state == :not_loaded
+ model.load!
+ end
+
return model
end
-
-
- def new_model(attributes={}, parent=nil, path=nil, class_paths=nil)
- model = Store.new(@tasks, attributes, parent, path, class_paths)
-
- if @tasks && path.last[-1] == 's'
- # puts "FIND NEW MODEL: #{path.inspect} - #{attributes.inspect}"
+ def load!
+ if @state == :not_loaded
+ @state = :loading
+
+ if @tasks && path.last.plural?
+ # Check to see the parents scope so we can only lookup associated
+ # models.
+ scope = {}
- # Check to see the parents scope so we can only lookup associated
- # models.
- scope = {}
-
- if parent.attributes && parent.attributes['_id'].true?
- scope[path[-1].singularize + '_id'] = parent._id
- end
-
- puts "FIND: #{collection(path).inspect} at #{scope.inspect}"
- @tasks.call('StoreTasks', 'find', collection(path), scope) do |results|
- # TODO: Globals evil, replace
- $loading_models = true
- results.each do |result|
- # Get model again, we need to fetch it each time so it gets the
- # updated model when it switches from nil.
- # TODO: Strange that this is needed
- model = self.send(path.last)
- model << Store.new(@tasks, result, model, path + [:[]], class_paths)
+ # Scope to the parent
+ if path.size > 2 && (attrs = parent.attributes) && attrs[:_id].true?
+ scope[:"#{path[-3].singularize}_id"] = parent._id
end
- $loading_models = false
+
+ load_child_models(scope)
end
end
- return model
+ return self
+ end
+
+ def load_child_models(scope)
+ # puts "FIND: #{collection(path).inspect} at #{scope.inspect}"
+ @tasks.call('StoreTasks', 'find', collection(path), scope) do |results|
+ # TODO: Globals evil, replace
+ $loading_models = true
+ results.each do |result|
+ self << Store.new(@tasks, result, self, path + [:[]], @class_paths)
+ end
+ $loading_models = false
+ end
+ end
+
+ def new_model(attributes={}, parent=nil, path=nil, class_paths=nil)
+ return Store.new(@tasks, attributes, parent, path, class_paths)
end
def new_array_model(*args)
StoreArray.new(@tasks, *args)
end
\ No newline at end of file