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.
- checksums.yaml +15 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.rvmrc +2 -0
- data/.travis.yml +2 -0
- data/.vimrc +27 -0
- data/.yard_templates/default/layout/html/footer.erb +0 -0
- data/.yardopts +1 -0
- data/Gemfile +54 -0
- data/Gemfile.lock +197 -0
- data/Guardfile +21 -0
- data/Guardfile.tmux +28 -0
- data/README.markdown +67 -0
- data/Rakefile +16 -0
- data/config/database.yml +16 -0
- data/doc/Array.html +116 -0
- data/doc/Array/ArrayLoggerPersistor.html +152 -0
- data/doc/Lims.html +114 -0
- data/doc/Lims/Core.html +178 -0
- data/doc/Lims/Core/Action.html +91 -0
- data/doc/Lims/Core/Actions.html +116 -0
- data/doc/Lims/Core/Actions/Action.html +216 -0
- data/doc/Lims/Core/Actions/Action/AfterEval.html +853 -0
- data/doc/Lims/Core/Actions/Action/InvalidParameters.html +268 -0
- data/doc/Lims/Core/Actions/ActionGroup.html +196 -0
- data/doc/Lims/Core/Actions/ActionGroup/AfterEval.html +315 -0
- data/doc/Lims/Core/Actions/BulkAction.html +224 -0
- data/doc/Lims/Core/Actions/BulkAction/AfterEval.html +253 -0
- data/doc/Lims/Core/Actions/TestActionGroup.html +101 -0
- data/doc/Lims/Core/Actions/TestActionGroup/Action.html +133 -0
- data/doc/Lims/Core/Actions/TestActionGroup/ActionGroup.html +127 -0
- data/doc/Lims/Core/Base.html +287 -0
- data/doc/Lims/Core/Base/AccessibleViaSuper.html +252 -0
- data/doc/Lims/Core/Base/ClassMethod.html +177 -0
- data/doc/Lims/Core/Base/HashString.html +177 -0
- data/doc/Lims/Core/Base/IsArrayOf.html +606 -0
- data/doc/Lims/Core/Base/State.html +130 -0
- data/doc/Lims/Core/Organization.html +113 -0
- data/doc/Lims/Core/Organization/Batch.html +106 -0
- data/doc/Lims/Core/Persistence.html +267 -0
- data/doc/Lims/Core/Persistence/ComparisonFilter.html +318 -0
- data/doc/Lims/Core/Persistence/Filter.html +252 -0
- data/doc/Lims/Core/Persistence/IdentityMap.html +409 -0
- data/doc/Lims/Core/Persistence/IdentityMap/Class.html +144 -0
- data/doc/Lims/Core/Persistence/IdentityMap/DuplicateError.html +126 -0
- data/doc/Lims/Core/Persistence/IdentityMap/DuplicateIdError.html +136 -0
- data/doc/Lims/Core/Persistence/IdentityMap/DuplicateObjectError.html +136 -0
- data/doc/Lims/Core/Persistence/IdentityMapClass.html +133 -0
- data/doc/Lims/Core/Persistence/Logger.html +105 -0
- data/doc/Lims/Core/Persistence/Logger/Persistor.html +334 -0
- data/doc/Lims/Core/Persistence/Logger/Session.html +452 -0
- data/doc/Lims/Core/Persistence/Logger/Store.html +470 -0
- data/doc/Lims/Core/Persistence/MessageBus.html +871 -0
- data/doc/Lims/Core/Persistence/MessageBus/ConnectionError.html +123 -0
- data/doc/Lims/Core/Persistence/MessageBus/InvalidSettingsError.html +122 -0
- data/doc/Lims/Core/Persistence/MultiCriteriaFilter.html +293 -0
- data/doc/Lims/Core/Persistence/PersistAssociationTrait.html +91 -0
- data/doc/Lims/Core/Persistence/PersistableTrait.html +91 -0
- data/doc/Lims/Core/Persistence/Persistor.html +3072 -0
- data/doc/Lims/Core/Persistence/Persistor/DuplicateError.html +205 -0
- data/doc/Lims/Core/Persistence/Persistor/DuplicateIdError.html +147 -0
- data/doc/Lims/Core/Persistence/Persistor/DuplicateObjectError.html +147 -0
- data/doc/Lims/Core/Persistence/PersistorTrait.html +91 -0
- data/doc/Lims/Core/Persistence/ResourceState.html +1738 -0
- data/doc/Lims/Core/Persistence/Search.html +269 -0
- data/doc/Lims/Core/Persistence/Search/CreateSearch.html +251 -0
- data/doc/Lims/Core/Persistence/Search/SearchPersistor.html +240 -0
- data/doc/Lims/Core/Persistence/Search/SearchSequelPersistor.html +396 -0
- data/doc/Lims/Core/Persistence/Sequel.html +117 -0
- data/doc/Lims/Core/Persistence/Sequel/Filters.html +462 -0
- data/doc/Lims/Core/Persistence/Sequel/ForTest.html +101 -0
- data/doc/Lims/Core/Persistence/Sequel/ForTest/Name.html +137 -0
- data/doc/Lims/Core/Persistence/Sequel/ForTest/Name/NamePersitor.html +143 -0
- data/doc/Lims/Core/Persistence/Sequel/Migrations.html +266 -0
- data/doc/Lims/Core/Persistence/Sequel/Persistor.html +665 -0
- data/doc/Lims/Core/Persistence/Sequel/Session.html +501 -0
- data/doc/Lims/Core/Persistence/Sequel/Store.html +417 -0
- data/doc/Lims/Core/Persistence/Session.html +2751 -0
- data/doc/Lims/Core/Persistence/Session/UnmanagedObjectError.html +111 -0
- data/doc/Lims/Core/Persistence/StateGroup.html +696 -0
- data/doc/Lims/Core/Persistence/StateList.html +498 -0
- data/doc/Lims/Core/Persistence/Store.html +695 -0
- data/doc/Lims/Core/Persistence/UuidResource.html +1044 -0
- data/doc/Lims/Core/Persistence/UuidResource/InvalidUuidError.html +111 -0
- data/doc/Lims/Core/Persistence/UuidResource/UuidResourcePersistor.html +337 -0
- data/doc/Lims/Core/Persistence/Uuidable.html +799 -0
- data/doc/Lims/Core/Persistor.html +320 -0
- data/doc/Lims/Core/Resource.html +165 -0
- data/doc/Object.html +228 -0
- data/doc/SessionSpec.html +101 -0
- data/doc/SessionSpec/Model.html +279 -0
- data/doc/SessionSpec/Model/ModelPersistor.html +327 -0
- data/doc/_index.html +732 -0
- data/doc/class_list.html +47 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +55 -0
- data/doc/css/style.css +322 -0
- data/doc/file.README.html +127 -0
- data/doc/file_list.html +49 -0
- data/doc/frames.html +13 -0
- data/doc/index.html +127 -0
- data/doc/js/app.js +205 -0
- data/doc/js/full_list.js +167 -0
- data/doc/js/jquery.js +16 -0
- data/doc/method_list.html +1894 -0
- data/doc/top-level-namespace.html +100 -0
- data/lib/common.rb +18 -0
- data/lib/lims-core.rb +29 -0
- data/lib/lims-core/actions.rb +10 -0
- data/lib/lims-core/actions/action.rb +185 -0
- data/lib/lims-core/actions/action_group.rb +54 -0
- data/lib/lims-core/actions/bulk_action.rb +65 -0
- data/lib/lims-core/base.rb +132 -0
- data/lib/lims-core/helpers.rb +41 -0
- data/lib/lims-core/persistence.rb +15 -0
- data/lib/lims-core/persistence/comparison_filter.rb +54 -0
- data/lib/lims-core/persistence/filter.rb +23 -0
- data/lib/lims-core/persistence/identity_map.rb +55 -0
- data/lib/lims-core/persistence/logger/all.rb +5 -0
- data/lib/lims-core/persistence/logger/persistor.rb +35 -0
- data/lib/lims-core/persistence/logger/session.rb +30 -0
- data/lib/lims-core/persistence/logger/store.rb +37 -0
- data/lib/lims-core/persistence/message_bus.rb +131 -0
- data/lib/lims-core/persistence/multi_criteria_filter.rb +50 -0
- data/lib/lims-core/persistence/persist_association_trait.rb +96 -0
- data/lib/lims-core/persistence/persistable_trait.rb +150 -0
- data/lib/lims-core/persistence/persistor.rb +495 -0
- data/lib/lims-core/persistence/resource_state.rb +157 -0
- data/lib/lims-core/persistence/search.rb +3 -0
- data/lib/lims-core/persistence/search/all.rb +3 -0
- data/lib/lims-core/persistence/search/create_search.rb +55 -0
- data/lib/lims-core/persistence/search/search_persistor.rb +45 -0
- data/lib/lims-core/persistence/search/search_sequel_persistor.rb +40 -0
- data/lib/lims-core/persistence/sequel.rb +14 -0
- data/lib/lims-core/persistence/sequel/filters.rb +106 -0
- data/lib/lims-core/persistence/sequel/migrations.rb +14 -0
- data/lib/lims-core/persistence/sequel/migrations/add_audit_tables.rb +147 -0
- data/lib/lims-core/persistence/sequel/migrations/initial.rb +156 -0
- data/lib/lims-core/persistence/sequel/persistor.rb +200 -0
- data/lib/lims-core/persistence/sequel/session.rb +136 -0
- data/lib/lims-core/persistence/sequel/store.rb +37 -0
- data/lib/lims-core/persistence/session.rb +409 -0
- data/lib/lims-core/persistence/state_group.rb +97 -0
- data/lib/lims-core/persistence/state_list.rb +56 -0
- data/lib/lims-core/persistence/store.rb +73 -0
- data/lib/lims-core/persistence/uuid_resource.rb +115 -0
- data/lib/lims-core/persistence/uuid_resource_persistor.rb +43 -0
- data/lib/lims-core/persistence/uuidable.rb +107 -0
- data/lib/lims-core/resource.rb +21 -0
- data/lib/lims-core/subclass_tracker.rb +30 -0
- data/lib/lims-core/version.rb +5 -0
- data/lims-core.gemspec +40 -0
- data/makefile +52 -0
- data/showoff/core-2012-06-11/core/01_slide.md +237 -0
- data/showoff/core-2012-06-11/core/02_slide.md +110 -0
- data/showoff/core-2012-06-11/custom.css +44 -0
- data/showoff/core-2012-06-11/main/01_slide.md +53 -0
- data/showoff/core-2012-06-11/showoff.json +10 -0
- data/showoff/core-2012-06-11/tp1.tpl +1 -0
- data/spec/actions/action_group_spec.rb +39 -0
- data/spec/actions/spec_helper.rb +1 -0
- data/spec/persistence/identity_map_spec.rb +55 -0
- data/spec/persistence/logger/spec_helper.rb +7 -0
- data/spec/persistence/logger/store_spec.rb +48 -0
- data/spec/persistence/message_bus_spec.rb +76 -0
- data/spec/persistence/sequel/session_spec.rb +125 -0
- data/spec/persistence/sequel/spec_helper.rb +39 -0
- data/spec/persistence/sequel/store_shared.rb +25 -0
- data/spec/persistence/sequel/store_spec.rb +22 -0
- data/spec/persistence/session_spec.rb +199 -0
- data/spec/persistence/spec_helper.rb +2 -0
- data/spec/persistence/uuid_resource_spec.rb +80 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/subclass_tracker_sperc.rb +62 -0
- data/utils/constant_tree.rb +29 -0
- data/utils/stack.rb +48 -0
- 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'
|