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,132 @@
1
+ # vi: ts=2 sts=2 et sw=2 spell spelllang=en
2
+ require 'common'
3
+
4
+ module Lims::Core
5
+ module Base
6
+ def self.included(klass)
7
+ klass.class_eval do
8
+ include Virtus
9
+ include Aequitas
10
+ include AccessibleViaSuper
11
+ extend Forwardable
12
+ extend ClassMethod
13
+ end
14
+ end
15
+
16
+ module AccessibleViaSuper
17
+ def initialize(*args, &block)
18
+ # readonly attributes are normaly not allowed in constructor
19
+ # by Virtus. We need to call set_attributes explicitely
20
+ options = args.extract_options!
21
+ # we would use `options & [:row ... ]` if we could
22
+ # but Sequel redefine Hash#& ...
23
+ initializables = self.class.attributes.select {|a| a.options[:initializable] == true }
24
+ initial_options = options.subset(initializables.map(&:name))
25
+ set_attributes(initial_options)
26
+ super(*args, options - initial_options, &block).tap {
27
+ }
28
+ end
29
+
30
+ # Like attributes but allow class to define a version
31
+ # without side effet.
32
+ # For example attribute on a UuidResource will generate an uuid.
33
+ # This is the method called by dirty_key_for
34
+ def attributes_for_dirty
35
+ attributes
36
+ end
37
+
38
+ end
39
+ # Compare 2 resources.
40
+ # They are == if they have the same values (attributes),
41
+ # regardless they are the same ruby object or not.
42
+ # @param other
43
+ # @return [Boolean]
44
+ def ==(other)
45
+ self.attributes == (other.respond(:attributes) || {} )
46
+ end
47
+
48
+
49
+ module ClassMethod
50
+ def is_array_of(child_klass, options = {}, &initializer)
51
+ define_method :initialize_array do |*args|
52
+ @content = initializer ? initializer[self, child_klass] : []
53
+ end
54
+
55
+ class_eval do
56
+ include Enumerable
57
+ include IsArrayOf
58
+ def_delegators :@content, :each, :size , :each_with_index, :map, :zip, :clear, :empty?, :to_s \
59
+ , :include?, :to_a, :first, :last
60
+
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+
67
+ module IsArrayOf
68
+
69
+ def initialize(*args, &block)
70
+ super(*args, &block)
71
+ initialize_array()
72
+ end
73
+
74
+ # Add content to compare
75
+ # If classe are not in the same hierarchy we only compare the content
76
+ # @param other to compare with
77
+ # @return [Boolean]
78
+ def ==(other)
79
+ if other.is_a?(self.class) || self.is_a?(other.class)
80
+ super(other)
81
+ else
82
+ !other.nil?
83
+ end && self.to_a == other.to_a
84
+ end
85
+
86
+ # The underlying array. Use to everything which is not directly delegated
87
+ # @return [Array]
88
+ def content
89
+ @content
90
+ end
91
+
92
+ # Delegate [] to the underlying array.
93
+ # This is needed because Virtus redefine [] as well
94
+ # @param [Fixnum, ... ] i index
95
+ # @return [Object]
96
+ def [](i)
97
+ case i
98
+ when Fixnum then self.content[i]
99
+ else super(i)
100
+ end
101
+ end
102
+
103
+ def []=(i, value)
104
+ case i
105
+ when Fixnum then self.content[i]=value
106
+ else super(i, value)
107
+ end
108
+ end
109
+ # iterate only between non empty lanes.
110
+ # @yield [content]
111
+ # @return itself
112
+ def each_content
113
+ @content.each do |content|
114
+ yield content if content
115
+ end
116
+ end
117
+ end
118
+
119
+ class HashString < Virtus::Attribute::Object
120
+ primitive Hash
121
+ def coerce(hash)
122
+ hash.rekey {|key| key.to_s }
123
+ end
124
+ end
125
+
126
+ # @todo override state_machine to automatically add
127
+ # attribute
128
+ class State < Virtus::Attribute::Object
129
+ primitive String
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,41 @@
1
+ module Lims::Core
2
+ module Helpers
3
+ def self.gem_available?(gem_name)
4
+ begin
5
+ Gem::Specification.find_by_name(gem_name)
6
+ rescue Gem::LoadError
7
+ false
8
+ end
9
+ end
10
+
11
+ # Load the available gem for json
12
+ if gem_available?('jrjackson')
13
+ require 'jrjackson'
14
+ def self.to_json(object)
15
+ JrJackson::Json.dump(object)
16
+ end
17
+
18
+ def self.load_json(json)
19
+ JrJackson::Json.load(json)
20
+ end
21
+ elsif gem_available?('oj')
22
+ require 'oj'
23
+ def self.to_json(object)
24
+ Oj.dump(object, :mode => :compat)
25
+ end
26
+
27
+ def self.load_json(json)
28
+ Oj.load(json)
29
+ end
30
+ else
31
+ require 'json'
32
+ def self.to_json(object)
33
+ object.to_json
34
+ end
35
+
36
+ def self.load_json(json)
37
+ JSON.parse(json)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ #vi: ts=2 sw=2 et
2
+ require 'common'
3
+ require 'facets/string'
4
+
5
+ require 'lims-core/persistence/session'
6
+
7
+ module Lims::Core
8
+ # Generic persistence layer.
9
+ # The main objects are {Persistence::Session Session} which
10
+ # is in charge of saving and restoring object and {Persistence::Store} via Persistors.
11
+ # Persistors are mixins specific to each persistence types.
12
+ # For example, see the {Sequel::Persistor}.
13
+ module Persistence
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ require 'lims-core/persistence/filter'
2
+ require 'lims-core/resource'
3
+
4
+
5
+ module Lims::Core
6
+ module Persistence
7
+ # Filter performing a comparison between the resource field's value
8
+ # and a given value.
9
+ # Key being the name of the resource's field and the value is a Hash.
10
+ # The key of the hash is a comparison operator
11
+ # and the value is the given value the filter do the comparison against.
12
+ #
13
+ # @example
14
+ #
15
+ # "model": "kit",
16
+ # "criteria": {
17
+ # "comparison": {
18
+ # "expires": {
19
+ # ">=": "2013-04-24"
20
+ # }
21
+ # }
22
+ # }
23
+ #
24
+ # Will look for all kits expires after the given date ("2013-04-24").
25
+ #
26
+ class ComparisonFilter < Filter
27
+ include Resource
28
+
29
+ NOT_IN_ROOT = 1
30
+
31
+ attribute :criteria, Hash, :required => true
32
+
33
+ # For Sequel, keys needs to be a Symbol to be seen as column.
34
+ # String are seen as 'value'
35
+ def initialize(criteria)
36
+ criteria = { :criteria => criteria } unless criteria.include?(:criteria)
37
+ criteria[:criteria].rekey!{ |k| k.to_sym }
38
+ super(criteria)
39
+ end
40
+
41
+ def call(persistor)
42
+ persistor.comparison_filter(criteria[:comparison])
43
+ end
44
+ end
45
+ end
46
+
47
+ class Persistor
48
+ # @param [Hash] criteria a
49
+ # @return [Persistor]
50
+ def comparison_filter(criteria)
51
+ raise NotImplementedError, "comparison_filter methods needs to be implemented for subclass of Persistor"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ #vi: ts=2 sw=2 et
2
+ require 'common'
3
+
4
+ module Lims::Core
5
+ module Persistence
6
+ # @abstract Base class of all filters.
7
+ # A filter acts on persistors and can be chained.
8
+ # Note: This class is not really usefull in a *Ruby world* and is mainly
9
+ # here for documentation.
10
+ class Filter
11
+ # Transform a persistor to a "filtered persistor"
12
+ # The filtered persistor loading only the filtered object.
13
+ # Note that the actual implementation of the filter depends on the
14
+ # *type* of the persistor (Sequel for example).
15
+ # @param persistor [Persistence::Persistor]
16
+ # @return [Persistor]
17
+ def call(persistor)
18
+ raise NotImplementedError
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,55 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+
3
+ require 'common'
4
+
5
+ module Lims::Core
6
+ module Persistence
7
+ # Mixing giving an identity map behavior
8
+ # ie a map (both way) between id and object
9
+ module IdentityMap
10
+ # Raised if there is any duplicate in the identity map
11
+ class DuplicateError < RuntimeError
12
+ end
13
+
14
+ #Raised if the `id` is already associated to a different `object`
15
+ class DuplicateIdError <DuplicateError
16
+ end
17
+
18
+ #Raised if the `object` is already associated to a different `id`
19
+ class DuplicateObjectError < DuplicateError
20
+ end
21
+
22
+ # Look for the id associated to an object and yield it to the block
23
+ # if found.
24
+ def id_for(object, &block)
25
+ @object_to_id[object].andtap(&block)
26
+ end
27
+
28
+ # Look for the object associated to an object and yield it to the block
29
+ # if found.
30
+ def object_for(id, &block)
31
+ @id_to_object[id].andtap(&block)
32
+ end
33
+
34
+ # bound an id to an object
35
+ def map_id_object(id, object)
36
+ return nil unless id && object
37
+ raise DuplicateIdError, id unless @id_to_object.fetch(id, object).equal? object
38
+ raise DuplicateObjectError, object unless @object_to_id.fetch(object, id).equal? id
39
+ @id_to_object[id] = object
40
+ @object_to_id[object] = id
41
+ end
42
+
43
+ def initialize(*args, &block)
44
+ super(*args, &block)
45
+ @id_to_object = {}
46
+ @object_to_id = {}
47
+ end
48
+
49
+ # Class version
50
+ class Class
51
+ include IdentityMap
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ require('lims-core/persistence/logger/persistor')
2
+ require('lims-core/persistence/logger/session')
3
+ require('lims-core/persistence/logger/store')
4
+ require('lims-core/persistence/message_bus')
5
+ require('lims-core/persistence/persistor')
@@ -0,0 +1,35 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+
3
+
4
+ module Lims::Core
5
+ module Persistence
6
+ module Logger
7
+ # Mixin giving extended the persistor classes with
8
+ # the Logger (save) behavior.
9
+ module Persistor
10
+ private
11
+
12
+ # Load an object to the underlying logger
13
+ # @param [Resource] object the object
14
+ # @return the Id if save successful
15
+ def save_raw(object, *params)
16
+ case object
17
+ when Resource then @session.log("#{object.class.name}: #{filter_attributes_on_save(object.attributes)}")
18
+ else
19
+ @session.log("#{object.inspect}")
20
+ end
21
+ object
22
+ end
23
+
24
+ def save_as_aggregation(source_id, target, *params)
25
+ @session.with_indent("#{params} - ") do
26
+ super(source_id, target)
27
+ end
28
+ end
29
+
30
+ def save_raw_association(source_id, target_id, *params)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ # vi: ts=2:sts=2:et:sw=2 spell:spelllang=en
2
+
3
+ require 'logger'
4
+ require 'lims-core/persistence/session'
5
+
6
+ module Lims::Core
7
+ module Persistence
8
+ module Logger
9
+ # Logger specific implementation of a {Persistence::Session Session}.
10
+ class Session < Persistence::Session
11
+
12
+ attr_reader :indent_level
13
+ def initialize(*args, &block)
14
+ @indent_level = ""
15
+ super(*args, &block)
16
+ end
17
+
18
+ def log(msg)
19
+ @store.log(indent_level+msg)
20
+ end
21
+
22
+ # Execute a block with the specified indent level indicator.
23
+ # @param [String] indent the indent level indicator
24
+ def with_indent(indent=" - ", &block)
25
+ temporarily('@indent_level' => @indent_level+indent, &block)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ # vi: ts=2:sts=2:et:sw=2
2
+
3
+ require 'logger'
4
+ require 'lims-core/persistence'
5
+ require 'lims-core/persistence/store'
6
+ require 'lims-core/persistence/logger/session'
7
+ require 'lims-core/persistence/logger/persistor'
8
+
9
+ module Lims::Core
10
+ module Persistence
11
+ module Logger
12
+ # An Logger::Store, a store 'logging' object instead of
13
+ # saving them.
14
+ class Store < Persistence::Store
15
+ attr_reader :logger
16
+ attr_reader :method
17
+
18
+ # Create a store with an underlying logger.
19
+ # @param [Logger, file] logger
20
+ # @param [Symbol, String] method the method call to
21
+ # send information to the logger.
22
+ def initialize(logger, method=:info, *args)
23
+ @logger = case logger
24
+ when ::Logger then logger
25
+ else ::Logger.new(logger)
26
+ end
27
+ @method = method
28
+ super(*args)
29
+ end
30
+
31
+ def log(msg)
32
+ @logger.send(@method, msg)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,131 @@
1
+ require 'bunny'
2
+ require 'common'
3
+
4
+ module Lims
5
+ module Core
6
+ module Persistence
7
+
8
+ # Basic methods to publish messages on the bus
9
+ # Use the bunny gem as RabbitMQ client
10
+ class MessageBus
11
+
12
+ include Virtus
13
+ include Aequitas
14
+ attribute :connection_uri, String, :required => true, :writer => :private
15
+ attribute :exchange_name, String, :required => true, :writer => :private
16
+ # By default, exchange_type is topic
17
+ attribute :exchange_type, String, :required => true, :writer => :private
18
+ attribute :durable, Boolean, :required => true, :writer => :private
19
+ attribute :prefetch_number, Integer, :required => true, :writer => :private
20
+ attribute :heart_beat, Integer, :required => false, :writer => :private
21
+ attribute :backend_application_id, String, :required => true
22
+
23
+ # Exception ConnectionError raised after a failed connection
24
+ # to RabbitMQ server.
25
+ class ConnectionError < StandardError
26
+ end
27
+
28
+ # Exception InvalidSettingsError raised after a setting error
29
+ class InvalidSettingsError < StandardError
30
+ end
31
+
32
+ # Initialize the message bus and check the required options
33
+ # are passed as parameters.
34
+ # @param [Hash] settings
35
+ def initialize(settings = {})
36
+ raise InvalidSettingsError, "MessageBug settings are empty" if settings == nil
37
+ @heart_beat = settings["heart_beat"]
38
+ @connection_uri = settings["url"]
39
+ @exchange_name = settings["exchange_name"]
40
+ @exchange_type = settings["exchange_type"] || "topic"
41
+ @durable = settings["durable"]
42
+ @prefetch_number = settings["prefetch_number"]
43
+ @backend_application_id = settings["backend_application_id"]
44
+ end
45
+
46
+ # sets backend_app_id, but just once,
47
+ # otherwise raise am InvalidSettings error
48
+ def backend_application_id=(backend_application_id)
49
+ raise InvalidSettingsError, "Backend Application ID has been set already." if @backend_application_id
50
+ @backend_application_id = backend_application_id
51
+ end
52
+
53
+ # Executed after a connection loss
54
+ # The exception should be catched and rollback the actions.
55
+ def connection_failure_handler
56
+ Proc.new do
57
+ raise ConnectionError, "can't connect to RabbitMQ server"
58
+ end
59
+ end
60
+
61
+ # Create a new connection to the broker using
62
+ # the connection settings.
63
+ # Create a channel and setup a new exchange.
64
+ def connect
65
+ begin
66
+ if valid?
67
+ options = @heart_beat ? { :heartbeat => heart_beat } : {}
68
+ @connection = Bunny.new(connection_uri, options)
69
+ @connection.start
70
+ @channel = @connection.create_channel
71
+ set_prefetch_number(prefetch_number)
72
+ set_exchange(exchange_name, :durable => durable)
73
+ else
74
+ raise InvalidSettingsError, "settings are invalid"
75
+ end
76
+ rescue Bunny::TCPConnectionFailed, Bunny::PossibleAuthenticationFailureError => e
77
+ connection_failure_handler.call
78
+ end
79
+ end
80
+
81
+ # Close the connection
82
+ def close
83
+ @connection.close
84
+ end
85
+
86
+ # Create (or get if it already exists) a new topic
87
+ # exchange with the given options.
88
+ # Especially, the durable option can be set here to
89
+ # mark the exchange as durable (survive a server restart)
90
+ # @param [String] name
91
+ # @param [Hash] exchange options
92
+ def set_exchange(exchange_name, options = {})
93
+ @exchange = Bunny::Exchange.new(@channel, exchange_type.to_sym, exchange_name , options)
94
+ end
95
+ private :set_exchange
96
+
97
+ # Specifies the number of messages to prefetch.
98
+ # @param [int] number of messages to prefetch
99
+ def set_prefetch_number(number)
100
+ @channel.prefetch(number)
101
+ end
102
+ private :set_prefetch_number
103
+
104
+ # Set the message persistence behaviour.
105
+ # If persistent, the message will be persisted to disk
106
+ # and remain in the queue until it is consumed.
107
+ # Survive a server restart.
108
+ # BUNNY ISSUE: bunny0.9pre4 hardcodes the persistent option.
109
+ # @see lib/bunny/channel.rb:174 :delivery_mode => 2
110
+ # It is set all the time, meaning the messages will survive
111
+ # a server restart, if the queue and the exchange are durable.
112
+ # @param [Bool] persistence
113
+ def set_message_persistence(persistent)
114
+ @message_persistence = persistent
115
+ end
116
+
117
+ # Publish a message on the bus with the given options
118
+ # The routing key is passed in the options.
119
+ # @param [String] JSON message
120
+ # @param [Hash] publishing options
121
+ def publish(message, options = {})
122
+ raise ConnectionError, "exchange is not reachable" unless @exchange.instance_of?(Bunny::Exchange)
123
+
124
+ options.merge!(:persistent => @message_persistence) unless @message_persistence.nil?
125
+ options.merge!(:app_id => @backend_application_id) if @backend_application_id
126
+ @exchange.publish(message, options)
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end