Class: Lims::Core::Persistence::Persistor Abstract
- Defined in:
- lib/lims-core/persistence/persistor.rb
Overview
Base class for all the persistors, needs to implements a ‘self.model`
returning the class to persist. A persistor , is used to save and load it’s cousin class. The specific code of a persistor should be extended by writting a persistor class within the class to persist and module corresponding to the store. The common Persistor architecture would be like this (let’s consider we have a Plate class and a Sequel Persistor). module SequelPersistor end Class Plate
Common to all store
class PlatePersistor < Persistence::Persistor
end
class PlateSequelPersistor < PlatePersistor
include SequelPersistor
end
end if a base persistor exists for a class but not the store specific one (PlatePersistor exists but PlateSequelPersistor not). If there is a store pecific Persistor module (like SequelPersistor). The equivalent of PlateSequelPersistor will be generated on the fly by deriving the base one and including the mixin. Persistor needs to be registered to be accessible form the session. However, if NO_AUTO_REGISTRATION is not enabled persistors will register themselves. In that case, they will need to be defined in class to persist see Persistor.register_model. If a base peristor for exists for a class but there is no Each instance can get an identity map, and or parameter specific to a session/thread.
-
Methods relative to store are
-
insert : a new object to the store
-
delete : remove an object fromt the store
-
update : modify an existing object from the store.
-
retrieve : get an object from the store.
-
bulk_<method> vs <method> refers to method acting on a list of states
instead of an individual object. Althoug only one version needs to be implemted , the bulk version is prefered for performance reason.
-
raw_<method_ refers when exists to the physical action done to the store
without any side effect on the Session or Persistor. They should not normally be called.
-
Methods relative to parents/children
-
parents : resources needed to be saved BEFORE the resource itself.
-
children : resources needed to be save AFTER the resource itself.
-
deletable_children : resources which needs to be deleted BEFORE the resource itself.
-
deletable_parent : resources which needs to be deleted AFTER the resource itself.
Direct Known Subclasses
Array::ArrayLoggerPersistor, ForTest::Name::NamePersitor, Search::SearchPersistor, UuidResource::UuidResourcePersistor, SessionSpec::Model::ModelPersistor
Defined Under Namespace
Classes: DuplicateError, DuplicateIdError, DuplicateObjectError
Class Method Summary collapse
-
.inherited(subclass) ⇒ Object
Performs an autoregistration if needed.
-
.register_model(subclass) ⇒ Object
Register a sub-persistor to the Session.
Instance Method Summary collapse
-
#[](id, single = true) ⇒ Object?
Load a model by different criteria.
-
#bind_state_to_id(state) ⇒ Object
Updates the cache so id_to_state reflects state.id.
-
#bind_state_to_resource(state) ⇒ Object
Update the cache.
-
#bulk_delete(states, *params) ⇒ Object
Remove object form the underlying store and Manages them.
-
#bulk_delete_raw(states, *params) ⇒ Object
abstract
Physically remove objects from a store.
-
#bulk_insert(states, *params) ⇒ Object
Inserts objects in the underlying store AND manages them.
-
#bulk_retrieve(ids, *params) ⇒ Array<Object]
Retreives a list of objects .
-
#bulk_update(states, *params) ⇒ Object
Updates the store and manages object.
-
#children(resource) ⇒ Array<Resource>
List of children , i.e, object which need to be saved AFTER it.
-
#count ⇒ Fixnum
abstract
Returns the number of object in the store.
- #deletable_children(resource) ⇒ Object
- #deletable_parents(resource) ⇒ Object
-
#dirty_key_for(resource) ⇒ Object
Computes “dirty_key” of an object.
-
#for_each_in_slice(start, length) {|key, attributes| ... } ⇒ Object
abstract
Load a slice.
-
#id_for(object) ⇒ Id, Nil
Get the id from an object from the cache.
-
#ids_for(criteria) ⇒ Array<Id>
compute a list of ids matching the criteria.
-
#initialize(session, *args, &block) ⇒ Persistor
constructor
A new instance of Persistor.
-
#invalid_resource?(resource) ⇒ Boolean
if a resource is invalid and need to be deleted.
- #load_children(states, *params) ⇒ Object
-
#model ⇒ Class
Associate class (without persistence).
- #new_from_attributes(attributes) ⇒ Object
-
#new_object(id, attributes) ⇒ Resource
Creates a new object from a Hash and associate it to its id.
-
#object_for(id) ⇒ Resourec, Nil
Get the object from a given id.
-
#on_object_load(state) ⇒ Object
Called by Persistor to inform the session about the loading of an object.
-
#parents(resource) ⇒ Array<Resource>
List of parents of object, i.e.
- #parents_for_attributes(attributes) ⇒ Object
-
#purge_invalid_object ⇒ Object
Delete all invalid object loaded by a persistor.
-
#retrieve(id, *params) ⇒ Object?
Retrieves an object from it’s id.
-
#slice(start, length) ⇒ Enumerable<Hash>
Get a slice of object by offset, length.
-
#state_for(object) ⇒ ResourceState
Returns the state proxy of an object.
- #state_for?(object) ⇒ Boolean
-
#state_for_id(id) ⇒ ResourceState
Returns the state proxy of an object fromt its id (in cache).
Constructor Details
#initialize(session, *args, &block) ⇒ Persistor
Returns a new instance of Persistor.
92 93 94 95 96 97 |
# File 'lib/lims-core/persistence/persistor.rb', line 92 def initialize (session, *args, &block) @session = session @id_to_state = Hash.new { |h,k| h[k] = ResourceState.new(nil, self, k) } @object_to_state = Hash.new { |h,k| h[k] = ResourceState.new(k, self) } super(*args, &block) end |
Class Method Details
.inherited(subclass) ⇒ Object
Performs an autoregistration if needed. Autoregistration can be skipped by defined NO_AUTO_REGISTRATION on the model class. See Persistor::register_model.
69 70 71 |
# File 'lib/lims-core/persistence/persistor.rb', line 69 def self.inherited(subclass) register_model(subclass) end |
.register_model(subclass) ⇒ Object
Register a sub-persistor to the Session. The name used to register the persistor would be either the name of the model (parent) class or if SESSION_NAME is specified on the model : SESSION_NAME
78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/lims-core/persistence/persistor.rb', line 78 def self.register_model(subclass) model = subclass.parent_scope return if model::const_defined? :NO_AUTO_REGISTRATION name =\ if model::const_defined? :SESSION_NAME model::SESSION_NAME else name = model.name.split('::').pop end Session::register_model(name, model) end |
Instance Method Details
#[](id, single = true) ⇒ Object?
Load a model by different criteria. Could be either :
-
an Id
-
a Hash
-
a list of Ids
This method will return either a single object or a list of object, depending of the parameter. Note that loaded object are automatically added to the session.
115 116 117 118 119 120 121 |
# File 'lib/lims-core/persistence/persistor.rb', line 115 def [](id, single=true) case id when Fixnum then retrieve(id) when Hash then find_by(filter_attributes_on_save(id), single) when Array, Enumerable then bulk_retrieve(id) end end |
#bind_state_to_id(state) ⇒ Object
Updates the cache so id_to_state reflects state.id
162 163 164 165 166 167 |
# File 'lib/lims-core/persistence/persistor.rb', line 162 def bind_state_to_id(state) raise RuntimeError, 'Invalid state' if state.persistor != self raise DuplicateIdError.new(self, state.id)if @id_to_state.include?(state.id) on_object_load(state) @id_to_state[state.id] = state end |
#bind_state_to_resource(state) ⇒ Object
Update the cache
178 179 180 181 182 |
# File 'lib/lims-core/persistence/persistor.rb', line 178 def bind_state_to_resource(state) raise RuntimeError, 'Invalobject state' if state.persistor != self raise DuplicateIdError.new(self, state.resource) if @object_to_state.include?(state.resource) @object_to_state[state.resource] = state end |
#bulk_delete(states, *params) ⇒ Object
Remove object form the underlying store and Manages them. This method only care about the objects themselves not about theirs parents or children.
285 286 287 288 289 290 291 292 293 |
# File 'lib/lims-core/persistence/persistor.rb', line 285 def bulk_delete(states, *params) # delete theme but leave them in cache # in case they need to be displayed. states.each do |state| state.id.andtap { |id| @id_to_state.delete(id) } state.resource #.andtap { |object| @object_to_state.delete(object) } end bulk_delete_raw(states.map(&:id).compact, *params) end |
#bulk_delete_raw(states, *params) ⇒ Object
Physically remove objects from a store.
297 298 299 |
# File 'lib/lims-core/persistence/persistor.rb', line 297 def bulk_delete_raw(states, *params) raise NotImplementedError end |
#bulk_insert(states, *params) ⇒ Object
Inserts objects in the underlying store AND manages them. This method only care about the objects themselves not about theirs parents or children. The physical insert in the store must be specified for each store.
278 279 280 |
# File 'lib/lims-core/persistence/persistor.rb', line 278 def bulk_insert(states, *params) states.map { |state| insert(state, *params) } end |
#bulk_retrieve(ids, *params) ⇒ Array<Object]
Retreives a list of objects . @param ids
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/lims-core/persistence/persistor.rb', line 330 def bulk_retrieve(ids, *params) # create a list of states and load them states = StateGroup.new(self, ids.map do |id| @id_to_state[id] end) states.load return StateList.new(states.map { |state| state.resource }) # we need to separate object which need to be loaded # from the one which are already in cache to_load = ids.reject { |id| id == nil || @id_to_state.include?(id) } loaded_states = bulk_load_raw_attributes(to_load, *params) do |att| id = att.delete(primary_key) new_state_for_attribute(id, att).resource end bulk_retrieve_children(new_states, *params) #bulk_retrieve_parent(new_states, *params) ids.map { |id| object_for(id) } end |
#bulk_update(states, *params) ⇒ Object
Updates the store and manages object. Doesn’t care of children or parents.
357 358 359 360 361 362 363 364 365 |
# File 'lib/lims-core/persistence/persistor.rb', line 357 def bulk_update(states, *params) attributes = states.map do |state| filter_attributes_on_save(state.resource.attributes).merge(primary_key => state.id) end bulk_update_raw_attributes(attributes, *params) states.each do |state| state.updated end end |
#children(resource) ⇒ Array<Resource>
List of children , i.e, object which need to be saved AFTER it.
387 388 389 |
# File 'lib/lims-core/persistence/persistor.rb', line 387 def children(resource) [] end |
#count ⇒ Fixnum
Returns the number of object in the store
246 247 248 |
# File 'lib/lims-core/persistence/persistor.rb', line 246 def count raise NotImplementedError end |
#deletable_children(resource) ⇒ Object
392 393 394 |
# File 'lib/lims-core/persistence/persistor.rb', line 392 def deletable_children(resource) [] end |
#deletable_parents(resource) ⇒ Object
396 397 398 |
# File 'lib/lims-core/persistence/persistor.rb', line 396 def deletable_parents(resource) [] end |
#dirty_key_for(resource) ⇒ Object
Computes “dirty_key” of an object. The dirty key is used to decide if an object has been modified or not.
201 202 203 204 205 |
# File 'lib/lims-core/persistence/persistor.rb', line 201 def dirty_key_for(resource) if resource && @session.dirty_attribute_strategy @session.dirty_key_for(filter_attributes_on_save(resource.attributes_for_dirty)) end end |
#for_each_in_slice(start, length) {|key, attributes| ... } ⇒ Object
Load a slice. Doesn’t return an object but a hash allowing to build it.
257 258 259 |
# File 'lib/lims-core/persistence/persistor.rb', line 257 def for_each_in_slice(start, length) raise NotImplementedError end |
#id_for(object) ⇒ Id, Nil
Get the id from an object from the cache.
126 127 128 |
# File 'lib/lims-core/persistence/persistor.rb', line 126 def id_for(object) state_for(object).andtap { |state| state.id } end |
#ids_for(criteria) ⇒ Array<Id>
compute a list of ids matching the criteria
239 240 241 |
# File 'lib/lims-core/persistence/persistor.rb', line 239 def ids_for(criteria) raise NotImplementedError end |
#invalid_resource?(resource) ⇒ Boolean
if a resource is invalid and need to be deleted. For example an association proxy corresponding to an old relation.
403 404 405 |
# File 'lib/lims-core/persistence/persistor.rb', line 403 def invalid_resource?(resource) resource.respond_to?(:invalid?) && resource.invalid? end |
#load_children(states, *params) ⇒ Object
436 437 438 |
# File 'lib/lims-core/persistence/persistor.rb', line 436 def load_children(states, *params) [] end |
#model ⇒ Class
Associate class (without persistence).
101 102 103 |
# File 'lib/lims-core/persistence/persistor.rb', line 101 def model self.class::Model end |
#new_from_attributes(attributes) ⇒ Object
441 442 443 444 445 |
# File 'lib/lims-core/persistence/persistor.rb', line 441 def new_from_attributes(attributes) id = attributes.delete(primary_key) resource = block_given? ? yield(attributes) : model.new(filter_attributes_on_load(attributes)) state_for_id(id).tap { |state| state.resource = resource } end |
#new_object(id, attributes) ⇒ Resource
Creates a new object from a Hash and associate it to its id
188 189 190 191 192 193 194 |
# File 'lib/lims-core/persistence/persistor.rb', line 188 def new_object(id, attributes) id = attributes.delete(primary_key) model.new(filter_attributes_on_load(attributes)).tap do |resource| state = state_for_id(id) state.resource = resource end end |
#object_for(id) ⇒ Resourec, Nil
Get the object from a given id.
133 134 135 |
# File 'lib/lims-core/persistence/persistor.rb', line 133 def object_for(id) @id_to_state[id].andtap(&:resource) end |
#on_object_load(state) ⇒ Object
Called by Persistor to inform the session about the loading of an object. MUST be called by persistors creating Resources.
173 174 175 |
# File 'lib/lims-core/persistence/persistor.rb', line 173 def on_object_load(state) @session.manage_state(state) end |
#parents(resource) ⇒ Array<Resource>
List of parents of object, i.e. object which need to be saved BEFORE it. Default implementation get all Resource attributes.
380 381 382 |
# File 'lib/lims-core/persistence/persistor.rb', line 380 def parents(resource) resource.attributes.values.select { |v| v.is_a? Resource } end |
#parents_for_attributes(attributes) ⇒ Object
431 432 433 |
# File 'lib/lims-core/persistence/persistor.rb', line 431 def parents_for_attributes(attributes) [] end |
#purge_invalid_object ⇒ Object
Delete all invalid object loaded by a persistor. Typically invalid object are association which doesn’t exist anymore
209 210 211 212 213 214 215 216 |
# File 'lib/lims-core/persistence/persistor.rb', line 209 def purge_invalid_object to_delete = StateGroup.new(self, []) @object_to_state.each do |object, state| to_delete << state if invalid_resource?(object) end to_delete.destroy end |
#retrieve(id, *params) ⇒ Object?
Retrieves an object from it’s id. Doesn’t load it if it’s been alreday loaded.
320 321 322 323 324 325 |
# File 'lib/lims-core/persistence/persistor.rb', line 320 def retrieve(id, *params) object_for(id).andtap { |o| return o } objects = bulk_retrieve([id], *params) return objects.first if objects && objects.size == 1 end |
#slice(start, length) ⇒ Enumerable<Hash>
Get a slice of object by offset, length. start
here is an offset (starting at 0) not an Id.
266 267 268 269 270 271 272 |
# File 'lib/lims-core/persistence/persistor.rb', line 266 def slice(start, length) to_load = StateGroup.new(self, []) for_each_in_slice(start, length) do |att| to_load << new_from_attributes(att) end to_load.load.map(&:resource) end |
#state_for(object) ⇒ ResourceState
Returns the state proxy of an object. Creates it if needed.
142 143 144 |
# File 'lib/lims-core/persistence/persistor.rb', line 142 def state_for(object) @object_to_state[object] end |
#state_for?(object) ⇒ Boolean
147 148 149 |
# File 'lib/lims-core/persistence/persistor.rb', line 147 def state_for?(object) @object_to_state.include?(object) end |
#state_for_id(id) ⇒ ResourceState
Returns the state proxy of an object fromt its id (in cache). Creates the state if needed.
155 156 157 |
# File 'lib/lims-core/persistence/persistor.rb', line 155 def state_for_id(id) @id_to_state[id] end |