lims-core 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/.rvmrc +2 -0
  5. data/.travis.yml +2 -0
  6. data/.vimrc +27 -0
  7. data/.yard_templates/default/layout/html/footer.erb +0 -0
  8. data/.yardopts +1 -0
  9. data/Gemfile +54 -0
  10. data/Gemfile.lock +197 -0
  11. data/Guardfile +21 -0
  12. data/Guardfile.tmux +28 -0
  13. data/README.markdown +67 -0
  14. data/Rakefile +16 -0
  15. data/config/database.yml +16 -0
  16. data/doc/Array.html +116 -0
  17. data/doc/Array/ArrayLoggerPersistor.html +152 -0
  18. data/doc/Lims.html +114 -0
  19. data/doc/Lims/Core.html +178 -0
  20. data/doc/Lims/Core/Action.html +91 -0
  21. data/doc/Lims/Core/Actions.html +116 -0
  22. data/doc/Lims/Core/Actions/Action.html +216 -0
  23. data/doc/Lims/Core/Actions/Action/AfterEval.html +853 -0
  24. data/doc/Lims/Core/Actions/Action/InvalidParameters.html +268 -0
  25. data/doc/Lims/Core/Actions/ActionGroup.html +196 -0
  26. data/doc/Lims/Core/Actions/ActionGroup/AfterEval.html +315 -0
  27. data/doc/Lims/Core/Actions/BulkAction.html +224 -0
  28. data/doc/Lims/Core/Actions/BulkAction/AfterEval.html +253 -0
  29. data/doc/Lims/Core/Actions/TestActionGroup.html +101 -0
  30. data/doc/Lims/Core/Actions/TestActionGroup/Action.html +133 -0
  31. data/doc/Lims/Core/Actions/TestActionGroup/ActionGroup.html +127 -0
  32. data/doc/Lims/Core/Base.html +287 -0
  33. data/doc/Lims/Core/Base/AccessibleViaSuper.html +252 -0
  34. data/doc/Lims/Core/Base/ClassMethod.html +177 -0
  35. data/doc/Lims/Core/Base/HashString.html +177 -0
  36. data/doc/Lims/Core/Base/IsArrayOf.html +606 -0
  37. data/doc/Lims/Core/Base/State.html +130 -0
  38. data/doc/Lims/Core/Organization.html +113 -0
  39. data/doc/Lims/Core/Organization/Batch.html +106 -0
  40. data/doc/Lims/Core/Persistence.html +267 -0
  41. data/doc/Lims/Core/Persistence/ComparisonFilter.html +318 -0
  42. data/doc/Lims/Core/Persistence/Filter.html +252 -0
  43. data/doc/Lims/Core/Persistence/IdentityMap.html +409 -0
  44. data/doc/Lims/Core/Persistence/IdentityMap/Class.html +144 -0
  45. data/doc/Lims/Core/Persistence/IdentityMap/DuplicateError.html +126 -0
  46. data/doc/Lims/Core/Persistence/IdentityMap/DuplicateIdError.html +136 -0
  47. data/doc/Lims/Core/Persistence/IdentityMap/DuplicateObjectError.html +136 -0
  48. data/doc/Lims/Core/Persistence/IdentityMapClass.html +133 -0
  49. data/doc/Lims/Core/Persistence/Logger.html +105 -0
  50. data/doc/Lims/Core/Persistence/Logger/Persistor.html +334 -0
  51. data/doc/Lims/Core/Persistence/Logger/Session.html +452 -0
  52. data/doc/Lims/Core/Persistence/Logger/Store.html +470 -0
  53. data/doc/Lims/Core/Persistence/MessageBus.html +871 -0
  54. data/doc/Lims/Core/Persistence/MessageBus/ConnectionError.html +123 -0
  55. data/doc/Lims/Core/Persistence/MessageBus/InvalidSettingsError.html +122 -0
  56. data/doc/Lims/Core/Persistence/MultiCriteriaFilter.html +293 -0
  57. data/doc/Lims/Core/Persistence/PersistAssociationTrait.html +91 -0
  58. data/doc/Lims/Core/Persistence/PersistableTrait.html +91 -0
  59. data/doc/Lims/Core/Persistence/Persistor.html +3072 -0
  60. data/doc/Lims/Core/Persistence/Persistor/DuplicateError.html +205 -0
  61. data/doc/Lims/Core/Persistence/Persistor/DuplicateIdError.html +147 -0
  62. data/doc/Lims/Core/Persistence/Persistor/DuplicateObjectError.html +147 -0
  63. data/doc/Lims/Core/Persistence/PersistorTrait.html +91 -0
  64. data/doc/Lims/Core/Persistence/ResourceState.html +1738 -0
  65. data/doc/Lims/Core/Persistence/Search.html +269 -0
  66. data/doc/Lims/Core/Persistence/Search/CreateSearch.html +251 -0
  67. data/doc/Lims/Core/Persistence/Search/SearchPersistor.html +240 -0
  68. data/doc/Lims/Core/Persistence/Search/SearchSequelPersistor.html +396 -0
  69. data/doc/Lims/Core/Persistence/Sequel.html +117 -0
  70. data/doc/Lims/Core/Persistence/Sequel/Filters.html +462 -0
  71. data/doc/Lims/Core/Persistence/Sequel/ForTest.html +101 -0
  72. data/doc/Lims/Core/Persistence/Sequel/ForTest/Name.html +137 -0
  73. data/doc/Lims/Core/Persistence/Sequel/ForTest/Name/NamePersitor.html +143 -0
  74. data/doc/Lims/Core/Persistence/Sequel/Migrations.html +266 -0
  75. data/doc/Lims/Core/Persistence/Sequel/Persistor.html +665 -0
  76. data/doc/Lims/Core/Persistence/Sequel/Session.html +501 -0
  77. data/doc/Lims/Core/Persistence/Sequel/Store.html +417 -0
  78. data/doc/Lims/Core/Persistence/Session.html +2751 -0
  79. data/doc/Lims/Core/Persistence/Session/UnmanagedObjectError.html +111 -0
  80. data/doc/Lims/Core/Persistence/StateGroup.html +696 -0
  81. data/doc/Lims/Core/Persistence/StateList.html +498 -0
  82. data/doc/Lims/Core/Persistence/Store.html +695 -0
  83. data/doc/Lims/Core/Persistence/UuidResource.html +1044 -0
  84. data/doc/Lims/Core/Persistence/UuidResource/InvalidUuidError.html +111 -0
  85. data/doc/Lims/Core/Persistence/UuidResource/UuidResourcePersistor.html +337 -0
  86. data/doc/Lims/Core/Persistence/Uuidable.html +799 -0
  87. data/doc/Lims/Core/Persistor.html +320 -0
  88. data/doc/Lims/Core/Resource.html +165 -0
  89. data/doc/Object.html +228 -0
  90. data/doc/SessionSpec.html +101 -0
  91. data/doc/SessionSpec/Model.html +279 -0
  92. data/doc/SessionSpec/Model/ModelPersistor.html +327 -0
  93. data/doc/_index.html +732 -0
  94. data/doc/class_list.html +47 -0
  95. data/doc/css/common.css +1 -0
  96. data/doc/css/full_list.css +55 -0
  97. data/doc/css/style.css +322 -0
  98. data/doc/file.README.html +127 -0
  99. data/doc/file_list.html +49 -0
  100. data/doc/frames.html +13 -0
  101. data/doc/index.html +127 -0
  102. data/doc/js/app.js +205 -0
  103. data/doc/js/full_list.js +167 -0
  104. data/doc/js/jquery.js +16 -0
  105. data/doc/method_list.html +1894 -0
  106. data/doc/top-level-namespace.html +100 -0
  107. data/lib/common.rb +18 -0
  108. data/lib/lims-core.rb +29 -0
  109. data/lib/lims-core/actions.rb +10 -0
  110. data/lib/lims-core/actions/action.rb +185 -0
  111. data/lib/lims-core/actions/action_group.rb +54 -0
  112. data/lib/lims-core/actions/bulk_action.rb +65 -0
  113. data/lib/lims-core/base.rb +132 -0
  114. data/lib/lims-core/helpers.rb +41 -0
  115. data/lib/lims-core/persistence.rb +15 -0
  116. data/lib/lims-core/persistence/comparison_filter.rb +54 -0
  117. data/lib/lims-core/persistence/filter.rb +23 -0
  118. data/lib/lims-core/persistence/identity_map.rb +55 -0
  119. data/lib/lims-core/persistence/logger/all.rb +5 -0
  120. data/lib/lims-core/persistence/logger/persistor.rb +35 -0
  121. data/lib/lims-core/persistence/logger/session.rb +30 -0
  122. data/lib/lims-core/persistence/logger/store.rb +37 -0
  123. data/lib/lims-core/persistence/message_bus.rb +131 -0
  124. data/lib/lims-core/persistence/multi_criteria_filter.rb +50 -0
  125. data/lib/lims-core/persistence/persist_association_trait.rb +96 -0
  126. data/lib/lims-core/persistence/persistable_trait.rb +150 -0
  127. data/lib/lims-core/persistence/persistor.rb +495 -0
  128. data/lib/lims-core/persistence/resource_state.rb +157 -0
  129. data/lib/lims-core/persistence/search.rb +3 -0
  130. data/lib/lims-core/persistence/search/all.rb +3 -0
  131. data/lib/lims-core/persistence/search/create_search.rb +55 -0
  132. data/lib/lims-core/persistence/search/search_persistor.rb +45 -0
  133. data/lib/lims-core/persistence/search/search_sequel_persistor.rb +40 -0
  134. data/lib/lims-core/persistence/sequel.rb +14 -0
  135. data/lib/lims-core/persistence/sequel/filters.rb +106 -0
  136. data/lib/lims-core/persistence/sequel/migrations.rb +14 -0
  137. data/lib/lims-core/persistence/sequel/migrations/add_audit_tables.rb +147 -0
  138. data/lib/lims-core/persistence/sequel/migrations/initial.rb +156 -0
  139. data/lib/lims-core/persistence/sequel/persistor.rb +200 -0
  140. data/lib/lims-core/persistence/sequel/session.rb +136 -0
  141. data/lib/lims-core/persistence/sequel/store.rb +37 -0
  142. data/lib/lims-core/persistence/session.rb +409 -0
  143. data/lib/lims-core/persistence/state_group.rb +97 -0
  144. data/lib/lims-core/persistence/state_list.rb +56 -0
  145. data/lib/lims-core/persistence/store.rb +73 -0
  146. data/lib/lims-core/persistence/uuid_resource.rb +115 -0
  147. data/lib/lims-core/persistence/uuid_resource_persistor.rb +43 -0
  148. data/lib/lims-core/persistence/uuidable.rb +107 -0
  149. data/lib/lims-core/resource.rb +21 -0
  150. data/lib/lims-core/subclass_tracker.rb +30 -0
  151. data/lib/lims-core/version.rb +5 -0
  152. data/lims-core.gemspec +40 -0
  153. data/makefile +52 -0
  154. data/showoff/core-2012-06-11/core/01_slide.md +237 -0
  155. data/showoff/core-2012-06-11/core/02_slide.md +110 -0
  156. data/showoff/core-2012-06-11/custom.css +44 -0
  157. data/showoff/core-2012-06-11/main/01_slide.md +53 -0
  158. data/showoff/core-2012-06-11/showoff.json +10 -0
  159. data/showoff/core-2012-06-11/tp1.tpl +1 -0
  160. data/spec/actions/action_group_spec.rb +39 -0
  161. data/spec/actions/spec_helper.rb +1 -0
  162. data/spec/persistence/identity_map_spec.rb +55 -0
  163. data/spec/persistence/logger/spec_helper.rb +7 -0
  164. data/spec/persistence/logger/store_spec.rb +48 -0
  165. data/spec/persistence/message_bus_spec.rb +76 -0
  166. data/spec/persistence/sequel/session_spec.rb +125 -0
  167. data/spec/persistence/sequel/spec_helper.rb +39 -0
  168. data/spec/persistence/sequel/store_shared.rb +25 -0
  169. data/spec/persistence/sequel/store_spec.rb +22 -0
  170. data/spec/persistence/session_spec.rb +199 -0
  171. data/spec/persistence/spec_helper.rb +2 -0
  172. data/spec/persistence/uuid_resource_spec.rb +80 -0
  173. data/spec/spec_helper.rb +10 -0
  174. data/spec/subclass_tracker_sperc.rb +62 -0
  175. data/utils/constant_tree.rb +29 -0
  176. data/utils/stack.rb +48 -0
  177. metadata +402 -0
@@ -0,0 +1,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
+