lims-core 3.2.3

Sign up to get free protection for your applications and to get access to all the features.
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,147 @@
1
+ module Lims::Core::Persistence::Sequel::Migrations
2
+ module AddAuditTables
3
+ def self.migration(exclude_tables={}, additional_tables_to_update=[])
4
+ [:schema_info, :sessions, :primary_keys].each do |table|
5
+ exclude_tables[table] = true
6
+ end
7
+ this = self
8
+ Proc.new do
9
+ next unless defined?(DB)
10
+ table_names = []
11
+ change do
12
+ if additional_tables_to_update.size == 0
13
+ # Create session table
14
+ create_table :sessions do
15
+ primary_key :id
16
+ String :user
17
+ String :backend_application_id
18
+ String :parameters, :text => true
19
+ boolean :success
20
+ timestamp :start_time
21
+ DateTime :end_time
22
+ end
23
+ end
24
+
25
+ #create migration session
26
+ self << <<-EOS
27
+ INSERT INTO sessions(user, backend_application_id)
28
+ VALUES('admin', 'lims-core');
29
+ EOS
30
+
31
+ session_id = DB[:sessions].order(:id).last[:id]
32
+
33
+ tables_to_update = additional_tables_to_update.size == 0 ? DB.tables : additional_tables_to_update
34
+ tables_to_update.each do |table_name|
35
+ next if exclude_tables.include?(table_name)
36
+ table_names << table_name
37
+ table = DB[table_name]
38
+
39
+ # extend all tables with revision_id
40
+ if DB[table].columns.include?(:revision) == false
41
+ alter_table table_name do
42
+ add_column :revision, Integer, :default => 1
43
+ end
44
+ end
45
+
46
+
47
+ # create history table
48
+ revision_table = "#{table_name}_revision"
49
+ self << <<-EOS
50
+ CREATE TABLE #{revision_table} AS
51
+ SELECT *, 'initial' AS `action`, #{session_id} as session_id
52
+ FROM #{table_name}
53
+ EOS
54
+
55
+ puts "adding key to #{revision_table}"
56
+ alter_table revision_table do
57
+ add_primary_key :internal_id
58
+ add_index [:id, :revision], :unique => true
59
+ add_index [:id, :session_id], :unique => true
60
+ add_foreign_key [:session_id], :sessions, :key => :id
61
+ end
62
+
63
+
64
+ # Create trigger
65
+ trigger_name = "maintain_#{table_name}_on_insert"
66
+ self << "DROP TRIGGER IF EXISTS #{trigger_name};"
67
+ trigger_code = <<-EOT
68
+
69
+ CREATE TRIGGER #{trigger_name} AFTER INSERT ON #{table_name}
70
+ FOR EACH ROW
71
+ BEGIN
72
+ #{this.insert_into_revision(table, revision_table, :insert)}
73
+ EOT
74
+
75
+ puts trigger_code
76
+ self << trigger_code
77
+
78
+ trigger_name = "maintain_#{table_name}_on_update"
79
+ self << "DROP TRIGGER IF EXISTS #{trigger_name};"
80
+ trigger_code = <<-EOT
81
+
82
+ CREATE TRIGGER #{trigger_name} BEFORE UPDATE ON #{table_name}
83
+ FOR EACH ROW
84
+ BEGIN
85
+ # Update the revision number
86
+ SET NEW.revision = OLD.revision+1;
87
+ # Update the revision table
88
+ #{this.insert_into_revision(table, revision_table, :update)}
89
+ EOT
90
+
91
+ puts trigger_code
92
+ self << trigger_code
93
+
94
+ trigger_name = "maintain_#{table_name}_on_delete"
95
+ self << "DROP TRIGGER IF EXISTS #{trigger_name};"
96
+ trigger_code = <<-EOT
97
+
98
+ CREATE TRIGGER #{trigger_name} BEFORE DELETE ON #{table_name}
99
+ FOR EACH ROW
100
+ BEGIN
101
+ #{this.insert_into_revision(table, revision_table, :delete)}
102
+ EOT
103
+
104
+ puts trigger_code
105
+ self << trigger_code
106
+ end
107
+
108
+ if additional_tables_to_update.size > 0
109
+ self << "DROP VIEW IF EXISTS revisions"
110
+ end
111
+
112
+ revision_tables = DB.tables.map { |table| table unless table.match(/revision/) }.compact
113
+ revision_tables -= exclude_tables.keys
114
+
115
+ view_code = "CREATE VIEW revisions AS " + revision_tables.map do |table_name|
116
+ revision_table = "#{table_name}_revision"
117
+ %Q{ SELECT '#{table_name}' AS revision_table,
118
+ id,
119
+ action,
120
+ session_id
121
+ FROM #{revision_table}
122
+
123
+ }
124
+ end.join(' UNION ')
125
+
126
+ puts view_code
127
+ self << view_code
128
+ end
129
+ end
130
+ end
131
+
132
+ def self.insert_into_revision(table, revision_table, type)
133
+ %Q{ INSERT INTO #{revision_table}
134
+ SET #{
135
+ if type == :delete
136
+ 'id = OLD.id, revision = OLD.revision+1'
137
+ else
138
+ table.columns.map { |c| "`#{c}` = NEW.#{c}" }.join(', ')
139
+ end
140
+ },
141
+ `action` = '#{type}',
142
+ `session_id` = @current_session_id;
143
+ END;
144
+ }
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,156 @@
1
+ # This module should not be require on it's own.
2
+ # Require migrations instead.
3
+
4
+ module Lims::Core::Persistence::Sequel::Migrations
5
+ Initial = Proc.new do
6
+ change do
7
+ create_table :samples do
8
+ primary_key :id
9
+ String :name
10
+ end
11
+
12
+ create_table :oligos do
13
+ primary_key :id
14
+ String :sequence
15
+ end
16
+
17
+ create_table :aliquots do
18
+ primary_key :id
19
+ foreign_key :sample_id, :samples, :key => :id
20
+ foreign_key :tag_id, :oligos, :key => :id
21
+ Integer :quantity
22
+ String :type
23
+ end
24
+
25
+ create_table :flowcells do
26
+ primary_key :id
27
+ Integer :number_of_lanes
28
+ end
29
+
30
+ create_table :lanes do
31
+ #primary_key :flowcell_id, :position
32
+ primary_key :id
33
+ foreign_key :flowcell_id, :flowcells, :key => :id
34
+ Integer :position
35
+ foreign_key :aliquot_id, :aliquots, :key => :id
36
+ end
37
+
38
+ create_table :tubes do
39
+ primary_key :id
40
+ end
41
+
42
+ create_table :tube_aliquots do
43
+ primary_key :id
44
+ Integer :tube_id
45
+ foreign_key :aliquot_id, :aliquots, :key => :id
46
+ end
47
+
48
+ create_table :spin_columns do
49
+ primary_key :id
50
+ end
51
+
52
+ create_table :spin_column_aliquots do
53
+ primary_key :id
54
+ Integer :spin_column_id
55
+ foreign_key :aliquot_id, :aliquots, :key => :id
56
+ end
57
+
58
+ create_table :plates do
59
+ primary_key :id
60
+ Integer :number_of_rows
61
+ Integer :number_of_columns
62
+ end
63
+
64
+ create_table :wells do
65
+ primary_key :id
66
+ foreign_key :plate_id, :plates, :key => :id
67
+ Integer :position
68
+ foreign_key :aliquot_id, :aliquots, :key => :id
69
+ end
70
+
71
+ create_table :tube_racks do
72
+ primary_key :id
73
+ Integer :number_of_rows
74
+ Integer :number_of_columns
75
+ end
76
+
77
+ create_table :tube_rack_slots do
78
+ primary_key :id
79
+ foreign_key :tube_rack_id, :tube_racks, :key => :id
80
+ Integer :position
81
+ foreign_key :tube_id, :tubes, :key=> :id
82
+ end
83
+
84
+ create_table :tag_groups do
85
+ primary_key :id
86
+ String :name
87
+ end
88
+
89
+ create_table :tag_group_associations do
90
+ primary_key :id
91
+ foreign_key :tag_group_id, :tag_groups, :key => :id
92
+ Integer :position
93
+ foreign_key :oligo_id, :oligos, :key => :id
94
+ end
95
+
96
+ create_table :uuid_resources do
97
+ primary_key :id
98
+ String :uuid, :fixed => true, :size => 64
99
+ String :model_class
100
+ Integer :key
101
+ end
102
+
103
+ create_table :users do
104
+ primary_key :id
105
+ end
106
+
107
+ create_table :studies do
108
+ primary_key :id
109
+ end
110
+
111
+ create_table :orders do
112
+ primary_key :id
113
+ foreign_key :creator_id, :users, :key => :id
114
+
115
+ String :pipeline
116
+ String :parameters
117
+ String :status
118
+ blob :state
119
+ foreign_key :study_id, :studies, :key => :id
120
+ String :cost_code
121
+ end
122
+
123
+ create_table :items do
124
+ primary_key :id
125
+ foreign_key :order_id, :orders, :key => :id
126
+ String :role
127
+ foreign_key :resource_id, :uuid_resources, :key => :id
128
+ String :uuid, :fixed => true, :size => 64
129
+ String :status
130
+ Integer :iteration, :default => 0
131
+ end
132
+
133
+ create_table :searches do
134
+ primary_key :id
135
+ String :description
136
+ String :filter_type
137
+ String :model
138
+ blob :filter_parameters
139
+ end
140
+
141
+ create_table :labellables do
142
+ primary_key :id
143
+ String :name
144
+ String :type
145
+ end
146
+
147
+ create_table :labels do
148
+ primary_key :id
149
+ foreign_key :labellable_id, :labellables, :key => :id
150
+ String :type
151
+ String :position
152
+ String :value
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,200 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+
3
+ require 'lims-core/persistence/identity_map'
4
+ require 'active_support/inflector'
5
+ require 'lims-core/persistence/sequel/filters'
6
+
7
+ module Lims::Core
8
+ module Persistence
9
+ module Sequel
10
+ # Mixin giving extended the persistor classes with
11
+ # the Sequel (load/save) behavior.
12
+ module Persistor
13
+
14
+ include Filters
15
+
16
+ def self.included(klass)
17
+ klass.class_eval do
18
+ # @return [String] the name of SQL table.
19
+ def self.table_name
20
+ @table_name ||= parent_scope.name.split('::').last.pluralize.snakecase.to_sym
21
+ end
22
+ # The Sequel::Dataset.
23
+ # Corresponds to table.
24
+ # @param [Sequel::Session] session
25
+ # @return [::Sequel::Dataset]
26
+ def self.dataset(session)
27
+ session.database[self.table_name]
28
+ end
29
+ end
30
+ end
31
+
32
+ def initialize(session_or_persistor, dataset=nil, *args, &block )
33
+ id_to_state, object_to_state = [nil, nil]
34
+ case session_or_persistor
35
+ when Sequel::Persistor
36
+ # We link the session and the identity map variables,
37
+ # so that object loaded via this persistor can be found (and their id)
38
+ # through the origial persistor.
39
+ # Hack to get those private variables.
40
+ session, identity_map_parameters = session_or_persistor.instance_eval do
41
+ [@session, [@id_to_state, @object_to_state]]
42
+ end
43
+ super(session, *args, &block)
44
+ @id_to_state , @object_to_state = identity_map_parameters
45
+ else Session
46
+ super(session_or_persistor, *args, &block)
47
+ end
48
+
49
+ @dataset = dataset || self.class.dataset(@session)
50
+ end
51
+
52
+ # @return [String] the name of the table
53
+ def table_name
54
+ self.class.table_name
55
+ end
56
+
57
+
58
+ # The Sequel::Dataset.
59
+ # Corresponds to a table.
60
+ # @return [::Sequel::Dataset]
61
+ def dataset
62
+ @dataset
63
+ end
64
+
65
+
66
+ # Returns the number of object in the store
67
+ # @return [Fixnum]
68
+ def count
69
+ dataset.count
70
+ end
71
+
72
+ protected
73
+ # load a slice.
74
+ def for_each_in_slice(start, length)
75
+ return if length == 0
76
+ dataset.order(primary_key).limit(length, start).each do |h|
77
+ yield(h)
78
+ end
79
+ end
80
+
81
+ # The primary key
82
+ # @return [Symbol]
83
+ def primary_key()
84
+ :id
85
+ end
86
+
87
+ def qualified_key
88
+ @qualified_key ||= ::Sequel.qualify(self.class.table_name, primary_key)
89
+ end
90
+
91
+ def bulk_load(ids, *params, &block)
92
+ dataset.filter(qualified_key => ids.map(&:id)).all(&block)
93
+ end
94
+ public :bulk_load
95
+
96
+ def ids_for(criteria)
97
+ dataset.select(qualified_key).filter(criteria).map { |h| h[primary_key] }
98
+
99
+ end
100
+
101
+ # Save a raw object, i.e. the object
102
+ # attributes excluding any associations.
103
+ # @param [Resource] object the object
104
+ # @return [Fixnum, nil] the Id if save successful
105
+ def insert(state, *params)
106
+ # use prepared statement for everything
107
+ # We only need it at the moment as a workaround for saving the UUID
108
+ # So we might in the future either move it to a UuidResourcePersistor
109
+ # or cached it by attributes
110
+ # @todo benchmark against normal insert
111
+ attributes = filter_attributes_on_save(state.resource.attributes, *params)
112
+ dataset.insert(attributes).tap { |id| state.id = id }
113
+ end
114
+
115
+ def bulk_insert(states, *params)
116
+ #super(states, *params)
117
+ bulk_insert_multi(states, *params)
118
+ #bulk_insert_prepared(states, *params)
119
+ end
120
+ public :bulk_insert
121
+
122
+ def bulk_insert_prepared(states, *params)
123
+ # use prepared statement for everything
124
+ # We only need it at the moment as a workaround for saving the UUID
125
+ # So we might in the future either move it to a UuidResourcePersistor
126
+ # or cached it by attributes
127
+ # @todo benchmark against normal insert
128
+ attributes = filter_attributes_on_save(states.first.resource.attributes, *params)
129
+ statement_name = :"#{table_name}__save_raw"
130
+ dataset.prepare(:insert, statement_name, attributes.keys.mash { |k| [k, :"$#{k}"] })
131
+
132
+ states.each do |state|
133
+ attributes = filter_attributes_on_save(state.resource.attributes, *params)
134
+ @session.database.call(statement_name, attributes)
135
+ end
136
+ end
137
+
138
+ # @todo
139
+ def bulk_insert_multi(states, *params)
140
+ free_ids = get_next_available_ids(states.size)
141
+ states.inject(0) { |i,s| s.id = free_ids[i]; i+1 }
142
+ attributes = states.map { |state| filter_attributes_on_save(state.resource.attributes, *params).merge(primary_key => state.id) }
143
+ dataset.multi_insert(attributes)
144
+ end
145
+
146
+ def bulk_update_raw_attributes(attributes, *params)
147
+ return dataset.on_duplicate_key_update.multi_insert(attributes) if dataset.respond_to? :on_duplicate_key_update
148
+ attributes.each { |att| dataset.filter(primary_key => att.delete(primary_key)).update(att) }
149
+ end
150
+
151
+ def bulk_delete_raw(ids, *params)
152
+ dataset.filter(primary_key => ids).delete
153
+ end
154
+
155
+ def delete_raw(objec, id, *params)
156
+ id.tap do
157
+ dataset.filter(primary_key => id).delete
158
+ end
159
+ end
160
+
161
+ # Return a sequence of free ids, ready to be inserted.
162
+ # The last used id corresponding to each table is store in a special table.
163
+ # We need to lock the table to avoid to thread or process to 'use' the same ids.
164
+ # @param [Integer] quantity
165
+ # @return [Array<Integer>]
166
+ def get_next_available_ids(quantity = 1)
167
+ @session.lock(dataset.from(:primary_keys), true) do |primary_keys|
168
+ current_key_row = primary_keys.first(:table_name => table_name.to_s)
169
+ if current_key_row
170
+ current_key = current_key_row[:current_key]
171
+ else
172
+ # We lock again primary keys and the dataset, because doing a new MySQL lock
173
+ # unlock the previous locked tables.
174
+ # Also we need to check again at this point if the primary_keys table has not
175
+ # been updated by another request. In fact, doing the lock below unlock first
176
+ # primary_keys table, and then lock it again with dataset table. If during this laps
177
+ # of time (between unlock and lock), another request try to initialize primary_keys table,
178
+ # we could have duplicate rows.
179
+ current_key = @session.lock([primary_keys, dataset]) do |primary_keys_dataset, resource_dataset|
180
+ primary_key_row = primary_keys_dataset.first(:table_name => table_name.to_s)
181
+ if primary_key_row
182
+ primary_key_row[:current_key]
183
+ else
184
+ last_id = resource_dataset.max(primary_key) || 0
185
+ primary_keys_dataset.insert(:table_name => table_name.to_s, :current_key => last_id)
186
+ last_id
187
+ end
188
+ end
189
+ end
190
+
191
+ new_current_key = current_key + quantity
192
+ primary_keys.where(:table_name => table_name.to_s).update(:current_key => new_current_key)
193
+ (current_key+1..new_current_key).to_a
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ require 'lims-core/persistence/sequel/filters'