Class: Lims::Core::Persistence::Session
- Extended by:
- Forwardable
- Defined in:
- lib/lims-core/persistence/session.rb
Overview
A session is in charge of restoring and saving object throug the persistence layer. A Session can not normally be created by the end user. It has to be in a Store::with_session block, which acts has a transaction and save/update everything at the end of it. It should also provides an identity map. Session information (user, time) are also associated to the modifications of those objects.
Direct Known Subclasses
Defined Under Namespace
Classes: UnmanagedObjectError
Instance Attribute Summary collapse
-
#dirty_attribute_strategy ⇒ Object
The dirty-attribute strategy decides how object modification is detected to avoid saved unmodified object.
Class Method Summary collapse
- .find_or_create_persistor_for(model) ⇒ Object private
-
.model_for(object) ⇒ Symbol
private
Find the model corresponding to an object Takes many type of input.
-
.model_map ⇒ Object
The map name <=> model class is shared between all type of session.
-
.model_to_name(model) ⇒ Symbol
private
Find the registered name of a given class @param model.
-
.name_to_model(name) ⇒ Class
private
Find the model class for a registered name registered name are used when doing session.model.
-
.pack_uuid(uuid) ⇒ Object
Pack if needed an uuid to its store representation This method is need to lookup an uuid by name.
- .persistor_class_for(object) ⇒ Class private
- .persistor_class_map ⇒ Object private
- .persistor_name_for(object) ⇒ Object private
-
.register_model(name, model) ⇒ Object
private
Register a model for a given name.
-
.unpack_uuid(puuid) ⇒ String
Unpac if needed an uuid from its store representation.
Instance Method Summary collapse
-
#<<(object) ⇒ Object
Tell the session to be responsible of an object.
-
#delete(object) ⇒ Object
Mark an object as to be deleted.
- #dirty_key_for(object) ⇒ Object
-
#filter(persistor) ⇒ Persistor
private
Create a new persistor sharing the same internal parameters but with the “context” (datasest) of the new one.
-
#id_for(object) ⇒ Id?
Returns the id of an object if exists.
-
#id_for!(object) ⇒ Id
Returns the id of an object and save it if necessary.
-
#initialize(store, *params) ⇒ Session
constructor
param [Store] store the underlying store.
- #manage_state(state) ⇒ Object
-
#managed?(object) ⇒ Boolean
Check if the session ‘mananage’ already this object.
- #method_missing(name, *args, &block) ⇒ Object
- #pack_uuid(uuid) ⇒ Object
-
#persistor_for(object) ⇒ Persistor?
Get the persistor corresponding to the object class.
- #persistor_name_for(object) ⇒ Object private
-
#save_all ⇒ Object
private
save all objects which needs to be.
- #serialize(object) ⇒ Object
-
#state_for(object) ⇒ ResourceState
Get or creates the ResourceState corresponding to an object.
- #states_for(objects) ⇒ Object
-
#transaction ⇒ Object
private
Execute the provided block within a transaction Here to be overriden if needed.
- #unpack_uuid(uuid) ⇒ Object
- #unserialize(object) ⇒ Object
-
#with_session(*params) {|session| ... } ⇒ Object
Execute a block and save every ‘marked’ object in a transaction at the End.
-
#with_subsession(*params, &block) ⇒ Object
Subsession allow to create a session within a session sharing the same persistor but saving only the object managed by the subsession.
Constructor Details
#initialize(store, *params) ⇒ Session
param [Store] store the underlying store.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/lims-core/persistence/session.rb', line 40 def initialize(store, *params) @store = store @object_states = StateList.new @in_session = false @saved = Set.new @persistor_map = {} @dirty_attribute_strategy = @store.dirty_attribute_strategy = params. @user ||= [:user] @backend_application_id ||= [:backend_application_id] @parameters ||= [:parameters] end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
113 114 115 116 117 118 119 120 121 |
# File 'lib/lims-core/persistence/session.rb', line 113 def method_missing(name, *args, &block) begin persistor_for(name) rescue NameError # No persistor found for the given name # Call the normal method_missing super(name, *args, &block) end end |
Instance Attribute Details
#dirty_attribute_strategy ⇒ Object
The dirty-attribute strategy decides how object modification is detected to avoid saved unmodified object. The default value comes from the session.
25 26 27 |
# File 'lib/lims-core/persistence/session.rb', line 25 def dirty_attribute_strategy @dirty_attribute_strategy end |
Class Method Details
.find_or_create_persistor_for(model) ⇒ Object (private)
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/lims-core/persistence/session.rb', line 342 def self.find_or_create_persistor_for(model) # find the persistor within the class # other corresponding to the current session type return nil unless model session_persistor_class = parent_scope.const_get(:Persistor) model.constants(false).each do |name| klass = model.const_get(name) next unless klass.is_a? Module if klass.ancestors.include?(session_persistor_class) # quick hack to fix JRuby test before refactoring this # If we are not in a sequel session, we need to not pick the Seque persistor. next if session_persistor_class.name !~ /sequel/i && klass.name =~ /sequel/i # found return klass end end # not found, we need to create it # First we look for the base persistor to inherit from #debugger unless superclass.respond_to? :persistor_class_for raise "Can't find base persistor for #{model.inspect}" unless superclass.respond_to? :persistor_class_for parent_persistor_class = superclass.persistor_class_for(model) # if the current persistor (ex Sequel::Persistor) is the same as the base one # there is nothing else to do return parent_persistor_class unless parent_scope::const_defined?(:Persistor, false) raise "no Persistor defined for #{model.name}" unless parent_persistor_class module_name = parent_scope.name.sub(/.*Persistence::/,'') model_name = model.name.split('::').pop # the we create a new Persistor class including the Persistor mixin # corresponding to the session class_declaration = <<-EOV class #{model_name}#{module_name}Persistor < #{parent_persistor_class.name} include #{parent_scope::Persistor} end EOV model.class_eval class_declaration end |
.model_for(object) ⇒ Symbol (private)
Find the model corresponding to an object Takes many type of input
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
# File 'lib/lims-core/persistence/session.rb', line 282 def self.model_for(object) case object when nil then nil when String then name_to_model(object) when Symbol then name_to_model(object) when Class then # check if the class has been registered # IMPORTANT needs to be done before 'when module' # because object can class and module at the same time. return object if model_to_name(object) # if it's already persistor find the associate model persistor_class_map.id_for(object) do |model| return model end # check the super class model_for(object.superclass).andtap { |model| return model } # Check the owner return nil unless object.respond_to? :parent_scope model_for(object.parent_scope).andtap { |model| return model } when Module then object else model_for(object.class) end end |
.model_map ⇒ Object
The map name <=> model class is shared between all type of session
31 32 33 |
# File 'lib/lims-core/persistence/session.rb', line 31 def self.model_map() @@model_map ||= IdentityMap::Class.new end |
.model_to_name(model) ⇒ Symbol (private)
Find the registered name of a given class @param model
261 262 263 |
# File 'lib/lims-core/persistence/session.rb', line 261 def self.model_to_name(model) model_map.id_for(model) end |
.name_to_model(name) ⇒ Class (private)
Find the model class for a registered name registered name are used when doing session.model
254 255 256 |
# File 'lib/lims-core/persistence/session.rb', line 254 def self.name_to_model(name) model_map.object_for(name.to_s) end |
.pack_uuid(uuid) ⇒ Object
Pack if needed an uuid to its store representation This method is need to lookup an uuid by name
178 179 180 |
# File 'lib/lims-core/persistence/session.rb', line 178 def self.pack_uuid(uuid) uuid end |
.persistor_class_for(object) ⇒ Class (private)
327 328 329 330 331 332 333 334 335 336 |
# File 'lib/lims-core/persistence/session.rb', line 327 def self.persistor_class_for(object) model = model_for(object) persistor = persistor_class_map.object_for(model) unless persistor persistor = find_or_create_persistor_for(model) persistor_class_map.map_id_object(model, persistor) end persistor end |
.persistor_class_map ⇒ Object (private)
338 339 340 |
# File 'lib/lims-core/persistence/session.rb', line 338 def self.persistor_class_map() @persistor_class_map ||= IdentityMap::Class.new end |
.persistor_name_for(object) ⇒ Object (private)
316 317 318 319 |
# File 'lib/lims-core/persistence/session.rb', line 316 def self.persistor_name_for(object) model = model_for(object) model_to_name(model) end |
.register_model(name, model) ⇒ Object (private)
Register a model for a given name. This name will be looked up when calling session.<name> Persistors need to be registered.
270 271 272 273 274 275 |
# File 'lib/lims-core/persistence/session.rb', line 270 def self.register_model(name, model) name = name.to_s.snakecase # skip if name already registered with the same object return if model_map.object_for(name) == model model_map.map_id_object(name, model) end |
.unpack_uuid(puuid) ⇒ String
Unpac if needed an uuid from its store representation
189 190 191 |
# File 'lib/lims-core/persistence/session.rb', line 189 def self.unpack_uuid(puuid) puuid end |
Instance Method Details
#<<(object) ⇒ Object
Tell the session to be responsible of an object. The object will be saved at the end of the session.
104 105 106 107 |
# File 'lib/lims-core/persistence/session.rb', line 104 def << (object) manage_state(state_for(object)) self end |
#delete(object) ⇒ Object
Mark an object as to be deleted. The corresponding object will be deleted at the end of the session. For most object you don’t need to load it to delete it but some needs (to delete the appropriate children). The real delete is made by calling the #delete_in_real method.
168 169 170 171 172 |
# File 'lib/lims-core/persistence/session.rb', line 168 def delete(object) raise UnmanagedObjectError, "can't delete #{object.inspect}" unless managed?(object) state = state_for(object) state.mark_for_deletion end |
#dirty_key_for(object) ⇒ Object
206 207 208 209 210 211 212 213 |
# File 'lib/lims-core/persistence/session.rb', line 206 def dirty_key_for(object) case @dirty_attribute_strategy when Store::DIRTY_ATTRIBUTE_STRATEGY_DEEP_COPY then object when Store::DIRTY_ATTRIBUTE_STRATEGY_SHA1 then Digest::SHA1.hexdigest(Lims::Core::Helpers::to_json(object)) when Store::DIRTY_ATTRIBUTE_STRATEGY_MD5 then Digest::MD5.hexdigest(Lims::Core::Helpers::to_json(object)) when Store::DIRTY_ATTRIBUTE_STRATEGY_QUICK_HASH then object.hash end end |
#filter(persistor) ⇒ Persistor (private)
Create a new persistor sharing the same internal parameters but with the “context” (datasest) of the new one. This can be used to “reset” a filtered persistor to the current session.
239 240 241 242 243 244 245 246 247 248 |
# File 'lib/lims-core/persistence/session.rb', line 239 def filter(persistor) # If the persistor session is the current session, there is nothing to do # just return the object as it is. return persistor if persistor.instance_eval {@session} == self # we need first to find the original persistor, ie the one that the user can call via # session.model original = persistor_for(persistor.class) persistor.class.new(original, persistor.dataset) end |
#id_for(object) ⇒ Id?
Returns the id of an object if exists.
127 128 129 130 131 132 |
# File 'lib/lims-core/persistence/session.rb', line 127 def id_for(object) case object when Resource then persistor_for(object).id_for(object) else object # the object should be already an id end end |
#id_for!(object) ⇒ Id
Returns the id of an object and save it if necessary
150 151 152 153 |
# File 'lib/lims-core/persistence/session.rb', line 150 def id_for!(object) return nil unless object id_for(object) || save(object) end |
#manage_state(state) ⇒ Object
109 110 111 |
# File 'lib/lims-core/persistence/session.rb', line 109 def manage_state(state) @object_states << state end |
#managed?(object) ⇒ Boolean
Check if the session ‘mananage’ already this object. .i.e if it’s been loaded or meant to be saved
159 160 161 |
# File 'lib/lims-core/persistence/session.rb', line 159 def managed?(object) persistor_for(object).state_for?(object) end |
#pack_uuid(uuid) ⇒ Object
182 183 184 |
# File 'lib/lims-core/persistence/session.rb', line 182 def pack_uuid(uuid) self.class.pack_uuid(uuid) end |
#persistor_for(object) ⇒ Persistor?
Get the persistor corresponding to the object class
387 388 389 390 391 392 393 394 395 396 397 398 |
# File 'lib/lims-core/persistence/session.rb', line 387 def persistor_for(object) if object.is_a?(Persistor) return filter(object) end model = self.class.model_for(object) @persistor_map[model] ||= begin persistor_class = self.class.persistor_class_for(model) raise NameError, "no persistor defined for #{object.class.name}" unless persistor_class && persistor_class.ancestors.include?(Persistor) persistor_class.new(self) end end |
#persistor_name_for(object) ⇒ Object (private)
321 322 323 |
# File 'lib/lims-core/persistence/session.rb', line 321 def persistor_name_for(object) self.class.persistor_name_for(object) end |
#save_all ⇒ Object (private)
save all objects which needs to be
217 218 219 220 221 222 223 224 |
# File 'lib/lims-core/persistence/session.rb', line 217 def save_all() transaction do @save_in_progress = true # allows saving @object_states.reset_status @object_states.save end @save_in_progress = false end |
#serialize(object) ⇒ Object
doc
198 199 200 |
# File 'lib/lims-core/persistence/session.rb', line 198 def serialize(object) object end |
#state_for(object) ⇒ ResourceState
Get or creates the ResourceState corresponding to an object.
137 138 139 |
# File 'lib/lims-core/persistence/session.rb', line 137 def state_for(object) return persistor_for(object).state_for(object) end |
#states_for(objects) ⇒ Object
141 142 143 |
# File 'lib/lims-core/persistence/session.rb', line 141 def states_for(objects) objects && objects.map { |o| state_for(o) } end |
#transaction ⇒ Object (private)
Execute the provided block within a transaction Here to be overriden if needed
228 229 230 231 232 |
# File 'lib/lims-core/persistence/session.rb', line 228 def transaction @store.transaction do yield end end |
#unpack_uuid(uuid) ⇒ Object
193 194 195 |
# File 'lib/lims-core/persistence/session.rb', line 193 def unpack_uuid(uuid) self.class.unpack_uuid(uuid) end |
#unserialize(object) ⇒ Object
202 203 204 |
# File 'lib/lims-core/persistence/session.rb', line 202 def unserialize(object) object end |
#with_session(*params) {|session| ... } ⇒ Object
Execute a block and save every ‘marked’ object in a transaction at the End.
62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/lims-core/persistence/session.rb', line 62 def with_session(*params, &block) return block[self] if @in_session begin @in_session = true to_return = block[self] @in_session = false save_all return to_return ensure @in_session = false end end |
#with_subsession(*params, &block) ⇒ Object
Subsession allow to create a session within a session sharing the same persistor but saving only the object managed by the subsession. The current implementation doesn’t create new session but just push some session attributes. The problem about creating a new Session, we want them to share ResourceState, but a state own a persistor which in turn own a session, so it’s easier if the session is the same.
82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/lims-core/persistence/session.rb', line 82 def with_subsession(*params, &block) backup = [@object_states, @in_session, @saved] @object_states = StateList.new @in_session = false @saved = Set.new return_value = with_session(*params, &block) @object_states, @in_session, @saved = backup return_value end |