lib/volt/repo_cache/collection.rb in volt-repo_cache-0.1.6 vs lib/volt/repo_cache/collection.rb in volt-repo_cache-0.1.7

- old
+ new

@@ -11,31 +11,28 @@ attr_reader :name attr_reader :model_class_name attr_reader :model_class attr_reader :repo_collection attr_reader :load_query - attr_reader :loaded_ids + attr_reader :cached_ids attr_reader :loaded # Promise attr_reader :marked_for_destruction attr_reader :associations attr_reader :read_only def initialize(cache: nil, name: nil, options: {}) - # debug __method__, __LINE__, "name: #{name} options: #{options}" super(observer: self) - # debug __method__, __LINE__ @cache = cache @name = name @load_query = options[:query] || options[:where] @read_only = options[:read_only].nil? ? true : options[:read_only] @marked_for_destruction = {} @model_class_name = @name.to_s.singularize.camelize @model_class = Object.const_get(@model_class_name) @repo_collection = @cache.repo.send(name) init_associations(options) load - # debug __method__, __LINE__ end # hide circular reference to cache def inspect __tmp = @cache @@ -68,20 +65,20 @@ # Returns the new model def create(hash = {}) append(hash.to_h) end - # Appends a model to the collection. + # Appends a new model to the collection. # Model may be a hash which will be converted. # (See #induct for more.) # If the model belongs_to any other owners, the foreign id(s) # MUST be already set to ensure associational integrity # in the cache - it is easier to ask the owner for a new # instance (e.g. product.recipe.new_ingredient). # NB: Returns the newly appended model. - def append(model, error_if_present: true, error_unless_new: true, notify: true) - model = induct(model, error_unless_new: error_unless_new, error_if_present: error_if_present) + def append(model, notify: true) + model = induct(model, loaded_from_repo: false) __append__(model, notify: notify) model end # Returns self after appending the given model @@ -121,55 +118,46 @@ # Called by RepoCache::Model#__destroy__. # Remove model from marked_for_destruction bucket. # Don't worry if we can't find it. def destroyed(model) - @loaded_ids.delete(model.id) + @cached_ids.delete(model.id) @marked_for_destruction.delete(model.id) end # Collection is being notified (probably by super/self) # that a model has been added or removed. Pass # this on to associations. def observe(action, model) - # debug __method__, __LINE__, "action=#{action} model=#{model} associations=#{associations}" # notify owner model(s) of appended model that it has been added notify_associations(action, model) end def notify_associations(action, model) - # debug __method__, __LINE__, "action=#{action} model=#{model} associations=#{associations}" associations.each_value do |assoc| - # debug __method__, __LINE__, "calling notify_associates(#{assoc}, #{action}, #{model})" notify_associates(assoc, action, model) - # debug __method__, __LINE__, "called notify_associates(#{assoc}, #{action}, #{model})" end end # Notify models in the given association that # the given model has been deleted from or # appended to this collection. For example, # this collection may be for orders, and # association may be owner customer - thus # association will be belongs_to def notify_associates(assoc, action, model) - # debug __method__, __LINE__, "action=#{action} model=#{model} assoc=#{assoc} reciprocate=#{assoc.reciprocal}" if assoc.reciprocal local_id = model.send(assoc.local_id_field) - # debug __method__, __LINE__, "local_id #{assoc.local_id_field}=#{local_id}" if local_id # may not be set yet assoc.foreign_collection.each do |other| - # debug __method__, __LINE__, "calling #{assoc.foreign_id_field} on #{other}" foreign_id = other.send(assoc.foreign_id_field) if local_id == foreign_id - # debug __method__, __LINE__, "foreign_id==local_id of #{other}, calling other.refresh_association(#{assoc.foreign_name})" other.send(:refresh_association, assoc.reciprocal) end end end end - # debug __method__, __LINE__ end # 'Induct' a model into the cache via this collection. # # Called by #append. @@ -191,69 +179,67 @@ # - if it should belong to (an)other model(s), then we require that # the foreign id(s) are already set, otherwise we cannot ensure # associational integrity in the cache. # # Returns the inducted model. - def induct(model_or_hash, error_unless_new: true, error_if_present: true) - created_in_cache = false - if model_or_hash.is_a?(Hash) - created_in_cache = true - model = model_class.new(model_or_hash, options: {persistor: cache.persistor}) + def induct(model_or_hash, loaded_from_repo: false) + model = if Hash === model_or_hash + if loaded_from_repo + raise TypeError, "cannot induct stored model from a hash #{model_or_hash}" + end + model_class.new(model_or_hash, options: {persistor: cache.persistor}) else - model = model_or_hash - # unless model.persistor.class == cache.persistor.class - # raise RuntimeError, "model persistor is #{model.persistor} but should be #{cache.persistor}" - # end - unless model.class == model_class - raise ArgumentError, "#{model} must be a #{model_class_name}" + if !loaded_from_repo && model_or_hash.buffer? + raise TypeError, "cannot induct new model_or_hash #{model_or_hash} with buffer" end - if error_unless_new && (!model.created_in_cache? || model.new?) - raise ArgumentError, "#{model} must be new (not stored) or have been created in cache" - end - if error_if_present && @loaded_ids.include?(model.id) - raise RuntimeError, "cannot add #{model} already in cached collection" - end + model_or_hash end - @loaded_ids << model.id - patch_for_cache(model, created_in_cache) + unless model.class == model_class + raise ArgumentError, "#{model} must be a #{model_class_name}" + end + if model.cached? + __debug __method__, __LINE__, "id=#{model.id} model.cached?=#{model.cached?}" + raise TypeError, "model.id #{model.id} already in cache" + end + @cached_ids << model.id + RepoCache::Model.induct_to_cache(model, self, loaded_from_repo) model end + def cached?(model) + @cached_ids.include?(model.id) + end + def load - # debug __method__, __LINE__ - @loaded_ids = Set.new # append/delete will update + @cached_ids = Set.new # append/delete will update q = @load_query ? repo_collection.where(@load_query) : repo_collection - # t1 = Time.now @loaded = q.all.collect{|e|e}.then do |models| - # t2 = Time.now - # debug __method__, __LINE__, "#{name} read_only=#{read_only} query promise resolved to #{models.size} models in #{t2-t1} seconds" - models.each do |model| - append(read_only ? model : model.buffer, error_unless_new: false, notify: false) + models.each do |_model| + model = read_only ? _model : _model.buffer + induct(model, loaded_from_repo: true) + __append__(model, notify: false) end - # t3 = Time.now - # debug __method__, __LINE__, "#{name} loaded ids for #{models.size} #{name} in #{t3-t2} seconds" self end - # debug __method__, __LINE__, "@loaded => #{@loaded.class.name}:#{@loaded.value.class.name}" end def init_associations(options) - # debug __method__, __LINE__, "options = #{options}" @associations = {} [:belongs_to, :has_one, :has_many].each do |type| arrify(options[type]).map(&:to_sym).each do |foreign_name| @associations[foreign_name] = Association.new(self, foreign_name, type) - # debug __method__, __LINE__, "@associations[#{foreign_name}] = #{@associations[foreign_name].inspect}" end end end - def patch_for_cache(model, created_in_cache) - unless model.respond_to?(:patched_for_cache?) - RepoCache::Model.patch_for_cache(model, self, created_in_cache) - end - model - end + def __debug(method, line, msg = nil) + s = "{__FILE__}[#{line}]:#{self.class.name}##{method}: #{msg}" + if RUBY_PLATFORM == 'opal' + Volt.logger.debug s + else + puts s + end + end end end end