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,97 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+ require 'lims-core/persistence/state_list'
3
+ module Lims::Core
4
+ module Persistence
5
+ # A immutable list of {ResourceState}.
6
+ class StateGroup < StateList
7
+ attr_reader :persistor
8
+ def initialize(persistor, states)
9
+ @persistor = persistor
10
+ super(states)
11
+ end
12
+ def groups
13
+ [self]
14
+ end
15
+
16
+ def new(&block)
17
+ self.class.new(self.persistor, block.call)
18
+ end
19
+
20
+ def save
21
+ # @todo method for that.
22
+ all_parents = StateList.new
23
+ each do |state|
24
+ next if state.resource == nil or state.to_delete
25
+ state.parents!.andtap do |parents|
26
+ all_parents.merge(parents)
27
+ end
28
+ end
29
+
30
+ all_parents.save
31
+
32
+ persistor.purge_invalid_object
33
+
34
+ # split by status
35
+ group_by(&:save_action).tap do |groups|
36
+ groups[:delete].andtap { |group| StateGroup.new(persistor, group).destroy }
37
+ groups[:update].andtap { |group| persistor.bulk_update(group) }
38
+ groups[:insert].andtap { |group| persistor.bulk_insert(group) }
39
+ end
40
+
41
+ all_children = StateList.new
42
+ each do |state|
43
+ next unless state.resource
44
+ state.body_saved!
45
+ state.children!.andtap do |children|
46
+ all_children.merge(children)
47
+ end
48
+ end
49
+ all_children.save
50
+ end
51
+
52
+ # @todo doc
53
+ # destroy because delete exists already for a Set
54
+ def destroy
55
+ return self if size == 0
56
+ # mark each item for deletion
57
+ # so the parents are not saved later.
58
+ # children needs to be deleted NOW to avoid
59
+ # foreign key constraint error.
60
+ each_with_object(StateList.new) do |state, list|
61
+ # don't delete children if they've been deleted already
62
+ next if state.children_saved?
63
+ state.mark_for_deletion
64
+ list.merge(persistor.deletable_children_for(state.resource))
65
+ state.children_saved!
66
+ end.destroy
67
+
68
+
69
+ persistor.bulk_delete(self.select { |s| !s.body_saved? })
70
+
71
+ each_with_object(StateList.new) do |state, list|
72
+ next if state.parents_saved?
73
+ list.merge(persistor.deletable_parents_for(state.resource))
74
+ state.parents_saved!
75
+ end.destroy
76
+ each { |state| state.body_saved! }
77
+
78
+ end
79
+
80
+ def load(*params)
81
+ to_load = select(&:to_load?)
82
+ all_parents = StateList.new
83
+ attributes_list = []
84
+ persistor.bulk_load(to_load, *params) do |att|
85
+ all_parents.merge(persistor.parents_for_attributes(att))
86
+ attributes_list << att
87
+ end
88
+ all_parents.load(*params)
89
+ attributes_list.map do |att|
90
+ persistor.new_from_attributes(att)
91
+ end
92
+ persistor.load_children(self, *params)
93
+ self
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,56 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+ require 'lims-core/persistence/resource_state'
3
+ require 'set'
4
+ module Lims::Core
5
+ module Persistence
6
+ # A immutable list of {ResourceState}.
7
+ class StateList < Set
8
+ # @return [List<StateGroup>]
9
+ def groups
10
+ persistor_order = map { |state| state.persistor}.uniq
11
+ grouped = group_by { |state| state.persistor }
12
+ persistor_order.map do |persistor|
13
+ StateGroup.new(persistor, grouped[persistor])
14
+ end
15
+ end
16
+
17
+ # We need to redefine some basic array function
18
+ # to keep the current class and not return an array
19
+ %w(select).each do |method|
20
+ define_method(method) do |&block|
21
+ new { super(&block) }
22
+ end
23
+ end
24
+
25
+ def new(&block)
26
+ self.class.new(block.call)
27
+ end
28
+
29
+ def save
30
+ groups.each do |group|
31
+ group.save
32
+ end
33
+ end
34
+
35
+ def destroy
36
+ groups.each do |group|
37
+ group.destroy
38
+ end
39
+ end
40
+
41
+ # @todo return object according to initial order ?
42
+ def load
43
+ groups.each do |group|
44
+ group.load
45
+ end
46
+ end
47
+
48
+ def reset_status
49
+ each { |state|
50
+ state.reset }
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+ require 'lims-core/persistence/state_group'
@@ -0,0 +1,73 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+ require 'common'
3
+
4
+ require 'lims-core/persistence'
5
+ require 'lims-core/persistence/session'
6
+
7
+ module Lims::Core
8
+ module Persistence
9
+ # A store represents a persistent datastore, where object can be saved and restored.
10
+ # A connection to a database, for example.
11
+ class Store
12
+ def self.const_missing(name)
13
+ super(name)
14
+ end
15
+
16
+ # The dirty-attribute strategy decides
17
+ # how object modification is detected
18
+ # to avoid saved unmodified object.
19
+ attr_accessor :dirty_attribute_strategy
20
+ DIRTY_ATTRIBUTE_STRATEGY_DEEP_COPY = 1
21
+ DIRTY_ATTRIBUTE_STRATEGY_SHA1 = 2
22
+ DIRTY_ATTRIBUTE_STRATEGY_MD5 = 3
23
+ DIRTY_ATTRIBUTE_STRATEGY_QUICK_HASH = 4
24
+ attr_accessor :dirty_attribute_strategy
25
+
26
+ # Retrieves the effective module of a class
27
+ # Useful to call "sibling" classes.
28
+ # @example
29
+ # class Sequel::Store < Store
30
+ # def session
31
+ # base_module::Session
32
+ # end
33
+ #
34
+ # session will return a Sequel::Session instead of a ::Session.
35
+ #
36
+ # @return [Module]
37
+ def self.base_module
38
+ @base_module ||= begin
39
+ base_name = name.sub(/::\w+$/, '')
40
+ constant(base_name)
41
+ end
42
+ end
43
+ def base_module
44
+ self.class.base_module
45
+ end
46
+
47
+ # Create a session and pass it to the block.
48
+ # This is the only way to get a session.
49
+ # @param [Array]
50
+ # @yieldparam [Session] session the created session.
51
+ # @return the value of the block
52
+ def with_session(*params, &block)
53
+ create_session(*params).with_session(&block)
54
+ end
55
+
56
+
57
+ # Create a session
58
+ # If a session is given a parameter
59
+ # return in instead of creating a new one.
60
+ def create_session(*params)
61
+ return params.first if(params.size >= 1 && params.first.is_a?(Session))
62
+ base_module::Session.new(self, *params)
63
+ end
64
+
65
+ # Execute given block within a transaction
66
+ # If it make sense.
67
+ def transaction
68
+ yield
69
+ end
70
+ end
71
+ end
72
+ end
73
+
@@ -0,0 +1,115 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+ require 'common'
3
+ require 'uuid'
4
+
5
+ require 'lims-core/resource'
6
+ require 'lims-core/persistence/resource_state'
7
+
8
+ module Lims::Core
9
+ module Persistence
10
+ # Bind a uuid (as a String) to a Resource (a key and a model)
11
+ # The key is a FixNum to find the corresponding resource in the store
12
+ # and the model is the real class of the object (or at least something allowing to find it)
13
+ # the `state` attribute is the {ResourceState} corresponding to linked {Resource}.
14
+ class UuidResource
15
+ include Resource
16
+ attribute :state, ResourceState, :writer => :private, :initializable => true
17
+ attribute :model_class, Class, :writer => :private, :initializable => true
18
+ attribute :uuid, String, :writer => :private, :initializable => true
19
+
20
+ def initialize(*args)
21
+ super(*args)
22
+ end
23
+
24
+ # Link the resouce state to the UuidResource itself
25
+ def state=(state_)
26
+ @state = state_
27
+ if state_
28
+ state_.uuid_resource = self
29
+ end
30
+ end
31
+
32
+ # for speed
33
+ def attributes
34
+ {state: state,
35
+ model_class: model_class,
36
+ uuid: uuid
37
+ }
38
+ end
39
+
40
+ def attributes_for_dirty
41
+ {state: state,
42
+ model_class: model_class,
43
+ }.tap do |att|
44
+ att[:uuid] = @uuid if @uuid
45
+ end
46
+ end
47
+
48
+ class InvalidUuidError < RuntimeError
49
+ end
50
+
51
+ Generator = UUID.new
52
+ Form = [8, 4, 4, 4, 12]
53
+ Length = Form.inject { |m, n| m+n }
54
+ ValidationRegexp = /#{Form.map { |n| "[0-9a-f]{#{n}}" }.join('-')}/i
55
+ SplitRegexp = /#{Form.map { |n| "([0-9a-f]{#{n}})"}.join('')}/
56
+
57
+
58
+ def key
59
+ @state.andtap { |state| state.id }
60
+ end
61
+
62
+ def self.valid?(uuid)
63
+ !!(uuid =~ ValidationRegexp)
64
+ end
65
+
66
+ def self.generate_uuid()
67
+ Generator.generate
68
+ end
69
+
70
+ # Pack a string representation of an uuid to a char(16)
71
+ def self.pack(to_pack)
72
+ [compact(to_pack)].pack("H*")
73
+ end
74
+
75
+ def self.unpack(packed)
76
+ expand(packed.unpack("H*").first)
77
+ end
78
+
79
+ # Convert the string representation of an Uuid to a bignum
80
+ # @param [String] s
81
+ # @return [Bignum]
82
+ def self.string_to_bignum(s)
83
+ compact(s).to_i(16)
84
+ end
85
+
86
+ # Convert a bignum to a string representation
87
+ # @param [Bignum] b
88
+ # @return [String]
89
+ def self.bignum_to_string(b)
90
+ l = Form.inject { |m, n| m+n }
91
+ expand("%0.*x" % [l,b])
92
+ end
93
+
94
+ def self.expand(s)
95
+ match = SplitRegexp.match(s)
96
+ raise InvalidUuidError.new(s) unless match
97
+ match.captures.join('-')
98
+ end
99
+
100
+ def self.compact(s)
101
+ s.tr('-', '')
102
+ end
103
+
104
+ def build_resource(attributes)
105
+ model_class.new(attributes)
106
+ end
107
+ protected :build_resource
108
+
109
+ def uuid
110
+ @uuid ||= UuidResource.generate_uuid
111
+ end
112
+ end
113
+ end
114
+ end
115
+
@@ -0,0 +1,43 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+ require 'common'
3
+
4
+ require 'lims-core/persistence/persistor'
5
+ require 'lims-core/persistence/uuid_resource'
6
+
7
+ module Lims::Core
8
+ module Persistence
9
+ class UuidResource
10
+ class UuidResourcePersistor < Persistence::Persistor
11
+ Model = UuidResource
12
+
13
+ def parents_for(resource)
14
+ resource.state && resource.state.resource ? [resource.state] : []
15
+ end
16
+
17
+ def filter_attributes_on_save(attributes)
18
+ attributes.mash do |k,v|
19
+ case k
20
+ when :model_class then [ k, @session.model_name_for(v) ]
21
+ when :uuid then [ k, @session.pack_uuid(v) ]
22
+ when :state
23
+ [:key, v && v.id]
24
+ else [k, v]
25
+ end
26
+ end
27
+ end
28
+
29
+ def filter_attributes_on_load(attributes)
30
+ attributes.mash do |k,v|
31
+ case k
32
+ when :model_class then [ k, @session.class_for(v) ]
33
+ when :uuid then [ k, @session.unpack_uuid(v) ]
34
+ when :key then [:state, @session.persistor_for(attributes[:model_class]).state_for_id(attributes[:key])]
35
+ else [k, v]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,107 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+
3
+ require 'sequel'
4
+ require 'lims-core/persistence'
5
+ require 'lims-core/persistence/uuid_resource_persistor'
6
+
7
+ module Lims::Core
8
+ module Persistence
9
+ # Add uuid behavior (lookup and creation) to a Session
10
+ module Uuidable
11
+ # lookup one or more objects by uuid or resource_uuid
12
+ # @param [String, Arrary<String>, UUidResource] args
13
+ # @return [Resource, nil, Array<Resource>]
14
+ def [](args)
15
+ case args
16
+ when UuidResource then for_uuid_resource(args)
17
+ when String then for_uuid(args)
18
+ when Array then for_uuids(args)
19
+ else
20
+ super(args)
21
+ end
22
+ end
23
+
24
+ # Retrieve id from an object or a Hash with a uuid key
25
+ # A list of uuids will
26
+ def id_for(object)
27
+ case object
28
+ when Array
29
+ object.map { |o| id_for(o) }
30
+ when Hash
31
+ id_for(object[:uuid] || object["uuid"])
32
+ when String
33
+ # We assume it is an uuid
34
+ self.uuid_resource[:uuid => pack_uuid(object)].andtap { |ur| ur.key }
35
+ else
36
+ super
37
+ end
38
+ end
39
+ # Compute the name (string) used to be saved in the Uuid table.
40
+ # @param [Class] model_class class of the resource
41
+ # @return [String]
42
+ def model_name_for(model_class)
43
+ persistor_name_for(model_class)
44
+ end
45
+
46
+ # Get the class from the class name. Inverse of {#model_name_for}.
47
+ def class_for(model_name)
48
+ persistor_for(model_name).model
49
+ end
50
+
51
+ def new_uuid_resource_for(object)
52
+ UuidResource.new(:state => state_for(object), :model_class => object.class).tap do |r|
53
+ self << r
54
+ end
55
+ end
56
+
57
+ def uuid_resource_for(object)
58
+ state, object = object.is_a?(ResourceState) ? [object, object.resource] : [state_for(object), object]
59
+ self.uuid_resource[:key => state.id, :model_class => model_name_for(object.class)]
60
+ end
61
+
62
+ # Finds the uuid of an object if it exists
63
+ def uuid_for(object)
64
+ # We need to check if the object is managed and have alreday an id
65
+ raise RuntimeError, "Unmanaged object, #{object.inspect}" unless managed?(object)
66
+ state = state_for(object)
67
+ state.uuid_resource.andtap { |ur| return ur.uuid }
68
+ state.id && uuid_resource_for(state).andtap { |r| r.uuid }
69
+ end
70
+
71
+
72
+ # Find or create a uuid for an object
73
+ def uuid_for!(object)
74
+ uuid_for(object) || new_uuid_resource_for(object).uuid
75
+ end
76
+
77
+ # Delete the underlying resource of a UuidResource
78
+ # @param [UuidResource] uuid_resource
79
+ # @return [Id, nil]
80
+ def delete_resource(uuid_resource)
81
+ delete(for_uuid_resource(uuid_resource))
82
+ uuid_resource.key
83
+ end
84
+
85
+
86
+ protected
87
+ # find/load the object referenced by a uuid resource.
88
+ # Don't need to be called explicitely. use session[resource_uuid] instead
89
+ # @param [UuidResource] uuid_resource
90
+ # @return [Resource]
91
+ def for_uuid_resource(uuid_resource)
92
+ persistor_for(uuid_resource.model_class)[uuid_resource.key]
93
+ end
94
+
95
+ def for_uuid(uuid)
96
+ self.uuid_resource[:uuid => uuid].andtap do |r|
97
+ for_uuid_resource(r)
98
+ end
99
+ end
100
+
101
+ # @todo bulk load
102
+ def for_uuids(uuids)
103
+ uuids.map { |u| for_uuid(u) }.compact
104
+ end
105
+ end
106
+ end
107
+ end