lims-core 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/.rvmrc +2 -0
  5. data/.travis.yml +2 -0
  6. data/.vimrc +27 -0
  7. data/.yard_templates/default/layout/html/footer.erb +0 -0
  8. data/.yardopts +1 -0
  9. data/Gemfile +54 -0
  10. data/Gemfile.lock +197 -0
  11. data/Guardfile +21 -0
  12. data/Guardfile.tmux +28 -0
  13. data/README.markdown +67 -0
  14. data/Rakefile +16 -0
  15. data/config/database.yml +16 -0
  16. data/doc/Array.html +116 -0
  17. data/doc/Array/ArrayLoggerPersistor.html +152 -0
  18. data/doc/Lims.html +114 -0
  19. data/doc/Lims/Core.html +178 -0
  20. data/doc/Lims/Core/Action.html +91 -0
  21. data/doc/Lims/Core/Actions.html +116 -0
  22. data/doc/Lims/Core/Actions/Action.html +216 -0
  23. data/doc/Lims/Core/Actions/Action/AfterEval.html +853 -0
  24. data/doc/Lims/Core/Actions/Action/InvalidParameters.html +268 -0
  25. data/doc/Lims/Core/Actions/ActionGroup.html +196 -0
  26. data/doc/Lims/Core/Actions/ActionGroup/AfterEval.html +315 -0
  27. data/doc/Lims/Core/Actions/BulkAction.html +224 -0
  28. data/doc/Lims/Core/Actions/BulkAction/AfterEval.html +253 -0
  29. data/doc/Lims/Core/Actions/TestActionGroup.html +101 -0
  30. data/doc/Lims/Core/Actions/TestActionGroup/Action.html +133 -0
  31. data/doc/Lims/Core/Actions/TestActionGroup/ActionGroup.html +127 -0
  32. data/doc/Lims/Core/Base.html +287 -0
  33. data/doc/Lims/Core/Base/AccessibleViaSuper.html +252 -0
  34. data/doc/Lims/Core/Base/ClassMethod.html +177 -0
  35. data/doc/Lims/Core/Base/HashString.html +177 -0
  36. data/doc/Lims/Core/Base/IsArrayOf.html +606 -0
  37. data/doc/Lims/Core/Base/State.html +130 -0
  38. data/doc/Lims/Core/Organization.html +113 -0
  39. data/doc/Lims/Core/Organization/Batch.html +106 -0
  40. data/doc/Lims/Core/Persistence.html +267 -0
  41. data/doc/Lims/Core/Persistence/ComparisonFilter.html +318 -0
  42. data/doc/Lims/Core/Persistence/Filter.html +252 -0
  43. data/doc/Lims/Core/Persistence/IdentityMap.html +409 -0
  44. data/doc/Lims/Core/Persistence/IdentityMap/Class.html +144 -0
  45. data/doc/Lims/Core/Persistence/IdentityMap/DuplicateError.html +126 -0
  46. data/doc/Lims/Core/Persistence/IdentityMap/DuplicateIdError.html +136 -0
  47. data/doc/Lims/Core/Persistence/IdentityMap/DuplicateObjectError.html +136 -0
  48. data/doc/Lims/Core/Persistence/IdentityMapClass.html +133 -0
  49. data/doc/Lims/Core/Persistence/Logger.html +105 -0
  50. data/doc/Lims/Core/Persistence/Logger/Persistor.html +334 -0
  51. data/doc/Lims/Core/Persistence/Logger/Session.html +452 -0
  52. data/doc/Lims/Core/Persistence/Logger/Store.html +470 -0
  53. data/doc/Lims/Core/Persistence/MessageBus.html +871 -0
  54. data/doc/Lims/Core/Persistence/MessageBus/ConnectionError.html +123 -0
  55. data/doc/Lims/Core/Persistence/MessageBus/InvalidSettingsError.html +122 -0
  56. data/doc/Lims/Core/Persistence/MultiCriteriaFilter.html +293 -0
  57. data/doc/Lims/Core/Persistence/PersistAssociationTrait.html +91 -0
  58. data/doc/Lims/Core/Persistence/PersistableTrait.html +91 -0
  59. data/doc/Lims/Core/Persistence/Persistor.html +3072 -0
  60. data/doc/Lims/Core/Persistence/Persistor/DuplicateError.html +205 -0
  61. data/doc/Lims/Core/Persistence/Persistor/DuplicateIdError.html +147 -0
  62. data/doc/Lims/Core/Persistence/Persistor/DuplicateObjectError.html +147 -0
  63. data/doc/Lims/Core/Persistence/PersistorTrait.html +91 -0
  64. data/doc/Lims/Core/Persistence/ResourceState.html +1738 -0
  65. data/doc/Lims/Core/Persistence/Search.html +269 -0
  66. data/doc/Lims/Core/Persistence/Search/CreateSearch.html +251 -0
  67. data/doc/Lims/Core/Persistence/Search/SearchPersistor.html +240 -0
  68. data/doc/Lims/Core/Persistence/Search/SearchSequelPersistor.html +396 -0
  69. data/doc/Lims/Core/Persistence/Sequel.html +117 -0
  70. data/doc/Lims/Core/Persistence/Sequel/Filters.html +462 -0
  71. data/doc/Lims/Core/Persistence/Sequel/ForTest.html +101 -0
  72. data/doc/Lims/Core/Persistence/Sequel/ForTest/Name.html +137 -0
  73. data/doc/Lims/Core/Persistence/Sequel/ForTest/Name/NamePersitor.html +143 -0
  74. data/doc/Lims/Core/Persistence/Sequel/Migrations.html +266 -0
  75. data/doc/Lims/Core/Persistence/Sequel/Persistor.html +665 -0
  76. data/doc/Lims/Core/Persistence/Sequel/Session.html +501 -0
  77. data/doc/Lims/Core/Persistence/Sequel/Store.html +417 -0
  78. data/doc/Lims/Core/Persistence/Session.html +2751 -0
  79. data/doc/Lims/Core/Persistence/Session/UnmanagedObjectError.html +111 -0
  80. data/doc/Lims/Core/Persistence/StateGroup.html +696 -0
  81. data/doc/Lims/Core/Persistence/StateList.html +498 -0
  82. data/doc/Lims/Core/Persistence/Store.html +695 -0
  83. data/doc/Lims/Core/Persistence/UuidResource.html +1044 -0
  84. data/doc/Lims/Core/Persistence/UuidResource/InvalidUuidError.html +111 -0
  85. data/doc/Lims/Core/Persistence/UuidResource/UuidResourcePersistor.html +337 -0
  86. data/doc/Lims/Core/Persistence/Uuidable.html +799 -0
  87. data/doc/Lims/Core/Persistor.html +320 -0
  88. data/doc/Lims/Core/Resource.html +165 -0
  89. data/doc/Object.html +228 -0
  90. data/doc/SessionSpec.html +101 -0
  91. data/doc/SessionSpec/Model.html +279 -0
  92. data/doc/SessionSpec/Model/ModelPersistor.html +327 -0
  93. data/doc/_index.html +732 -0
  94. data/doc/class_list.html +47 -0
  95. data/doc/css/common.css +1 -0
  96. data/doc/css/full_list.css +55 -0
  97. data/doc/css/style.css +322 -0
  98. data/doc/file.README.html +127 -0
  99. data/doc/file_list.html +49 -0
  100. data/doc/frames.html +13 -0
  101. data/doc/index.html +127 -0
  102. data/doc/js/app.js +205 -0
  103. data/doc/js/full_list.js +167 -0
  104. data/doc/js/jquery.js +16 -0
  105. data/doc/method_list.html +1894 -0
  106. data/doc/top-level-namespace.html +100 -0
  107. data/lib/common.rb +18 -0
  108. data/lib/lims-core.rb +29 -0
  109. data/lib/lims-core/actions.rb +10 -0
  110. data/lib/lims-core/actions/action.rb +185 -0
  111. data/lib/lims-core/actions/action_group.rb +54 -0
  112. data/lib/lims-core/actions/bulk_action.rb +65 -0
  113. data/lib/lims-core/base.rb +132 -0
  114. data/lib/lims-core/helpers.rb +41 -0
  115. data/lib/lims-core/persistence.rb +15 -0
  116. data/lib/lims-core/persistence/comparison_filter.rb +54 -0
  117. data/lib/lims-core/persistence/filter.rb +23 -0
  118. data/lib/lims-core/persistence/identity_map.rb +55 -0
  119. data/lib/lims-core/persistence/logger/all.rb +5 -0
  120. data/lib/lims-core/persistence/logger/persistor.rb +35 -0
  121. data/lib/lims-core/persistence/logger/session.rb +30 -0
  122. data/lib/lims-core/persistence/logger/store.rb +37 -0
  123. data/lib/lims-core/persistence/message_bus.rb +131 -0
  124. data/lib/lims-core/persistence/multi_criteria_filter.rb +50 -0
  125. data/lib/lims-core/persistence/persist_association_trait.rb +96 -0
  126. data/lib/lims-core/persistence/persistable_trait.rb +150 -0
  127. data/lib/lims-core/persistence/persistor.rb +495 -0
  128. data/lib/lims-core/persistence/resource_state.rb +157 -0
  129. data/lib/lims-core/persistence/search.rb +3 -0
  130. data/lib/lims-core/persistence/search/all.rb +3 -0
  131. data/lib/lims-core/persistence/search/create_search.rb +55 -0
  132. data/lib/lims-core/persistence/search/search_persistor.rb +45 -0
  133. data/lib/lims-core/persistence/search/search_sequel_persistor.rb +40 -0
  134. data/lib/lims-core/persistence/sequel.rb +14 -0
  135. data/lib/lims-core/persistence/sequel/filters.rb +106 -0
  136. data/lib/lims-core/persistence/sequel/migrations.rb +14 -0
  137. data/lib/lims-core/persistence/sequel/migrations/add_audit_tables.rb +147 -0
  138. data/lib/lims-core/persistence/sequel/migrations/initial.rb +156 -0
  139. data/lib/lims-core/persistence/sequel/persistor.rb +200 -0
  140. data/lib/lims-core/persistence/sequel/session.rb +136 -0
  141. data/lib/lims-core/persistence/sequel/store.rb +37 -0
  142. data/lib/lims-core/persistence/session.rb +409 -0
  143. data/lib/lims-core/persistence/state_group.rb +97 -0
  144. data/lib/lims-core/persistence/state_list.rb +56 -0
  145. data/lib/lims-core/persistence/store.rb +73 -0
  146. data/lib/lims-core/persistence/uuid_resource.rb +115 -0
  147. data/lib/lims-core/persistence/uuid_resource_persistor.rb +43 -0
  148. data/lib/lims-core/persistence/uuidable.rb +107 -0
  149. data/lib/lims-core/resource.rb +21 -0
  150. data/lib/lims-core/subclass_tracker.rb +30 -0
  151. data/lib/lims-core/version.rb +5 -0
  152. data/lims-core.gemspec +40 -0
  153. data/makefile +52 -0
  154. data/showoff/core-2012-06-11/core/01_slide.md +237 -0
  155. data/showoff/core-2012-06-11/core/02_slide.md +110 -0
  156. data/showoff/core-2012-06-11/custom.css +44 -0
  157. data/showoff/core-2012-06-11/main/01_slide.md +53 -0
  158. data/showoff/core-2012-06-11/showoff.json +10 -0
  159. data/showoff/core-2012-06-11/tp1.tpl +1 -0
  160. data/spec/actions/action_group_spec.rb +39 -0
  161. data/spec/actions/spec_helper.rb +1 -0
  162. data/spec/persistence/identity_map_spec.rb +55 -0
  163. data/spec/persistence/logger/spec_helper.rb +7 -0
  164. data/spec/persistence/logger/store_spec.rb +48 -0
  165. data/spec/persistence/message_bus_spec.rb +76 -0
  166. data/spec/persistence/sequel/session_spec.rb +125 -0
  167. data/spec/persistence/sequel/spec_helper.rb +39 -0
  168. data/spec/persistence/sequel/store_shared.rb +25 -0
  169. data/spec/persistence/sequel/store_spec.rb +22 -0
  170. data/spec/persistence/session_spec.rb +199 -0
  171. data/spec/persistence/spec_helper.rb +2 -0
  172. data/spec/persistence/uuid_resource_spec.rb +80 -0
  173. data/spec/spec_helper.rb +10 -0
  174. data/spec/subclass_tracker_sperc.rb +62 -0
  175. data/utils/constant_tree.rb +29 -0
  176. data/utils/stack.rb +48 -0
  177. metadata +402 -0
