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,157 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+ require 'virtus'
3
+
4
+ require 'lims-core/resource'
5
+ require 'lims-core/persistence/persistor'
6
+
7
+ module Lims::Core
8
+ module Persistence
9
+ # Hold different information relateed particular resource
10
+ # within a {Session}/{Persistor}.
11
+ # This could be the database id, the current saving state etc ...
12
+ class ResourceState
13
+ include Virtus
14
+ attribute :id, Object
15
+ attribute :uuid_resource, Object
16
+ attribute :resource, Object, :writer => :private, :required => true
17
+ attribute :persistor, Persistor, :writer => :private, :required => true
18
+ attribute :to_delete, Object, :writer => :private
19
+
20
+ def children_saved?
21
+ @children_saved
22
+ end
23
+
24
+ def body_saved?
25
+ @body_saved
26
+ end
27
+
28
+ def initialize(resource, persistor, id=nil)
29
+ @resource=resource
30
+ @persistor=persistor
31
+ self.id=id if id
32
+ @to_delete = false
33
+ update_dirty_key
34
+ reset
35
+
36
+ end
37
+
38
+ def to_delete
39
+ @to_delete
40
+ end
41
+
42
+
43
+ def id
44
+ @id
45
+ end
46
+
47
+ def resource
48
+ @resource
49
+ end
50
+
51
+ def persistor
52
+ @persistor
53
+ end
54
+
55
+ def dirty_key
56
+ @persistor.dirty_key_for(resource)
57
+ end
58
+
59
+ def update_dirty_key
60
+ @last_dirty_key = resource ? @persistor.dirty_key_for(resource) : nil
61
+ end
62
+
63
+ def new?
64
+ @id == nil
65
+ end
66
+
67
+ def to_load?
68
+ @id != nil && @resource == nil
69
+ end
70
+
71
+
72
+ def dirty?
73
+ !@body_saved && dirty_key != @last_dirty_key
74
+ end
75
+
76
+ def id=(new_id)
77
+ return if new_id == @id
78
+ raise RuntimeError, "modifing existing id not allowed. #{self}" if @id
79
+ @id = new_id
80
+ # link the new id in th id_to_state map
81
+ @persistor.bind_state_to_id(self)
82
+ id
83
+ end
84
+
85
+ def resource=(new_resource)
86
+ # Hack
87
+ # for some emtpy resources , resource == nil returns true
88
+ # but nil == resource returns false
89
+ # which is why we test both.
90
+ return if resource == new_resource && new_resource == resource
91
+ raise RuntimeError, "modifing existing resource not allowed. #{self}" if @resource
92
+ @resource = new_resource
93
+ # link the new resource in th resource_to_state map
94
+ @persistor.bind_state_to_resource(self)
95
+ update_dirty_key
96
+ resource
97
+ end
98
+
99
+ def mark_for_deletion
100
+ @to_delete = true
101
+ end
102
+
103
+ # @todo use state machine ?
104
+ def save_action
105
+ case
106
+ when @body_saved then nil
107
+ when to_delete && !new? then :delete
108
+ when new? then :insert
109
+ when dirty? then :update
110
+ end
111
+ end
112
+
113
+ def inserted(new_id=nil)
114
+ self.id = new_id
115
+ update_dirty_key
116
+ end
117
+
118
+ def updated
119
+ update_dirty_key
120
+ end
121
+
122
+ def parents_saved!
123
+ @parents_saved = true
124
+ end
125
+ def parents
126
+ !@parents_saved && @persistor.parents_for(resource)
127
+ end
128
+ def parents!
129
+ parents.tap { parents_saved! }
130
+ end
131
+ def children_saved!
132
+ @children_saved = true
133
+ end
134
+
135
+ def parents_saved?
136
+ @parents_saved
137
+ end
138
+ def children
139
+ return [] if to_delete
140
+ !@children_saved && @persistor.children_for(resource)
141
+ end
142
+ def children!
143
+ children.tap { children_saved! }
144
+ end
145
+ def body_saved!
146
+ @body_saved = true
147
+ end
148
+
149
+ def reset
150
+ @parents_saved = nil
151
+ @children_saved = nil
152
+ @body_saved= nil
153
+ end
154
+ end
155
+ end
156
+ end
157
+
@@ -0,0 +1,3 @@
1
+ #vi: ts=2 sw=2 et
2
+ # Convenience file.
3
+ require 'lims-core/persistence/search/search_persistor'
@@ -0,0 +1,3 @@
1
+ require('lims-core/persistence/search/create_search')
2
+ require('lims-core/persistence/search/search_persistor')
3
+ require('lims-core/persistence/search/search_sequel_persistor')
@@ -0,0 +1,55 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+ require 'lims-core/actions/action'
3
+
4
+ require 'lims-core/persistence/search/search_persistor'
5
+ require 'lims-core/persistence/multi_criteria_filter'
6
+ require 'lims-core/persistence/comparison_filter'
7
+
8
+ module Lims::Core
9
+ module Persistence
10
+ class Search
11
+ class CreateSearch
12
+ include Actions::Action
13
+
14
+ attribute :description, String, :required => true
15
+ attribute :model, String, :required => true
16
+ attribute :criteria, Hash, :required => true
17
+
18
+ def _call_in_session(session)
19
+ # Use the appropriate filter if needed.
20
+ filter = nil
21
+ keys = criteria.keys
22
+ keys.delete("comparison") if criteria.keys.size > 1
23
+ if keys.size == 1
24
+ keys.first.andtap do |model|
25
+ filter_class_name = "#{model.capitalize.gsub(/_[^_]*/) { |b| b[1..b.size].capitalize }}Filter"
26
+ if Persistence::const_defined? filter_class_name
27
+ filter = Persistence::const_get(filter_class_name).new(:criteria => criteria)
28
+ end
29
+ end
30
+ end
31
+ filter ||= Persistence::MultiCriteriaFilter.new(:criteria => criteria)
32
+ search = Persistence::Search.new(:description => description,
33
+ :model => session.send(model).model,
34
+ :filter => filter)
35
+ if search.valid?
36
+ stored_search = session.search[search.attributes, false].first
37
+ if stored_search.nil?
38
+ session << search
39
+ else
40
+ search = stored_search
41
+ end
42
+ { :search => search, :uuid => session.uuid_for!(search) }
43
+ else
44
+ false
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ module Persistence
51
+ class Search
52
+ Create = CreateSearch
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,45 @@
1
+ #vi: ts=2 sw=2 et
2
+ require 'common'
3
+
4
+ require 'lims-core/resource'
5
+ require 'lims-core/persistence/persistor'
6
+ require 'lims-core/persistence/filter'
7
+
8
+ # We need to load all the possible filter to be able load them by name.
9
+ require_all('*filter')
10
+
11
+ module Lims::Core
12
+ module Persistence
13
+ # base class handling searches. A Search represent a set of parameters
14
+ # which when executed returns a set of similar object (.i.e the same class).
15
+ # A search is savable.
16
+ class Search
17
+ include Resource
18
+ attribute :description, String, :required => true, :initializable => true, :write => :private
19
+ attribute :model, Class, :required => true, :initializable => true, :writer => :private
20
+ attribute :filter, Filter, :required => true, :initializable => true, :writer => :private
21
+
22
+ # Main method. Take an session an return an filtered persistor.
23
+ # @param [Session]
24
+ # @return [Persistor]
25
+ def call(session)
26
+ filter.call(session.persistor_for(@model))
27
+ end
28
+
29
+ # Base persistor for Search object.
30
+ # It should be called Persistence::Search but, this is
31
+ # already taken by the main Search class.
32
+ class SearchPersistor < Persistence::Persistor
33
+ Model = Persistence::Search
34
+ # Override default one, because filter are serialized
35
+ # and even though they are a Resource they are not counted as parents
36
+ def parents(resource)
37
+ []
38
+ end
39
+ end
40
+
41
+
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,40 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+
3
+ require 'lims-core/persistence/search/search_persistor'
4
+ require 'lims-core/persistence/sequel/persistor'
5
+
6
+
7
+ module Lims::Core
8
+ module Persistence
9
+ # Not a search but a search persistor.
10
+ class Search
11
+ class SearchSequelPersistor < Search::SearchPersistor
12
+ include Persistence::Sequel::Persistor
13
+
14
+ def self.table_name
15
+ :searches
16
+ end
17
+
18
+ def filter_attributes_on_load(attributes)
19
+ filter_parameters = @session.unserialize(attributes[:filter_parameters])
20
+ # The first key should be a symbol, @see persistence/comparison_filter.rb for example
21
+ filter_parameters.rekey! { |k| k.to_sym }
22
+ {
23
+ :description => attributes[:description],
24
+ :model => constant(attributes[:model]),
25
+ :filter => Persistence.const_get(attributes[:filter_type]).new(filter_parameters)
26
+ }
27
+ end
28
+ def filter_attributes_on_save(attributes, *args)
29
+ filter = attributes[:filter]
30
+ {
31
+ :description => attributes[:description],
32
+ :model => attributes[:model].name,
33
+ :filter_type => filter.class.name.split('::').last,
34
+ :filter_parameters => @session.serialize(filter.attributes)
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+ # please keep 2 empty lines to avoid to comment to be
3
+ # seen as Persistence doc.
4
+ require 'common'
5
+
6
+ module Lims::Core::Persistence
7
+ # Persistence module using the {http://sequel.rubyforge.org/ sequel} gem.
8
+ # It implements abstract/base classes : {Store}, {Session} etc.
9
+ module Sequel
10
+ end
11
+ end
12
+
13
+ # require everything
14
+ require 'lims-core/persistence/sequel/store'
@@ -0,0 +1,106 @@
1
+ require 'lims-core/persistence/sequel'
2
+ require 'active_support/inflector'
3
+
4
+ module Lims::Core
5
+ module Persistence
6
+ # Implementes filter methods needed by persitors.
7
+ module Sequel::Filters
8
+
9
+ LIKE_OPERATOR = 'LIKE'
10
+ COMPARISON_OPERATORS = ["<", "<=", "=", ">=", ">", LIKE_OPERATOR]
11
+
12
+ # Implement a multicriteria filter for a Sequel::Persistor.
13
+ # Value can be either a String, an Array or a Hash.
14
+ # Strings and Arrays are normal filters, whereas Hashes
15
+ # correspond to a joined search. The criteria will apply to the
16
+ # joined object corresponding to the key.
17
+ # @param [Hash<String, Object > criteria
18
+ # @return [Persistor]
19
+ def multi_criteria_filter(criteria)
20
+ # We need to create the adequat dataset.
21
+ dataset = __multi_criteria_filter(criteria).dataset
22
+ # As the dataset can include join, we need to select only the columns
23
+ # corresponding to the persistor
24
+ self.class.new(self, dataset.qualify(table_name).distinct())
25
+ end
26
+
27
+ # Implement a comparison filter for a Sequel::Persistor.
28
+ # Key being the name of the resource's field and the value is a Hash.
29
+ # The key of the hash is a comparison operator
30
+ # and the value is the given value the filter do the comparison against.
31
+ # @param [Hash<String, Object>] criteria
32
+ # @return [Persistor]
33
+ def comparison_filter(criteria)
34
+ clause = ""
35
+ criteria.each do |field, comparison_expression|
36
+ comparison_expression.each do |operator, value|
37
+ if operator.upcase == LIKE_OPERATOR
38
+ operator = operator.upcase
39
+ value = "%#{value}%" if operator == LIKE_OPERATOR
40
+ end
41
+
42
+ raise ArgumentError, "Not supported comparison operator has been given: '#{operator}'" unless COMPARISON_OPERATORS.include?(operator)
43
+
44
+ clause = clause + ') & (' unless clause == ""
45
+ clause = clause + " #{field} " + operator + "'#{value}'"
46
+ end
47
+ end
48
+
49
+ self.class.new(self, dataset.where(clause).qualify)
50
+ end
51
+
52
+ # Joins the comparison filter to the existing persistor.
53
+ # @param [Dataset] dataset
54
+ # @param [Hash<String, Object>] criteria for the comparison
55
+ # @return [Persistor]
56
+ def add_comparison_filter(dataset, comparison_criteria)
57
+ comparison_persistor = comparison_filter(comparison_criteria)
58
+ self.class.new(self, dataset.join(comparison_persistor.dataset, :id => :key).qualify)
59
+ end
60
+
61
+ protected
62
+ # @param Hash criteria
63
+ # @return Persistor
64
+ def __multi_criteria_filter(criteria)
65
+ # Extract critera recursively and apply subhashes to
66
+ # joined table
67
+ # Hash value are criteria for the corresponding joined tabled
68
+ # We need to extract them and do the obvious join
69
+ # Values are passed to filter_attributes_on save to get
70
+ # the right format if needed.
71
+ joined = criteria.reduce(self) do |persistor, (key, value)|
72
+ case value
73
+ when Hash
74
+ criteria_persistor = persistor.send(key)
75
+ filtered_value = criteria_persistor.filter_attributes_on_save(value.rekey {|k| k.to_sym})
76
+ joined_persistor = criteria_persistor.__multi_criteria_filter(filtered_value)
77
+ __join(joined_persistor)
78
+ else persistor
79
+ end
80
+ end
81
+
82
+ # We need to passes to filter are applied on the joined persistor.
83
+ # This is needed because the __join function expected bare persistor
84
+ # and will loose any filter applied on the original persistor
85
+ criteria.reduce(joined) do |persistor, (key, value)|
86
+ case value
87
+ when Hash
88
+ persistor
89
+ else
90
+ self.class.new(persistor, persistor.dataset.filter(::Sequel.qualify(table_name, key) => value))
91
+ end
92
+ end
93
+ end
94
+
95
+ # Assume that the original persistor his *blank* ie
96
+ # it doesn't contain any SQL modifier
97
+ # @param [Persistor] persistor
98
+ # @return [Persistor]
99
+ def __join(persistor)
100
+ self.class.new(self, persistor.dataset.join(table_name, primary_key => :"#{table_name.to_s.singularize}_#{persistor.primary_key}"))
101
+ end
102
+
103
+ end
104
+ end
105
+ end
106
+
@@ -0,0 +1,14 @@
1
+ require 'common'
2
+
3
+ # A namespace containing migrations mixin
4
+ module Lims
5
+ module Core
6
+ module Persistence
7
+ module Sequel
8
+ module Migrations
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ require_all('migrations/*')