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,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/*')