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