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,100 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.7.3
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
relpath = '';
|
19
|
+
if (relpath != '') relpath += '/';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
23
|
+
|
24
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
25
|
+
|
26
|
+
|
27
|
+
</head>
|
28
|
+
<body>
|
29
|
+
<script type="text/javascript" charset="utf-8">
|
30
|
+
if (window.top.frames.main) document.body.className = 'frames';
|
31
|
+
</script>
|
32
|
+
|
33
|
+
<div id="header">
|
34
|
+
<div id="menu">
|
35
|
+
|
36
|
+
<a href="_index.html">Index</a> »
|
37
|
+
|
38
|
+
|
39
|
+
<span class="title">Top Level Namespace</span>
|
40
|
+
|
41
|
+
|
42
|
+
<div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
<div id="search">
|
46
|
+
|
47
|
+
<a id="class_list_link" href="#">Class List</a>
|
48
|
+
|
49
|
+
<a id="method_list_link" href="#">Method List</a>
|
50
|
+
|
51
|
+
<a id="file_list_link" href="#">File List</a>
|
52
|
+
|
53
|
+
</div>
|
54
|
+
<div class="clear"></div>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<iframe id="search_frame"></iframe>
|
58
|
+
|
59
|
+
<div id="content"><h1>Top Level Namespace
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
</h1>
|
64
|
+
|
65
|
+
<dl class="box">
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
</dl>
|
75
|
+
<div class="clear"></div>
|
76
|
+
|
77
|
+
<h2>Defined Under Namespace</h2>
|
78
|
+
<p class="children">
|
79
|
+
|
80
|
+
|
81
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="Lims.html" title="Lims (module)">Lims</a></span>, <span class='object_link'><a href="SessionSpec.html" title="SessionSpec (module)">SessionSpec</a></span>
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
<strong class="classes">Classes:</strong> <span class='object_link'><a href="Array.html" title="Array (class)">Array</a></span>, <span class='object_link'><a href="Object.html" title="Object (class)">Object</a></span>
|
86
|
+
|
87
|
+
|
88
|
+
</p>
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
</div>
|
97
|
+
|
98
|
+
|
99
|
+
</body>
|
100
|
+
</html>
|
data/lib/common.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# requirement used by everything
|
2
|
+
require 'facets/string'
|
3
|
+
require 'facets/kernel'
|
4
|
+
require 'facets/hash'
|
5
|
+
require 'facets/array'
|
6
|
+
|
7
|
+
require 'virtus'
|
8
|
+
require 'aequitas/virtus_integration'
|
9
|
+
|
10
|
+
class Object
|
11
|
+
def andtap(&block)
|
12
|
+
self && (block ? block.call(self) : self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parent_scope()
|
16
|
+
@__parent_scope ||= eval self.name.split('::').tap { |_| _.pop }.join('::')
|
17
|
+
end
|
18
|
+
end
|
data/lib/lims-core.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# vi: spell:spelllang=en ts=2:sts=2:sw=2:et
|
2
|
+
require "lims-core/version"
|
3
|
+
|
4
|
+
require 'lims-core/persistence'
|
5
|
+
# Persistence submodules need to be required manually. This is to avoid
|
6
|
+
# having to require and install all the store dependency (mysql, postgres) etc ...
|
7
|
+
|
8
|
+
|
9
|
+
# LIMS stands for Laboratory Information Management System.
|
10
|
+
# A namespace.
|
11
|
+
module Lims
|
12
|
+
# The Core of the {Lims LIM S}ystem.
|
13
|
+
# Includes the basic classes corresponding to the :
|
14
|
+
# 1. Resource base class
|
15
|
+
# 2. Persistence Layer
|
16
|
+
#
|
17
|
+
# The Core is split in the following submodule/namespace :
|
18
|
+
# 10. {Actions}
|
19
|
+
# High level {Actions::Action actions} that can be performed on things.
|
20
|
+
#
|
21
|
+
# 12. {Persistence}
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# This partition is more for clarity/documentation purposes and it's not meant to be really tight.
|
25
|
+
# However it's more likely than the submodules dependency will be a tree than a graph, (but it's not a necessity).
|
26
|
+
module Core
|
27
|
+
# Your code goes here...
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#vi: ts=2 sw=2 et spell spelllang=en
|
2
|
+
# true needed to avoid modeline comment to be seen as Lims::Core doc.
|
3
|
+
require 'lims-core/actions/action'
|
4
|
+
|
5
|
+
module Lims::Core
|
6
|
+
# Actions are high level end user groups of elementary steps that a user can perform on things .
|
7
|
+
# They can be seen as use-case, scenario and will probably each correspond to one call in the API.
|
8
|
+
module Actions
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'common'
|
2
|
+
|
3
|
+
require 'virtus'
|
4
|
+
require 'facets/ostruct'
|
5
|
+
|
6
|
+
require 'lims-core/persistence/store'
|
7
|
+
|
8
|
+
module Lims::Core
|
9
|
+
module Actions
|
10
|
+
# This mixin add the Action behavior to a Class.
|
11
|
+
# An action can be called and reverted (if possible) within a {Persistence::Session session}.
|
12
|
+
# For this, the action must implements the {Action::AfterEval#_call_in_session _call_in_session} and {Action::AfterEval#_revert_in_session _revert_in_session}.
|
13
|
+
# Those methods are private and take a session as a parameter.
|
14
|
+
# The public equivalent (call/revert) will create a session (using the store) and call the corresponding methods.
|
15
|
+
|
16
|
+
module Action
|
17
|
+
extend SubclassTracker
|
18
|
+
class << self
|
19
|
+
alias_method :tracker_included, :included
|
20
|
+
end
|
21
|
+
UnrevertableAction = Class.new(StandardError)
|
22
|
+
def self.included(klass)
|
23
|
+
klass.class_eval do
|
24
|
+
include Base
|
25
|
+
attribute :store, Persistence::Store, :required => true
|
26
|
+
attribute :user, Object, :required => true, :writer => :private, :initializable => true
|
27
|
+
attribute :application, String, :required => true
|
28
|
+
attribute :result, Object
|
29
|
+
include AfterEval # hack so initialize would be called properly
|
30
|
+
end
|
31
|
+
tracker_included(klass)
|
32
|
+
end
|
33
|
+
|
34
|
+
class InvalidParameters < RuntimeError
|
35
|
+
attr_reader :errors
|
36
|
+
def initialize(errors = {})
|
37
|
+
@errors = errors
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module AfterEval
|
42
|
+
# Initialize a new actions
|
43
|
+
# 'Common' parameters are set as argument
|
44
|
+
# whereas specific ones are set on a dummy object via the block.
|
45
|
+
# The block is executed within a session allowing to find object form id, etc.
|
46
|
+
|
47
|
+
def initialize(*args, &initializer)
|
48
|
+
@initializer = initializer
|
49
|
+
super(*args)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Executes the action.
|
53
|
+
# This is a wrapper around _call_in_session,
|
54
|
+
# and it shouldn't be overriden.
|
55
|
+
# A block can be passed to be evaluated with the session after the save session been saved.
|
56
|
+
# This is usefull to get ids of saved object.
|
57
|
+
# False will be returned if the action failed (or parameters are invalid)
|
58
|
+
# @return the value return by the block
|
59
|
+
# @yieldparam [Action] a self
|
60
|
+
# @yieldparam [Session] session the current session.
|
61
|
+
def call(session=nil, &after_save)
|
62
|
+
with_session(session) do |session|
|
63
|
+
execute_and_store_result(session, &after_save)
|
64
|
+
end.andtap { |block| block.call }
|
65
|
+
end
|
66
|
+
|
67
|
+
def execute_and_store_result(session, &after_save)
|
68
|
+
after_save ||= lambda { |a,s| a.result }
|
69
|
+
self.result = _call_in_session(session)
|
70
|
+
_objects_to_save.each do |a|
|
71
|
+
session << a
|
72
|
+
end
|
73
|
+
lambda { after_save[self, session] }
|
74
|
+
end
|
75
|
+
|
76
|
+
protected :execute_and_store_result
|
77
|
+
|
78
|
+
# Execute the opposite of the action if possible.
|
79
|
+
# This a wrapper around _revert_in_session,
|
80
|
+
# and shouldn't be overriden.
|
81
|
+
# @raise UnrevertableAction
|
82
|
+
def revert()
|
83
|
+
with_session { |s| _revert_in_session(s) }
|
84
|
+
end
|
85
|
+
|
86
|
+
# Execute the given block within a new session.
|
87
|
+
# Validates the action and fill #errors if needed
|
88
|
+
# @return [Object, False]
|
89
|
+
def with_session(*args, &block)
|
90
|
+
@store.with_session(*args) do |session|
|
91
|
+
# initialize action
|
92
|
+
if @initializer
|
93
|
+
params = OpenStruct.new
|
94
|
+
|
95
|
+
@initializer[params, session]
|
96
|
+
|
97
|
+
# We want to catch ALL attributes errors
|
98
|
+
# therefore We need to iterate on each attributes
|
99
|
+
# and catch the potentiel exception raised by each
|
100
|
+
# assignment.
|
101
|
+
|
102
|
+
|
103
|
+
attribute_errors = []
|
104
|
+
params.each do |key, value|
|
105
|
+
next if %w(user application_id).include?(key.to_s)
|
106
|
+
begin
|
107
|
+
send("#{key}=", value)
|
108
|
+
rescue NoMethodError => e
|
109
|
+
attribute_errors << [key, value]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
unless attribute_errors.empty?
|
114
|
+
# An error occured.
|
115
|
+
# We need to check if set attributes are valid
|
116
|
+
# and add the attributes errors to the general error message.
|
117
|
+
valid?
|
118
|
+
invalid_parameters = errors_to_hash
|
119
|
+
|
120
|
+
attribute_errors.each do |key, value|
|
121
|
+
invalid_parameters[key] = ["field :#{key} doesn't exist or value '#{value}' is invalid"]
|
122
|
+
end
|
123
|
+
raise InvalidParameters.new(invalid_parameters)
|
124
|
+
end
|
125
|
+
@initializer = nil
|
126
|
+
end
|
127
|
+
|
128
|
+
# Note: there is a bug in Aequitas gem on the valid?
|
129
|
+
# method call. For an attribute which needs to be required
|
130
|
+
# and greater than 0, the greater than 0 is tested first
|
131
|
+
# and the required after. So if the parameter is not set,
|
132
|
+
# nil >= 0 is evaluated by Aequitas and an exception is
|
133
|
+
# raised. We catch it here and raise an InvalidParameters error.
|
134
|
+
is_valid = begin
|
135
|
+
valid?
|
136
|
+
rescue
|
137
|
+
raise InvalidParameters.new
|
138
|
+
end
|
139
|
+
|
140
|
+
if is_valid
|
141
|
+
block.call(session)
|
142
|
+
else
|
143
|
+
invalid_parameters = errors_to_hash
|
144
|
+
raise InvalidParameters.new(invalid_parameters)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def errors_to_hash()
|
150
|
+
{}.tap do |hash|
|
151
|
+
errors.keys.each do |key|
|
152
|
+
hash[key] = [].tap do |array|
|
153
|
+
# errors[key] returns an array of Aequitas::Violation
|
154
|
+
errors[key].each do |error|
|
155
|
+
array << error.message
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# This is the main method of an action,
|
163
|
+
# called to effectively perform an action.
|
164
|
+
def _call_in_session(session)
|
165
|
+
raise NotImplementedError
|
166
|
+
end
|
167
|
+
|
168
|
+
# how to revert the action,
|
169
|
+
# if possible.
|
170
|
+
def _revert_in_session(session)
|
171
|
+
raise UnrevertableAction(self)
|
172
|
+
end
|
173
|
+
|
174
|
+
# List of objects to save (add to the session).
|
175
|
+
# By default get all attributes and the resulth.
|
176
|
+
# Override if need (to add a created resource for example).
|
177
|
+
# @return a list of object to save
|
178
|
+
def _objects_to_save
|
179
|
+
[result, *attributes.map { |a| a[1] }].select { |o| o.is_a?(Resource) }
|
180
|
+
end
|
181
|
+
private :_call_in_session, :_revert_in_session, :_objects_to_save
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'lims-core/actions/action'
|
2
|
+
|
3
|
+
module Lims::Core
|
4
|
+
module Actions
|
5
|
+
# This module provide a helper to execute multiple action a time.
|
6
|
+
# This module is not intended to be used as a bare class but more
|
7
|
+
# as a base to define multiple actions "creator", which will
|
8
|
+
# manage the needed parameters and create the actions consequently.
|
9
|
+
# This action executes all the action in sequence and the result
|
10
|
+
# is an array of the results.
|
11
|
+
module ActionGroup
|
12
|
+
def self.included(klass)
|
13
|
+
klass.class_eval do
|
14
|
+
include Action
|
15
|
+
include AfterEval
|
16
|
+
attribute :actions, Array, :required => true, :writer => :private , :reader => :private, :initializable => true
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
# This module is needed
|
22
|
+
# otherwire the following methogs
|
23
|
+
# will be overriden by the mixin called in self.included.
|
24
|
+
module AfterEval
|
25
|
+
|
26
|
+
# We need to override call to process all the after_save
|
27
|
+
# once all actions have been executed
|
28
|
+
def execute_and_store_result(session, &after_save)
|
29
|
+
after_save ||= lambda { |a,s| a.result }
|
30
|
+
self.result = _call_in_session(session)
|
31
|
+
lambda { after_save[self, session] }
|
32
|
+
end
|
33
|
+
|
34
|
+
def _call_in_session(session)
|
35
|
+
actions.map do |action|
|
36
|
+
update_action_attribute(action)
|
37
|
+
action.with_session(session) do |new_session|
|
38
|
+
action.execute_and_store_result(new_session)
|
39
|
+
action.result
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set the required attribute of the children action
|
45
|
+
# to the parent one.
|
46
|
+
def update_action_attribute(action)
|
47
|
+
%w(store user application).each do |attribute|
|
48
|
+
action[attribute]=self[attribute]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'lims-core/actions/action_group'
|
2
|
+
|
3
|
+
module Lims::Core
|
4
|
+
module Actions
|
5
|
+
# *Lift* an action to a bulk one
|
6
|
+
# by creating a group of similar actions.
|
7
|
+
# The parameters being a array of parameters which will
|
8
|
+
# be used to create each individual action.
|
9
|
+
module BulkAction
|
10
|
+
def self.included(klass)
|
11
|
+
klass.instance_eval do
|
12
|
+
# @param [String] element_name name of the underlying action. Use to get the result from the subaction element.
|
13
|
+
# @param [String] parameters key to get parameters array for each action and return the result.
|
14
|
+
def initialize_class(element_name, group_name, action_class)
|
15
|
+
define_method :group_name do
|
16
|
+
group_name
|
17
|
+
end
|
18
|
+
|
19
|
+
define_method :action_class do
|
20
|
+
action_class
|
21
|
+
end
|
22
|
+
|
23
|
+
define_method :element_name do
|
24
|
+
element_name
|
25
|
+
end
|
26
|
+
|
27
|
+
self.class_eval do
|
28
|
+
include ActionGroup
|
29
|
+
attribute group_name, Array, :required => true
|
30
|
+
include AfterEval
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module AfterEval
|
37
|
+
|
38
|
+
def initialize(*args, &initializer)
|
39
|
+
super(*args) do |a,s|
|
40
|
+
initializer.call(a,s) if initializer
|
41
|
+
parameter_list = a[group_name]
|
42
|
+
a.actions = parameter_list.map do |parameters|
|
43
|
+
action_class.new do |action, session|
|
44
|
+
# We copy the parameters to action class.
|
45
|
+
# action, is not the real action but an open struct.
|
46
|
+
# We can't just pass the parameters in the constructor
|
47
|
+
# in case some parameters are private.
|
48
|
+
parameters.each { |k, v| action[k] = v }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def _call_in_session(session)
|
55
|
+
super(session)
|
56
|
+
if element_name
|
57
|
+
{group_name=> actions.map { |a| a.result[element_name] }}
|
58
|
+
else
|
59
|
+
{group_name=> actions.map { |a| a.result }}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|