lims-core 3.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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,21 @@
|
|
1
|
+
# vi: ts=2 sts=2 et sw=2 spell spelllang=en
|
2
|
+
require 'common'
|
3
|
+
require 'lims-core/base'
|
4
|
+
require 'lims-core/subclass_tracker'
|
5
|
+
|
6
|
+
module Lims::Core
|
7
|
+
module Resource
|
8
|
+
extend SubclassTracker
|
9
|
+
# We need to rename the actual self.included method
|
10
|
+
class << self
|
11
|
+
alias_method :tracker_included, :included
|
12
|
+
end
|
13
|
+
def self.included(klass)
|
14
|
+
klass.class_eval do
|
15
|
+
include Base
|
16
|
+
end
|
17
|
+
tracker_included(klass)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# vi: ts=2 sts=2 et sw=2 spell spelllang=en
|
2
|
+
# This module allows a class or module to track
|
3
|
+
# all of its descendants, i.e. being able to iterate
|
4
|
+
# all over all subclasses (submodules) of the tracking class.
|
5
|
+
# To do so, just include the following in the tracking class/module
|
6
|
+
# then call ::subclasses.
|
7
|
+
#
|
8
|
+
|
9
|
+
module Lims
|
10
|
+
module Core
|
11
|
+
module SubclassTracker
|
12
|
+
def self.extended(klass)
|
13
|
+
(class <<klass; self; end).send :attr_accessor, :subclasses
|
14
|
+
(class <<klass; self; end).send :define_method, :inherited do |subclass|
|
15
|
+
klass.subclasses << subclass
|
16
|
+
super(subclass)
|
17
|
+
end
|
18
|
+
(class <<klass; self; end).send :define_method, :included do |submodule|
|
19
|
+
klass.subclasses << submodule
|
20
|
+
(class <<submodule; self; end).send :define_method, :inherited do |subclass|
|
21
|
+
klass.subclasses << subclass
|
22
|
+
super(subclass)
|
23
|
+
end
|
24
|
+
super(submodule)
|
25
|
+
end
|
26
|
+
klass.subclasses = []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lims-core.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "lims-core/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "lims-core"
|
7
|
+
s.version = Lims::Core::VERSION
|
8
|
+
s.authors = ["Maxime Bourget"]
|
9
|
+
s.email = ["mb14@sanger.ac.uk"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Core of new LIMS system}
|
12
|
+
s.description = %q{Provide the core classes and persistence needed to build an API used by the pipelines applications.}
|
13
|
+
s.description = %q{provides all the necessary to interact with the core database}
|
14
|
+
|
15
|
+
s.rubyforge_project = "lims-core"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# specify any dependencies here; for example:
|
23
|
+
# s.add_development_dependency "rspec"
|
24
|
+
# s.add_runtime_dependency "rest-client"
|
25
|
+
s.add_dependency('facets', '2.9.3')
|
26
|
+
s.add_dependency('virtus', '0.2.0')
|
27
|
+
s.add_dependency('aequitas')
|
28
|
+
s.add_dependency('modularity', '0.6.1')
|
29
|
+
s.add_dependency('dm-validations')
|
30
|
+
s.add_dependency('sequel')
|
31
|
+
s.add_dependency('activesupport')
|
32
|
+
s.add_dependency('uuid')
|
33
|
+
s.add_dependency('state_machine')
|
34
|
+
s.add_dependency('bunny', '0.9.0.pre10')
|
35
|
+
|
36
|
+
|
37
|
+
#development
|
38
|
+
s.add_development_dependency('rspec', '~> 2')
|
39
|
+
s.add_development_dependency('rake')
|
40
|
+
end
|
data/makefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Yes we could have use rake, but rake is so slow and verbose ...
|
2
|
+
# Look at this as a collection of shell commands.
|
3
|
+
|
4
|
+
.PHONY: migrate_test test doc clean clean_tree profile
|
5
|
+
|
6
|
+
# launch rspec
|
7
|
+
test:
|
8
|
+
bundle exec rspec
|
9
|
+
|
10
|
+
focus:
|
11
|
+
bundle exec rspec -tfocus
|
12
|
+
|
13
|
+
profile:
|
14
|
+
bundle exec rspec -p
|
15
|
+
|
16
|
+
# generate yard doc
|
17
|
+
doc:
|
18
|
+
bundle exec yardoc
|
19
|
+
|
20
|
+
# migrate test database
|
21
|
+
migrate_test:
|
22
|
+
bundle exec sequel -m db/migrations -e test config/database.yml
|
23
|
+
|
24
|
+
# Start server for core presentation
|
25
|
+
show:
|
26
|
+
bundle exec showoff serve showoff/core-2012-06-11
|
27
|
+
|
28
|
+
# console
|
29
|
+
console:
|
30
|
+
bundle exec sequel -e test config/database.yml
|
31
|
+
|
32
|
+
|
33
|
+
# generate constants tree of a starting required file
|
34
|
+
constants:
|
35
|
+
|
36
|
+
%.tree:
|
37
|
+
bundle exec ruby utils/constant_tree.rb $* | tee $@
|
38
|
+
|
39
|
+
core.tree: \ .tree
|
40
|
+
mv $< $@
|
41
|
+
|
42
|
+
clean_tree:
|
43
|
+
rm *.tree
|
44
|
+
|
45
|
+
clean: clean_tree
|
46
|
+
|
47
|
+
guard:
|
48
|
+
bundle exec guard
|
49
|
+
|
50
|
+
guardt:
|
51
|
+
bundle exec guard start Guardfile.tmux
|
52
|
+
|
@@ -0,0 +1,237 @@
|
|
1
|
+
!SLIDE smaller transition=scrollLeft
|
2
|
+
# `lims::core`
|
3
|
+
### A set of dependent components
|
4
|
+
|
5
|
+
* API : `Actions`
|
6
|
+
* Persistence : `Session`
|
7
|
+
* Domain: `Laboratoy`, `LabProcess`, `Organization`
|
8
|
+
|
9
|
+
### API => Persistence => Domains.
|
10
|
+
!SLIDE small transition=scrollUp subsection
|
11
|
+
# Lims::core
|
12
|
+
## Principles
|
13
|
+
* Models exists without the notion of persistence
|
14
|
+
* Everything created within a `Session` is saved (at the end)
|
15
|
+
* Everything manipulating objects is done through *external* `Actions`.
|
16
|
+
|
17
|
+
!SLIDE small transition=scrollUp subsection
|
18
|
+
# Lims::core
|
19
|
+
## Foundation #
|
20
|
+
### The lims::core is based on 3 gems :
|
21
|
+
|
22
|
+
* `Facets` ruby extensions.
|
23
|
+
* `Datamapper2`
|
24
|
+
* `Virtus` - model definition
|
25
|
+
* <span class="red">`Aequitas`</span>- validation library
|
26
|
+
* `Sequel` '*The database toolkit for ruby*'.
|
27
|
+
|
28
|
+
.notes they both provide a full active record like
|
29
|
+
|
30
|
+
!SLIDE code transition=scrollLeft smaller subsection
|
31
|
+
# Foundation
|
32
|
+
## Virtus, Aequitas
|
33
|
+
|
34
|
+
@@@ ruby
|
35
|
+
class Sample
|
36
|
+
include Virtus
|
37
|
+
include Aequitas
|
38
|
+
attribute :name, String, :required => true
|
39
|
+
end
|
40
|
+
|
41
|
+
s = Sample.new
|
42
|
+
s.valid? #=> false
|
43
|
+
|
44
|
+
s = Sample.new(:name => "my sample")
|
45
|
+
s.name #=> "my sample"
|
46
|
+
s.attributes #=> { :name => "my sample" }
|
47
|
+
|
48
|
+
!SLIDE code transition=scrollUp smaller subsection
|
49
|
+
# Foundation
|
50
|
+
## Sequel #
|
51
|
+
|
52
|
+
@@@ ruby
|
53
|
+
samples = DB[:samples] #=> proxy
|
54
|
+
|
55
|
+
samples.first #=> { :id => 1, :name => 'my sample' }
|
56
|
+
|
57
|
+
with_aliquots = samples.join(:aliquots, :sample_id => :id)
|
58
|
+
#=> proxy
|
59
|
+
|
60
|
+
samples_with_aliquots.first
|
61
|
+
#=> { :sample_id => 1, :name => 'my sample', :id => 3)
|
62
|
+
|
63
|
+
!SLIDE smaller transition=scrollRight subsection
|
64
|
+
# Persistence-less model
|
65
|
+
## Plate declaration
|
66
|
+
|
67
|
+
@@@ ruby
|
68
|
+
class Plate
|
69
|
+
include Resource
|
70
|
+
# declare row_number and column_number
|
71
|
+
%w(row column).each do |w|
|
72
|
+
attribute :"#{w}_number", Fixnum, :required => true,
|
73
|
+
:gte => 0, :writer => :private
|
74
|
+
end
|
75
|
+
|
76
|
+
class Well
|
77
|
+
include Resource
|
78
|
+
...
|
79
|
+
end
|
80
|
+
|
81
|
+
is_array_of Well do |p,t|
|
82
|
+
(p.row_number*p.column_number).times.map { t.new }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
### Plate is like an Array
|
86
|
+
|
87
|
+
!SLIDE code smaller transition=scrollUp subsection
|
88
|
+
# Persistence-less model
|
89
|
+
## Plate spec
|
90
|
+
|
91
|
+
@@@ ruby
|
92
|
+
describe Plate do
|
93
|
+
subject { Plate.new(:row_number => 8,
|
94
|
+
:column_number => 12) }
|
95
|
+
|
96
|
+
it "can be indexed with a symbol " do
|
97
|
+
subject[:B3].should be_a(Plate::Well)
|
98
|
+
aliquot = mock(:aliquot)
|
99
|
+
|
100
|
+
subject[:B3] << aliquot
|
101
|
+
subject[:B3].should include(aliquot)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
### Well is like an array too.
|
106
|
+
|
107
|
+
|
108
|
+
!SLIDE small transition=scrollLeft
|
109
|
+
# Session
|
110
|
+
|
111
|
+
* A session is in charge of restoring and saving objects to a `Store`.
|
112
|
+
* Every modifications within a session **block** is saved.
|
113
|
+
* Everything is saved only once.
|
114
|
+
* <span class="red">It's the only way to save an object</span>
|
115
|
+
* It provides an **IdentityMap**.
|
116
|
+
|
117
|
+
!SLIDE small transition=scrollUp subsection
|
118
|
+
# Session
|
119
|
+
## Benefits #
|
120
|
+
|
121
|
+
* Persistence operations not mixed with domain model.
|
122
|
+
* Everything is automatically wrapped in a transaction.
|
123
|
+
* Saves can be bulk (not implemented)
|
124
|
+
* Eager loading (not implemented) can be set at session, or application level
|
125
|
+
* Session information (user, date, client) can be audited in a `session` table
|
126
|
+
|
127
|
+
!SLIDE small transition=scrollUp subsection
|
128
|
+
# Session #
|
129
|
+
## Purpose ##
|
130
|
+
### Sessions have been designed to make some stuff hard for the developer as:
|
131
|
+
|
132
|
+
* Load and save object(s) in different sessions.
|
133
|
+
* Save an object in the middle of an *action* (method, function, ...).
|
134
|
+
* User can't create a Session without a store
|
135
|
+
|
136
|
+
### If you find yourself struggling trying to do something above, you **probably **shouldn't be doing it.
|
137
|
+
|
138
|
+
|
139
|
+
|
140
|
+
!SLIDE smaller subsection transition=scrollLeft
|
141
|
+
# Session #
|
142
|
+
## Example
|
143
|
+
|
144
|
+
@@@ ruby
|
145
|
+
source_id = ...
|
146
|
+
# Session can't be created on its own
|
147
|
+
store.with_session(params) do |s|
|
148
|
+
# load a plate
|
149
|
+
source_plate = s.plate[source_id]
|
150
|
+
|
151
|
+
# create a new plate and add it to the session
|
152
|
+
s << target_plate= L::Plate.new(plate.attributes)
|
153
|
+
|
154
|
+
# transfer from one well to the another well
|
155
|
+
# modifies BOTH plates
|
156
|
+
target_plate[:A1] << source_plate[:C3].take(0.5)
|
157
|
+
|
158
|
+
end # saves source and target
|
159
|
+
|
160
|
+
|
161
|
+
!SLIDE small subsection transition=scrollUp
|
162
|
+
# Session
|
163
|
+
## API
|
164
|
+
|
165
|
+
* Session needs to be created from a store :
|
166
|
+
`store.with_session { |s| .... }`
|
167
|
+
* Tells a session to manage an object :
|
168
|
+
` session << object_to_manage`
|
169
|
+
* Load a object :
|
170
|
+
`session.plate[plate_id]`
|
171
|
+
* Get the id of an object :
|
172
|
+
`session.id_for(plate)`
|
173
|
+
|
174
|
+
!SLIDE small subsection transition=scrollUp
|
175
|
+
# Session
|
176
|
+
## Objects no managed are not saved
|
177
|
+
|
178
|
+
### The following code saves only one plate.
|
179
|
+
|
180
|
+
@@@ ruby
|
181
|
+
store.with_session do |s|
|
182
|
+
plate1 = new_plate
|
183
|
+
s << plate2=new_plate
|
184
|
+
end
|
185
|
+
|
186
|
+
### `plate2`.
|
187
|
+
|
188
|
+
!SLIDE small subsection transition=scrollUp
|
189
|
+
# Session
|
190
|
+
## Object loaded are automatically managed
|
191
|
+
|
192
|
+
@@@ ruby
|
193
|
+
store.with_session do |s|
|
194
|
+
plate = s.plate[plate_id]
|
195
|
+
# s << plate - unnecessary
|
196
|
+
end
|
197
|
+
|
198
|
+
!SLIDE small subsection transition=scrollUp
|
199
|
+
# Session
|
200
|
+
## Post save block
|
201
|
+
### Objects are saved at the end of a `with_session` block, but ids of new objects are only available after the block, when the session doesn't exist anymore.
|
202
|
+
|
203
|
+
@@@ ruby
|
204
|
+
store.with_session do |s|
|
205
|
+
s << new_plate = Plate.new(params)
|
206
|
+
{:new_plate => s.id_for(new_plate)}.to_json
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
### Doesn't work.
|
211
|
+
|
212
|
+
!SLIDE small subsection transition=shuffle
|
213
|
+
# Session / Post save block
|
214
|
+
## returning the session ...
|
215
|
+
|
216
|
+
@@@ ruby
|
217
|
+
session, new_plate = store.with_session do |s|
|
218
|
+
s << new_plate = Plate.new(params)
|
219
|
+
[ s, new_plate ]
|
220
|
+
end
|
221
|
+
|
222
|
+
{:new_plate => session.id_for(new_plate}.to_json
|
223
|
+
|
224
|
+
### Would probably work, but splits the block in 2 different places. Probably one of those things session has been to designed to make hard.
|
225
|
+
|
226
|
+
|
227
|
+
!SLIDE smaller subsection transition=shuffle
|
228
|
+
# Session / Post save block
|
229
|
+
## Using a lambda
|
230
|
+
|
231
|
+
@@@ ruby
|
232
|
+
store.with_session do |s|
|
233
|
+
s << new_plate = Plate.new(params)
|
234
|
+
lambda { {:new_plate => .id_for(new_plate)}.to_json }
|
235
|
+
end.call
|
236
|
+
|
237
|
+
### Better, all the code is inside one block and the `lambda` is saying 'I am a future statement'. Note the `call` at the end.
|
@@ -0,0 +1,110 @@
|
|
1
|
+
!SLIDE small transition=scrollLeft
|
2
|
+
# Actions #
|
3
|
+
## A *reversible* functor:
|
4
|
+
|
5
|
+
* Has parameters.
|
6
|
+
* Can be called.
|
7
|
+
* Can be reverted (when possible).
|
8
|
+
|
9
|
+
### corresponding to an API call.
|
10
|
+
|
11
|
+
!SLIDE small transition=scrollUp subsection
|
12
|
+
# Actions #
|
13
|
+
* Persistence aware
|
14
|
+
* Called as-is by the API server.
|
15
|
+
* Can be pipeline specific.
|
16
|
+
* Executed within a `Session` and
|
17
|
+
* ... a context (user, application).
|
18
|
+
|
19
|
+
|
20
|
+
!SLIDE small transition=scrollUp subsection
|
21
|
+
# Actions
|
22
|
+
## How to call an action
|
23
|
+
|
24
|
+
1. Pass context parameters (store, user, application),
|
25
|
+
2. pass a parameters initializer block,
|
26
|
+
3. call it.
|
27
|
+
|
28
|
+
!SLIDE smaller subsection transition=scrollLeft
|
29
|
+
# Actions / call
|
30
|
+
## Example
|
31
|
+
@@@ ruby
|
32
|
+
target_id = ...
|
33
|
+
source_id = ...
|
34
|
+
action = PlateTransfer.new(:store => store,
|
35
|
+
:user => user,
|
36
|
+
:application => application
|
37
|
+
) do |action, session|
|
38
|
+
# session needed
|
39
|
+
action.target = session.plate[target_id]
|
40
|
+
action.source = session.plate[source_id]
|
41
|
+
|
42
|
+
action.transfer_map = { :C3 => :B1 }
|
43
|
+
end
|
44
|
+
|
45
|
+
action.call
|
46
|
+
|
47
|
+
### This also separates parameter decoding from implementation, allowing
|
48
|
+
parameters validation.
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
!SLIDE small transition=scrollRight subsection
|
53
|
+
# Actions
|
54
|
+
## Design
|
55
|
+
* Include `Action`.
|
56
|
+
* Declare parameters (attributes).
|
57
|
+
* Implement `_call_in_session` to modify/create the model.
|
58
|
+
* Attributes and results are automatically managed.
|
59
|
+
|
60
|
+
!SLIDE smaller transition=scrollUp subsection
|
61
|
+
# Actions / Design
|
62
|
+
## PlateTransfer Action
|
63
|
+
@@@ ruby
|
64
|
+
class PlateTransfer
|
65
|
+
include Action
|
66
|
+
|
67
|
+
attribute :source, Laboratory::Plate, :required => true
|
68
|
+
attribute :target, Laboratory::Plate, :required => true
|
69
|
+
attribute :transfer_map, Hash, :required => true
|
70
|
+
|
71
|
+
# Transfers the content of from source to target
|
72
|
+
# according to a transef map.
|
73
|
+
def _call_in_session(session)
|
74
|
+
transfer_map.each do |from ,to|
|
75
|
+
target[to] << source[from].take
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
!SLIDE small transition=scrollRight
|
82
|
+
# Done
|
83
|
+
|
84
|
+
* Laboratory classes needed for pulldown.
|
85
|
+
* Persistence on Laboratory.
|
86
|
+
* SQL store and basic Persistors.
|
87
|
+
* Transfer, Create Plate, Tube, and Tag action.
|
88
|
+
|
89
|
+
!SLIDE small transition=scrollLeft
|
90
|
+
# To do
|
91
|
+
* Manage `dirty` attributes
|
92
|
+
* Integration with API server
|
93
|
+
* Add samples
|
94
|
+
* Add studies, projects
|
95
|
+
* Submissions, orders
|
96
|
+
|
97
|
+
!SLIDE small transition=scrollUp subsection
|
98
|
+
# To do
|
99
|
+
## wish list
|
100
|
+
|
101
|
+
* Audit session
|
102
|
+
* Eager loading
|
103
|
+
* Bulk saving
|
104
|
+
* Manage automatically relationship throug attributes.
|
105
|
+
|
106
|
+
!SLIDE transition=shuffle
|
107
|
+
# The End
|
108
|
+
Any Questions ?
|
109
|
+
|
110
|
+
|