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,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
@@ -0,0 +1,5 @@
1
+ module Lims
2
+ module Core
3
+ VERSION = "3.2.3"
4
+ end
5
+ 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
+