@@ -0,0 +1,136 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+
3
+ require 'sequel'
4
+ require 'common'
5
+ require 'lims-core/persistence'
6
+ require 'lims-core/persistence/uuidable'
7
+ require 'lims-core/persistence/session'
8
+ require 'lims-core/helpers'
9
+
10
+ module Lims::Core
11
+ module Persistence
12
+ module Sequel
13
+ # Sequel specific implementation of a {Persistence::Session Session}.
14
+ class Session < Persistence::Session
15
+ include Uuidable
16
+ # Pack if needed an uuid to its store representation
17
+ # @param [String] uuid
18
+ # @return [Object]
19
+ def self.pack_uuid(uuid)
20
+ # Normal behavior shoulb be pack to binary data
21
+ UuidResource::pack(uuid)
22
+ #For now, we just compact it.
23
+ UuidResource::compact(uuid)
24
+
25
+ end
26
+
27
+ # Unpac if needed an uuid from its store representation
28
+ # @param [Object] puuid
29
+ # @return [String]
30
+ def self.unpack_uuid(puuid)
31
+ #UuidResource::unpack(puuid)
32
+ UuidResource::expand(puuid)
33
+ end
34
+
35
+ def serialize(object)
36
+ Lims::Core::Helpers::to_json(object)
37
+ end
38
+
39
+ def unserialize(object)
40
+ Lims::Core::Helpers::load_json(object)
41
+ end
42
+
43
+ def lock(datasets, unlock=false, &block)
44
+ datasets = [datasets] unless datasets.is_a?(Array)
45
+ db = datasets.first.db
46
+
47
+ # sqlite3 handles lock differently.
48
+ # @TODO create Session Subclass for each database type.
49
+ return lock_for_update(datasets, &block) if db.database_type == :sqlite
50
+
51
+ db.run("LOCK TABLES #{datasets.map { |d| "#{d.first_source} WRITE"}.join(",")}")
52
+ block.call(*datasets).tap { db.run("UNLOCK TABLES") if unlock }
53
+ end
54
+
55
+ # this method is to be used when the SQL store
56
+ # doesn't support LOCK, which is the case for SQLITE
57
+ # It can be used to redefine lock if needed.
58
+ def lock_for_update(datasets, &block)
59
+ datasets.first.db.transaction do
60
+ block.call(*datasets.map(&:for_update))
61
+ end
62
+ end
63
+
64
+ # Return the parameters needed for the creation
65
+ # of a session object. It use session attributes
66
+ # which have been set at contruction time.
67
+ # This allow the same session to be reopen multiple times
68
+ # and create each time a new session with the same parameters.
69
+ # @return [Hash]
70
+ def session_object_parameters
71
+ {:user => @user ,
72
+ :backend_application_id => @backend_application_id,
73
+ :parameters => serialize(@parameters) || nil
74
+ }
75
+ end
76
+
77
+ # Override with_session to create a session object
78
+ # needed by the database to update revision.
79
+ # session object are create from the parameters
80
+ # If the session can't be created due to the lack of parameters.
81
+ # Nothing is created.
82
+ def with_session(*params, &block)
83
+ create_session = true
84
+ success = false
85
+
86
+ # @todo Subclass Session for Sql adapter
87
+ if database.database_type == :sqlite
88
+ create_session = false
89
+ else
90
+ previous_session_id = database.fetch("SELECT @current_session_id AS id").first[:id]
91
+ create_session = false if previous_session_id
92
+ end
93
+
94
+ if create_session
95
+ session_id = database[:sessions].insert(session_object_parameters)
96
+ set_current_session(session_id)
97
+ end
98
+
99
+ begin
100
+ result = super(*params, &block)
101
+ success = true
102
+ ensure
103
+ if create_session
104
+ # mark it as finished
105
+ database[:sessions].where(:id => session_id).update(:end_time => DateTime.now, :success => success)
106
+ set_current_session(nil)
107
+ end
108
+ end
109
+
110
+ return result
111
+ end
112
+
113
+ def get_current_session
114
+ return if database.database_type == :sqlite
115
+ database.fetch("SELECT @current_session_id AS id").first[:id]
116
+ end
117
+
118
+ def set_current_session(current_session_id=@current_session_id)
119
+ return if database.database_type == :sqlite
120
+ database.run "SET @current_session_id = #{current_session_id ? current_session_id : "NULL"};"
121
+ @current_session_id = current_session_id
122
+ end
123
+
124
+ def transaction
125
+ super do
126
+ # Set the current_session_id again
127
+ # in case it's been overriden by another thread.
128
+ # Solves bug #64570338
129
+ set_current_session
130
+ yield
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,37 @@
1
+ # vi: ts=2:sts=2:et:sw=2
2
+
3
+ require 'lims-core/persistence/store'
4
+ require 'lims-core/persistence/sequel/session'
5
+ require 'sequel'
6
+
7
+ module Lims::Core
8
+ module Persistence
9
+ module Sequel
10
+ # An Sequel::Store, ie an wrapper around a database
11
+ # using the sequel gem
12
+ class Store < Persistence::Store
13
+ attr_reader :database
14
+ # @attribute
15
+
16
+ # Create a store with a Sequel::Database
17
+ # We don't wrap for now the creation of the database
18
+ # @param [Sequel::Database] type underlying database
19
+ def initialize(database, *args)
20
+ raise RuntimeError unless database.is_a?(::Sequel::Database)
21
+ @database = database
22
+ super(*args)
23
+ @dirty_attribute_strategy = DIRTY_ATTRIBUTE_STRATEGY_SHA1
24
+ end
25
+
26
+ # Execute given block within a transaction
27
+ # and create a session object needed to update
28
+ # revisionned table.
29
+ def transaction
30
+ database.transaction do
31
+ super
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,409 @@
1
+ # vi: ts=2:sts=2:et:sw=2
2
+
3
+ require 'common'
4
+ require 'forwardable'
5
+ require 'digest/md5'
6
+
7
+ require 'lims-core/persistence/filter'
8
+ require 'lims-core/persistence/identity_map'
9
+ require 'lims-core/persistence/state_list'
10
+ require 'lims-core/helpers'
11
+
12
+ module Lims::Core
13
+ module Persistence
14
+ # A session is in charge of restoring and saving object throug the persistence layer.
15
+ # A Session can not normally be created by the end user. It has to be in a Store::with_session
16
+ # block, which acts has a transaction and save/update everything at the end of it.
17
+ # It should also provides an identity map.
18
+ # Session information (user, time) are also associated to the modifications of those objects.
19
+ class Session
20
+
21
+ # The dirty-attribute strategy decides
22
+ # how object modification is detected
23
+ # to avoid saved unmodified object.
24
+ # The default value comes from the session.
25
+ attr_accessor :dirty_attribute_strategy
26
+ class UnmanagedObjectError < RuntimeError
27
+ end
28
+
29
+ # The map name <=> model class is shared between all type of session
30
+ #
31
+ def self.model_map()
32
+ @@model_map ||= IdentityMap::Class.new
33
+ end
34
+ # The map of peristor classes depends of the session type (sequel, log, etc ..)
35
+ # As they will be different classes
36
+
37
+
38
+ extend Forwardable
39
+ # param [Store] store the underlying store.
40
+ def initialize(store, *params)
41
+ @store = store
42
+ @object_states = StateList.new
43
+ @in_session = false
44
+ @saved = Set.new
45
+ @persistor_map = {}
46
+ @dirty_attribute_strategy = @store.dirty_attribute_strategy
47
+
48
+
49
+ options = params.extract_options!
50
+ @user ||= options[:user]
51
+ @backend_application_id ||= options[:backend_application_id]
52
+ @parameters ||= options[:parameters]
53
+ end
54
+
55
+
56
+ def_delegators :@store, :database
57
+
58
+ # Execute a block and save every 'marked' object
59
+ # in a transaction at the End.
60
+ # @yieldparam [Session] session the created session.
61
+ # @return the value of the block
62
+ def with_session(*params, &block)
63
+ return block[self] if @in_session
64
+ begin
65
+ @in_session = true
66
+ to_return = block[self]
67
+ @in_session = false
68
+ save_all
69
+ return to_return
70
+ ensure
71
+ @in_session = false
72
+ end
73
+ end
74
+ # Subsession allow to create a session
75
+ # within a session sharing the same persistor
76
+ # but saving only the object managed by the subsession.
77
+ # The current implementation doesn't create new session
78
+ # but just push some session attributes.
79
+ # The problem about creating a new Session, we want them
80
+ # to share ResourceState, but a state own a persistor which in
81
+ # turn own a session, so it's easier if the session is the same.
82
+ def with_subsession(*params, &block)
83
+ backup = [@object_states, @in_session, @saved]
84
+ @object_states = StateList.new
85
+ @in_session = false
86
+ @saved = Set.new
87
+
88
+ return_value = with_session(*params, &block)
89
+
90
+ @object_states, @in_session, @saved = backup
91
+
92
+ return_value
93
+ end
94
+
95
+
96
+ # Tell the session to be responsible of an object.
97
+ # The object will be saved at the end of the session.
98
+ # @example
99
+ # store.with_session do |session|
100
+ # session << Plate.new
101
+ # end
102
+ # @param [Persistable] object the object to persist.
103
+ # @return the session, to allow for chaining
104
+ def << (object)
105
+ manage_state(state_for(object))
106
+ self
107
+ end
108
+
109
+ def manage_state(state)
110
+ @object_states << state
111
+ end
112
+
113
+ def method_missing(name, *args, &block)
114
+ begin
115
+ persistor_for(name)
116
+ rescue NameError
117
+ # No persistor found for the given name
118
+ # Call the normal method_missing
119
+ super(name, *args, &block)
120
+ end
121
+ end
122
+
123
+
124
+ # Returns the id of an object if exists.
125
+ # @param [Resource, Id] object or id.
126
+ # @return [Id, nil]
127
+ def id_for(object)
128
+ case object
129
+ when Resource then persistor_for(object).id_for(object)
130
+ else object # the object should be already an id
131
+ end
132
+ end
133
+
134
+ # Get or creates the ResourceState corresponding to an object.
135
+ # @param [Resource] object
136
+ # @return [ResourceState]
137
+ def state_for(object)
138
+ return persistor_for(object).state_for(object)
139
+ end
140
+
141
+ def states_for(objects)
142
+ objects && objects.map { |o| state_for(o) }
143
+ end
144
+
145
+
146
+
147
+ # Returns the id of an object and save it if necessary
148
+ # @param [Resource, Id] object or id.
149
+ # @return [Id]
150
+ def id_for!(object)
151
+ return nil unless object
152
+ id_for(object) || save(object)
153
+ end
154
+
155
+ # Check if the session 'mananage' already this object.
156
+ # .i.e if it's been loaded or meant to be saved
157
+ # @param [Resource] object
158
+ # @return [Boolean]
159
+ def managed?(object)
160
+ persistor_for(object).state_for?(object)
161
+ end
162
+
163
+ # Mark an object as to be deleted.
164
+ # The corresponding object will be deleted at the end of the session.
165
+ # For most object you don't need to load it to delete it
166
+ # but some needs (to delete the appropriate children).
167
+ # The real delete is made by calling the {#delete_in_real} method.
168
+ def delete(object)
169
+ raise UnmanagedObjectError, "can't delete #{object.inspect}" unless managed?(object)
170
+ state = state_for(object)
171
+ state.mark_for_deletion
172
+ end
173
+
174
+ # Pack if needed an uuid to its store representation
175
+ # This method is need to lookup an uuid by name
176
+ # @param [String] uuid
177
+ # @return [Object]
178
+ def self.pack_uuid(uuid)
179
+ uuid
180
+ end
181
+
182
+ def pack_uuid(uuid)
183
+ self.class.pack_uuid(uuid)
184
+ end
185
+
186
+ # Unpac if needed an uuid from its store representation
187
+ # @param [Object] puuid
188
+ # @return [String]
189
+ def self.unpack_uuid(puuid)
190
+ puuid
191
+ end
192
+
193
+ def unpack_uuid(uuid)
194
+ self.class.unpack_uuid(uuid)
195
+ end
196
+
197
+ # @todo doc
198
+ def serialize(object)
199
+ object
200
+ end
201
+
202
+ def unserialize(object)
203
+ object
204
+ end
205
+
206
+ def dirty_key_for(object)
207
+ case @dirty_attribute_strategy
208
+ when Store::DIRTY_ATTRIBUTE_STRATEGY_DEEP_COPY then object
209
+ when Store::DIRTY_ATTRIBUTE_STRATEGY_SHA1 then Digest::SHA1.hexdigest(Lims::Core::Helpers::to_json(object))
210
+ when Store::DIRTY_ATTRIBUTE_STRATEGY_MD5 then Digest::MD5.hexdigest(Lims::Core::Helpers::to_json(object))
211
+ when Store::DIRTY_ATTRIBUTE_STRATEGY_QUICK_HASH then object.hash
212
+ end
213
+ end
214
+
215
+ private
216
+ # save all objects which needs to be
217
+ def save_all()
218
+ transaction do
219
+ @save_in_progress = true # allows saving
220
+ @object_states.reset_status
221
+ @object_states.save
222
+ end
223
+ @save_in_progress = false
224
+ end
225
+
226
+ # Execute the provided block within a transaction
227
+ # Here to be overriden if needed
228
+ def transaction
229
+ @store.transaction do
230
+ yield
231
+ end
232
+ end
233
+
234
+ # Create a new persistor sharing the same internal parameters
235
+ # but with the "context" (datasest) of the new one.
236
+ # This can be used to "reset" a filtered persistor to the current session.
237
+ # @param [Persistor] persistor
238
+ # @return [Persistor]
239
+ def filter(persistor)
240
+ # If the persistor session is the current session, there is nothing to do
241
+ # just return the object as it is.
242
+ return persistor if persistor.instance_eval {@session} == self
243
+
244
+ # we need first to find the original persistor, ie the one that the user can call via
245
+ # session.model
246
+ original = persistor_for(persistor.class)
247
+ persistor.class.new(original, persistor.dataset)
248
+ end
249
+
250
+ # Find the model class for a registered name
251
+ # registered name are used when doing session.model
252
+ # @param [String, Symbol] name
253
+ # @return [Class]
254
+ def self.name_to_model(name)
255
+ model_map.object_for(name.to_s)
256
+ end
257
+
258
+ # Find the registered name of a given class
259
+ # @param[Class] model
260
+ # @return [Symbol]
261
+ def self.model_to_name(model)
262
+ model_map.id_for(model)
263
+ end
264
+
265
+ # Register a model for a given name.
266
+ # This name will be looked up when calling session.<name>
267
+ # Persistors need to be registered.
268
+ # @param [String, Symbol] name
269
+ # @param [Class] model
270
+ def self.register_model(name, model)
271
+ name = name.to_s.snakecase
272
+ # skip if name already registered with the same object
273
+ return if model_map.object_for(name) == model
274
+ model_map.map_id_object(name, model)
275
+ end
276
+
277
+
278
+ # Find the model corresponding to an object
279
+ # Takes many type of input
280
+ # @param [String, Symbol, Resource, Persistor] object
281
+ # @return [Symbol]
282
+ def self.model_for(object)
283
+ case object
284
+ when nil then nil
285
+ when String then name_to_model(object)
286
+ when Symbol then name_to_model(object)
287
+ when Class then
288
+ # check if the class has been registered
289
+ # IMPORTANT needs to be done before 'when module'
290
+ # because object can class and module at the same time.
291
+ return object if model_to_name(object)
292
+
293
+ # if it's already persistor find the associate model
294
+ persistor_class_map.id_for(object) do |model|
295
+ return model
296
+ end
297
+
298
+
299
+
300
+ # check the super class
301
+ model_for(object.superclass).andtap { |model|
302
+ return model
303
+ }
304
+
305
+ # Check the owner
306
+ return nil unless object.respond_to? :parent_scope
307
+ model_for(object.parent_scope).andtap { |model|
308
+ return model
309
+ }
310
+ when Module then object
311
+ else
312
+ model_for(object.class)
313
+ end
314
+ end
315
+
316
+ def self.persistor_name_for(object)
317
+ model = model_for(object)
318
+ model_to_name(model)
319
+ end
320
+
321
+ def persistor_name_for(object)
322
+ self.class.persistor_name_for(object)
323
+ end
324
+
325
+ # @param [String, Symbol, Resource, Persistor] object
326
+ # @return [Class]
327
+ def self.persistor_class_for(object)
328
+ model = model_for(object)
329
+
330
+ persistor = persistor_class_map.object_for(model)
331
+ unless persistor
332
+ persistor = find_or_create_persistor_for(model)
333
+ persistor_class_map.map_id_object(model, persistor)
334
+ end
335
+ persistor
336
+ end
337
+
338
+ def self.persistor_class_map()
339
+ @persistor_class_map ||= IdentityMap::Class.new
340
+ end
341
+
342
+ def self.find_or_create_persistor_for(model)
343
+ # find the persistor within the class
344
+ # other corresponding to the current session type
345
+ return nil unless model
346
+ session_persistor_class = parent_scope.const_get(:Persistor)
347
+ model.constants(false).each do |name|
348
+ klass = model.const_get(name)
349
+ next unless klass.is_a? Module
350
+ if klass.ancestors.include?(session_persistor_class)
351
+ # quick hack to fix JRuby test before refactoring this
352
+ # If we are not in a sequel session, we need to not pick the Seque persistor.
353
+ next if session_persistor_class.name !~ /sequel/i && klass.name =~ /sequel/i
354
+ # found
355
+ return klass
356
+ end
357
+ end
358
+ # not found, we need to create it
359
+ # First we look for the base persistor to inherit from
360
+ #debugger unless superclass.respond_to? :persistor_class_for
361
+ raise "Can't find base persistor for #{model.inspect}" unless superclass.respond_to? :persistor_class_for
362
+
363
+ parent_persistor_class = superclass.persistor_class_for(model)
364
+
365
+ # if the current persistor (ex Sequel::Persistor) is the same as the base one
366
+ # there is nothing else to do
367
+ return parent_persistor_class unless parent_scope::const_defined?(:Persistor, false)
368
+
369
+ raise "no Persistor defined for #{model.name}" unless parent_persistor_class
370
+ module_name = parent_scope.name.sub(/.*Persistence::/,'')
371
+ model_name = model.name.split('::').pop
372
+ # the we create a new Persistor class including the Persistor mixin
373
+ # corresponding to the session
374
+ class_declaration = <<-EOV
375
+ class #{model_name}#{module_name}Persistor < #{parent_persistor_class.name}
376
+ include #{parent_scope::Persistor}
377
+ end
378
+ EOV
379
+ model.class_eval class_declaration
380
+
381
+ end
382
+
383
+
384
+ # Get the persistor corresponding to the object class
385
+ # @param [Resource, String, Symbol, Persistor] object
386
+ # @return [Persistor, nil]
387
+ def persistor_for(object)
388
+ if object.is_a?(Persistor)
389
+ return filter(object)
390
+ end
391
+
392
+ model = self.class.model_for(object)
393
+ @persistor_map[model] ||= begin
394
+ persistor_class = self.class.persistor_class_for(model)
395
+ raise NameError, "no persistor defined for #{object.class.name}" unless persistor_class && persistor_class.ancestors.include?(Persistor)
396
+ persistor_class.new(self)
397
+ end
398
+ end
399
+
400
+
401
+ public :persistor_for
402
+ # Compute the class name of the persistor corresponding to the argument
403
+ # @param [Resource, String, Symbol] object
404
+ # @return [String]
405
+ end
406
+ end
407
+ end
408
+
409
+ require 'lims-core/persistence/uuid_resource_persistor'