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