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