caruby-core 1.5.5 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. data/Gemfile +9 -0
  2. data/History.md +5 -1
  3. data/lib/caruby.rb +3 -5
  4. data/lib/caruby/caruby-src.tar.gz +0 -0
  5. data/lib/caruby/database.rb +53 -69
  6. data/lib/caruby/database/application_service.rb +25 -0
  7. data/lib/caruby/database/cache.rb +60 -0
  8. data/lib/caruby/database/fetched_matcher.rb +52 -38
  9. data/lib/caruby/database/lazy_loader.rb +4 -4
  10. data/lib/caruby/database/operation.rb +34 -0
  11. data/lib/caruby/database/persistable.rb +171 -86
  12. data/lib/caruby/database/persistence_service.rb +32 -34
  13. data/lib/caruby/database/persistifier.rb +100 -43
  14. data/lib/caruby/database/reader.rb +107 -85
  15. data/lib/caruby/database/reader_template_builder.rb +60 -0
  16. data/lib/caruby/database/saved_matcher.rb +3 -3
  17. data/lib/caruby/database/sql_executor.rb +88 -17
  18. data/lib/caruby/database/writer.rb +213 -177
  19. data/lib/caruby/database/writer_template_builder.rb +334 -0
  20. data/lib/caruby/{util → helpers}/controlled_value.rb +0 -0
  21. data/lib/caruby/{util → helpers}/coordinate.rb +4 -4
  22. data/lib/caruby/{util → helpers}/person.rb +3 -3
  23. data/lib/caruby/{util → helpers}/properties.rb +7 -9
  24. data/lib/caruby/{util → helpers}/roman.rb +2 -2
  25. data/lib/caruby/{util → helpers}/version.rb +1 -1
  26. data/lib/caruby/json/deserializer.rb +2 -2
  27. data/lib/caruby/json/serializer.rb +49 -7
  28. data/lib/caruby/metadata.rb +30 -0
  29. data/lib/caruby/metadata/java_property.rb +21 -0
  30. data/lib/caruby/metadata/propertied.rb +191 -0
  31. data/lib/caruby/metadata/property.rb +22 -0
  32. data/lib/caruby/metadata/property_characteristics.rb +201 -0
  33. data/lib/caruby/migration/migratable.rb +11 -182
  34. data/lib/caruby/rdbi/driver/jdbc.rb +446 -0
  35. data/lib/caruby/resource.rb +20 -823
  36. data/lib/caruby/version.rb +1 -1
  37. data/test/lib/caruby/database/cache_test.rb +54 -0
  38. data/test/lib/caruby/{util → helpers}/controlled_value_test.rb +3 -5
  39. data/test/lib/caruby/{util → helpers}/person_test.rb +4 -6
  40. data/test/lib/caruby/helpers/properties_test.rb +34 -0
  41. data/test/lib/caruby/{util → helpers}/roman_test.rb +2 -3
  42. data/test/lib/caruby/{util → helpers}/version_test.rb +2 -3
  43. data/test/lib/helper.rb +7 -0
  44. metadata +161 -214
  45. data/lib/caruby/cli/application.rb +0 -36
  46. data/lib/caruby/cli/command.rb +0 -202
  47. data/lib/caruby/csv/csv_mapper.rb +0 -159
  48. data/lib/caruby/csv/csvio.rb +0 -203
  49. data/lib/caruby/database/search_template_builder.rb +0 -56
  50. data/lib/caruby/database/store_template_builder.rb +0 -278
  51. data/lib/caruby/domain.rb +0 -193
  52. data/lib/caruby/domain/attribute.rb +0 -584
  53. data/lib/caruby/domain/attributes.rb +0 -628
  54. data/lib/caruby/domain/dependency.rb +0 -225
  55. data/lib/caruby/domain/id_alias.rb +0 -22
  56. data/lib/caruby/domain/importer.rb +0 -183
  57. data/lib/caruby/domain/introspection.rb +0 -176
  58. data/lib/caruby/domain/inverse.rb +0 -172
  59. data/lib/caruby/domain/inversible.rb +0 -90
  60. data/lib/caruby/domain/java_attribute.rb +0 -173
  61. data/lib/caruby/domain/merge.rb +0 -185
  62. data/lib/caruby/domain/metadata.rb +0 -142
  63. data/lib/caruby/domain/mixin.rb +0 -35
  64. data/lib/caruby/domain/properties.rb +0 -95
  65. data/lib/caruby/domain/reference_visitor.rb +0 -428
  66. data/lib/caruby/domain/uniquify.rb +0 -50
  67. data/lib/caruby/import/java.rb +0 -387
  68. data/lib/caruby/migration/migrator.rb +0 -918
  69. data/lib/caruby/migration/resource_module.rb +0 -9
  70. data/lib/caruby/migration/uniquify.rb +0 -17
  71. data/lib/caruby/util/attribute_path.rb +0 -44
  72. data/lib/caruby/util/cache.rb +0 -56
  73. data/lib/caruby/util/class.rb +0 -149
  74. data/lib/caruby/util/collection.rb +0 -1152
  75. data/lib/caruby/util/domain_extent.rb +0 -46
  76. data/lib/caruby/util/file_separator.rb +0 -65
  77. data/lib/caruby/util/inflector.rb +0 -27
  78. data/lib/caruby/util/log.rb +0 -95
  79. data/lib/caruby/util/math.rb +0 -12
  80. data/lib/caruby/util/merge.rb +0 -59
  81. data/lib/caruby/util/module.rb +0 -18
  82. data/lib/caruby/util/options.rb +0 -97
  83. data/lib/caruby/util/partial_order.rb +0 -35
  84. data/lib/caruby/util/pretty_print.rb +0 -204
  85. data/lib/caruby/util/stopwatch.rb +0 -74
  86. data/lib/caruby/util/topological_sync_enumerator.rb +0 -62
  87. data/lib/caruby/util/transitive_closure.rb +0 -55
  88. data/lib/caruby/util/tree.rb +0 -48
  89. data/lib/caruby/util/trie.rb +0 -37
  90. data/lib/caruby/util/uniquifier.rb +0 -30
  91. data/lib/caruby/util/validation.rb +0 -20
  92. data/lib/caruby/util/visitor.rb +0 -365
  93. data/lib/caruby/util/weak_hash.rb +0 -36
  94. data/test/lib/caruby/csv/csv_mapper_test.rb +0 -40
  95. data/test/lib/caruby/csv/csvio_test.rb +0 -69
  96. data/test/lib/caruby/database/persistable_test.rb +0 -92
  97. data/test/lib/caruby/domain/domain_test.rb +0 -112
  98. data/test/lib/caruby/domain/inversible_test.rb +0 -99
  99. data/test/lib/caruby/domain/reference_visitor_test.rb +0 -130
  100. data/test/lib/caruby/import/java_test.rb +0 -80
  101. data/test/lib/caruby/import/mixed_case_test.rb +0 -14
  102. data/test/lib/caruby/migration/test_case.rb +0 -102
  103. data/test/lib/caruby/test_case.rb +0 -230
  104. data/test/lib/caruby/util/cache_test.rb +0 -23
  105. data/test/lib/caruby/util/class_test.rb +0 -61
  106. data/test/lib/caruby/util/collection_test.rb +0 -398
  107. data/test/lib/caruby/util/command_test.rb +0 -55
  108. data/test/lib/caruby/util/domain_extent_test.rb +0 -60
  109. data/test/lib/caruby/util/file_separator_test.rb +0 -30
  110. data/test/lib/caruby/util/inflector_test.rb +0 -12
  111. data/test/lib/caruby/util/lazy_hash_test.rb +0 -34
  112. data/test/lib/caruby/util/merge_test.rb +0 -83
  113. data/test/lib/caruby/util/module_test.rb +0 -25
  114. data/test/lib/caruby/util/options_test.rb +0 -59
  115. data/test/lib/caruby/util/partial_order_test.rb +0 -42
  116. data/test/lib/caruby/util/pretty_print_test.rb +0 -85
  117. data/test/lib/caruby/util/properties_test.rb +0 -50
  118. data/test/lib/caruby/util/stopwatch_test.rb +0 -18
  119. data/test/lib/caruby/util/topological_sync_enumerator_test.rb +0 -69
  120. data/test/lib/caruby/util/transitive_closure_test.rb +0 -67
  121. data/test/lib/caruby/util/tree_test.rb +0 -23
  122. data/test/lib/caruby/util/trie_test.rb +0 -14
  123. data/test/lib/caruby/util/visitor_test.rb +0 -278
  124. data/test/lib/caruby/util/weak_hash_test.rb +0 -45
  125. data/test/lib/examples/clinical_trials/migration/migration_test.rb +0 -58
  126. data/test/lib/examples/clinical_trials/migration/test_case.rb +0 -38
@@ -1,56 +0,0 @@
1
- require 'caruby/util/log'
2
- require 'caruby/util/pretty_print'
3
-
4
- module CaRuby
5
- # SearchTemplateBuilder builds a template suitable for a caCORE saarch database operation.
6
- class SearchTemplateBuilder
7
- # Returns a template for matching the domain object obj and the optional hash values.
8
- # The default hash attributes are the {Domain::Attributes#searchable_attributes}.
9
- # The template includes only the non-domain attributes of the hash references.
10
- #
11
- # @quirk caCORE Because of caCORE API limitations, the obj searchable attribute
12
- # values are limited to the following:
13
- # * non-domain attribute values
14
- # * non-collection domain attribute references which contain a key
15
- #
16
- # @quirk caCORE the caCORE query builder breaks on reference cycles and is easily confused
17
- # by extraneous references, so it is necessary to search with a template instead that contains
18
- # only references essential to the search. Each reference is confirmed to exist and the
19
- # reference content in the template consists entirely of the fetched identifier attribute.
20
- def build_template(obj, hash=nil)
21
- # split the attributes into reference and non-reference attributes.
22
- # the new search template object is built from the non-reference attributes.
23
- # the reference attributes values are copied and added.
24
- logger.debug { "Building search template for #{obj.qp}..." }
25
- hash ||= obj.value_hash(obj.class.searchable_attributes)
26
- # the searchable attribute => value hash
27
- rh, nrh = hash.split { |attr, value| Resource === value }
28
- # make the search template from the non-reference attributes
29
- tmpl = obj.class.new.merge_attributes(nrh)
30
- # get references for the search template
31
- unless rh.empty? then
32
- logger.debug { "Collecting search reference parameters for #{obj.qp} from attributes #{rh.keys.to_series}..." }
33
- end
34
- rh.each { |attr, ref| add_search_template_reference(tmpl, ref, attr) }
35
- tmpl
36
- end
37
-
38
- private
39
-
40
- # Sets the template attribute to a new search reference object created from the given
41
- # source domain object. The reference contains only the source identifier, if it exists,
42
- # or the source non-domain attributes otherwise.
43
- #
44
- # @return [Resource] the search reference
45
- def add_search_template_reference(template, source, attribute)
46
- ref = source.identifier ? source.copy(:identifier) : source.copy
47
- # Disable inverse integrity, since the template attribute assignment might have added a reference
48
- # from ref to template, which introduces a template => ref => template cycle that causes a caCORE
49
- # search infinite loop. Use the Java property writer instead.
50
- wtr = template.class.attribute_metadata(attribute).property_writer
51
- template.send(wtr, ref)
52
- logger.debug { "Search reference parameter #{attribute} for #{template.qp} set to #{ref} copied from #{source.qp}" }
53
- ref
54
- end
55
- end
56
- end
@@ -1,278 +0,0 @@
1
- require 'caruby/domain/reference_visitor'
2
-
3
- module CaRuby
4
- # StoreTemplateBuilder creates a template suitable for a create or update database operation.
5
- class StoreTemplateBuilder
6
- # Creates a new StoreTemplateBuilder for the given database. The attributes to merge into
7
- # the template are determined by the block given to this initializer, filtered as follows:
8
- # * If the save operation is a create, then exclude the auto-generated attributes.
9
- # * If the visited object has an identifier, then include only those attributes
10
- # which {Domain::Attribute#cascade_update_to_create?} or have an identifier.
11
- #
12
- # @param [Database] database the target database
13
- # @yield [ref] the required selector block which determines which attributes are copied into the template
14
- # @yieldparam [Resource] ref the domain object to copy
15
- def initialize(database)
16
- @database = database
17
- unless block_given? then
18
- raise ArgumentError.new("StoreTemplateBuilder is missing the required template copy attribute selector block")
19
- end
20
-
21
- # the mergeable attributes filter the given block with exclusions
22
- @mergeable = Proc.new { |obj| mergeable_attributes(obj, yield(obj)) }
23
- # the storable prerequisite reference visitor
24
- @prereq_vstr = ReferenceVisitor.new(:prune_cycle) { |ref| savable_template_attributes(ref) }
25
-
26
- # the savable attributes filter the given block with exclusions
27
- savable = Proc.new { |obj| savable_attributes(obj, yield(obj)) }
28
- # the domain attributes to copy is determined by the constructor caller
29
- # @quirk caTissue must copy all of the non-domain attributes rather than just the identifier,
30
- # since caTissue auto-generated Specimen update requires the parent collection status. This
31
- # is the only known occurrence of a referenced object required non-identifier attribute.
32
- # The copy attributes are parameterized by the top-level save target.
33
- copier = Proc.new do |src|
34
- tgt = src.copy
35
- logger.debug { "Store template builder copied #{src.qp} into #{tgt}." }
36
- copy_proxied_save_references(src, tgt)
37
- tgt
38
- end
39
- # the template copier
40
- @copy_vstr = CopyVisitor.new(:mergeable => savable, :copier => copier) { |ref| savable_template_attributes(ref) }
41
- end
42
-
43
- # Returns a new domain object which serves as the argument for obj create or update.
44
- #
45
- # This method copies a portion of the obj object graph to a template object.
46
- # The template object graph consists of copies of obj object graph which are necessary
47
- # to store obj. The template object graph contains only those references which are
48
- # essential to the store operation.
49
- #
50
- # @quirk caCORE +caCORE+ expects the store argument to be carefully prepared prior to
51
- # the create or update. build_storable_template culls the target object with a template
52
- # which includes only those references which are necessary for the store to succeed.
53
- # This template builder ensures that mandatory independent references exist. Cascaded
54
- # dependent references are included in the template but are not created before submission
55
- # to +caCORE+. These reference attribute distinctions are implicit application rules which
56
- # are explicated in the +caRuby+ application domain class definition using Metadata
57
- # methods.
58
- #
59
- # @quirk caCORE +caCORE+ create issues an error if a create argument directly or
60
- # indirectly references a non-cascaded domain object without an identifier, even if the
61
- # reference is not relevant to the create. The template returned by this method elides
62
- # all non-essential references.
63
- #
64
- # @quirk caCORE application business logic performs unnecessary verification
65
- # of uncascaded references as if they were a cascaded create. This can result in
66
- # an obscure ApplicationException. The server.log stack trace indicates the
67
- # extraneous verification code. For example, +caTissue+ +NewSpecimenBizLogic.validateStorageContainer+
68
- # is unnecessarily called on a SpecimenCollectionGroup (SCG) update. SCG does not
69
- # cascade to Specimen, but caTissue considers the SCG update a Specimen create
70
- # anyway if the SCG references a Specimen without an identifier. The Specimen
71
- # business logic then raises an exception when it finds a StorageContainer
72
- # without an identifier in the Specimen object graph. Therefore, an update must
73
- # build a storable template which prunes the update object graph to exclude uncascaded
74
- # objects. These uncascaded objects should be ignored by the application but aren't.
75
- #
76
- # @param [Resource] obj the domain object to save
77
- # @return [Resource] the template to use as the caCORE argument
78
- def build_template(obj, autogenerated=false)
79
- # set the database operation subject
80
- @subject = obj
81
- # prepare the object for a store operation
82
- ensure_storable(obj)
83
- # copy the cascade hierarchy
84
- logger.debug { "Building storable template for #{obj.qp}..." }
85
- tmpl = @copy_vstr.visit(obj)
86
- logger.debug { "Built #{obj.qp} template #{tmpl.qp} by mapping references #{@copy_vstr.matches.qp}" }
87
- tmpl
88
- end
89
-
90
- private
91
-
92
- # Ensure that the given domain object obj can be created or updated by setting the identifier for
93
- # each independent reference in the create template object graph.
94
- #
95
- # @quirk caCORE +caCORE+ raises an ApplicationException if an independent reference in the create or
96
- # update argument does not have an identifier. The +caCORE+ server log error is as follows:
97
- # java.lang.IllegalArgumentException: id to load is required for loading
98
- # The server log stack trace indicates a bizlogic line that offers a clue to the offending reference.
99
- def ensure_storable(obj)
100
- # Add defaults, which might introduce independent references. Enable the lazy loader to fetch
101
- # create references from the database where needed to build defaults.
102
- obj.add_defaults
103
- # create the prerequisite references if necessary
104
- prereqs = collect_prerequisites(obj)
105
- unless prereqs.empty? then
106
- logger.debug { "Ensuring references for #{obj.qp} exist: #{prereqs.map { |ref| ref.qp }.to_series}..." }
107
- @database.ensure_exists(prereqs)
108
- logger.debug { "Prerequisite references for #{obj.qp} exist: #{prereqs.map { |ref| ref }.to_series}." }
109
- end
110
- # Verify that the object is complete
111
- obj.validate
112
- end
113
-
114
- # Returns the attributes to visit in building the template for the given
115
- # domain object. The visitable attributes consist of the following:
116
- # * The {Domain::Attributes#unproxied_savable_template_attributes} filtered as follows:
117
- # * If the database operation is a create, then exclude the cascaded attributes.
118
- # * If the given object has an identifier, then exclude the attributes which
119
- # have the the :no_cascade_update_to_create flag set.
120
- # * The {Domain::Attributes#proxied_savable_template_attributes} are included if and
121
- # only if every referenced object has an identifier, and therefore does not
122
- # need to be proxied.
123
- #
124
- # @quirk caTissue caTissue ignores some references, e.g. Participant CPR, and auto-generates
125
- # the values instead. Therefore, the create template builder excludes these auto-generated
126
- # attributes. After the create, the auto-generated references are merged into the created
127
- # object graph and the references are updated if necessary.
128
- #
129
- # @param [Resource] obj the domain object copied to the update template
130
- # @return [<Symbol>] the reference attributes to include in the update template
131
- def savable_template_attributes(obj)
132
- # The starting set of candidate attributes is the unproxied cascaded references.
133
- unproxied = savable_attributes(obj, obj.class.unproxied_savable_template_attributes)
134
- # The proxied attributes to save.
135
- proxied = savable_proxied_attributes(obj)
136
- # The combined set of savable attributes
137
- proxied.empty? ? unproxied : unproxied + proxied
138
- end
139
-
140
- # Filters the given attributes, if necessary, to exclude attributes as follows:
141
- # * If the save operation is a create, then exclude the
142
- # {Domain::Attribute#autogenerated_on_create?} attributes.
143
- #
144
- # @param [Resource] obj the visited domain object
145
- # @param [Attributes::Filter] the savable attribute filter
146
- # @return [Attributes::Filter] the composed attribute filter
147
- def mergeable_attributes(obj, attributes)
148
- # If this is an update, then there is no filter on the given attributes.
149
- return attributes if @subject.identifier
150
- # This is a create: ignore the optional auto-generated attributes.
151
- mas = obj.mandatory_attributes.to_set
152
- attributes.compose { |attr_md| mas.include?(attr_md.to_sym) or not attr_md.autogenerated_on_create? }
153
- end
154
-
155
- # Composes the given attributes, if necessary, to exclude attributes as follows:
156
- # * If the save operation is a create, then exclude the auto-generated attributes.
157
- # * If the visited object has an identifier, then include only those attributes
158
- # which {Domain::Attribute#cascade_update_to_create?} or have an identifier.
159
- #
160
- # @param (see #mergeable_attributes)
161
- # @return (see #mergeable_attributes)
162
- def savable_attributes(obj, attributes)
163
- mgbl = mergeable_attributes(obj, attributes)
164
- return mgbl if obj.identifier.nil?
165
- # The currently visited object is an update: include attributes which
166
- # either cascade update to create or have saved references.
167
- mgbl.compose do |attr_md|
168
- attr_md.cascade_update_to_create? or Persistable.saved?(obj.send(attr_md.to_sym))
169
- end
170
- end
171
-
172
- # Returns the proxied attributes to save. A proxied attribute is included only if the proxied
173
- # dependents have an identifier, since those without an identifer are created separately via
174
- # the proxy.
175
- #
176
- # @param [Resource] obj the visited domain object
177
- # @return [<Attribute>] the proxied cascaded attributes with an unsaved reference
178
- def savable_proxied_attributes(obj)
179
- # Include a proxied reference only if the proxied dependents have an identifier,
180
- # since those without an identifer are created separately via the proxy.
181
- obj.class.proxied_savable_template_attributes.reject do |attr|
182
- ref = obj.send(attr)
183
- case ref
184
- when Enumerable then ref.any? { |dep| not dep.identifier }
185
- when Resource then not ref.identifier
186
- end
187
- end
188
- end
189
-
190
- # Copies proxied references as needed.
191
- #
192
- # @quirk caTissue even though Specimen save cascades to SpecimenPosition,
193
- # SpecimenPosition cannot be updated directly. Rather than simply not
194
- # cascading to the SpecimenPosition, caTissue checks a Specimen save argument
195
- # to ensure that the SpecimenPosition reflects the current database state
196
- # rather than the desired cascaded state. Play along with this bizarre
197
- # mechanism by adding our own bizarre work-around mechanism to copy a
198
- # proxied reference only if it has an identifier. This works only because
199
- # another work-around in the #{Writer} updates proxied
200
- # references via the proxy create before building the update template.
201
- def copy_proxied_save_references(obj, template)
202
- return unless obj.identifier
203
- obj.class.proxied_savable_template_attributes.each do |attr|
204
- # the proxy source
205
- ref = obj.send(attr)
206
- case ref
207
- when Enumerable then
208
- # recurse on the source collection
209
- coll = template.send(attr)
210
- ref.each do |dep|
211
- copy = copy_proxied_save_reference(obj, attr, template, dep)
212
- coll << copy if copy
213
- end
214
- when Resource then
215
- # copy the source
216
- copy = copy_proxied_save_reference(obj, attr, template, ref)
217
- # set the attribute to the copy
218
- template.set_attribute(attr, copy) if copy
219
- end
220
- end
221
- end
222
-
223
- # Copies a proxied reference.
224
- #
225
- # @return [Resource, nil] the copy, or nil if no copy is made
226
- def copy_proxied_save_reference(obj, attribute, template, proxied)
227
- # only copy an existing proxied
228
- return unless proxied.identifier
229
- # the proxied attribute => value hash
230
- vh = proxied.value_hash
231
- # map references to either the copied owner or a new copy of the reference
232
- tvh = vh.transform { |value| Resource === value ? (value == obj ? template : value.copy) : value }
233
- # the copy with the adjusted values
234
- copy = proxied.class.new.merge_attributes(tvh)
235
- logger.debug { "Created #{obj.qp} proxied #{attribute} save template copy #{proxied.pp_s}." }
236
- copy
237
- end
238
-
239
- # @param [Resource] obj the domain object to store
240
- # @return [<Resource>] the references which must be created in order to store the object
241
- def collect_prerequisites(obj)
242
- prereqs = Set.new
243
- # visit the cascaded attributes
244
- @prereq_vstr.visit(obj) do |stbl|
245
- # Check each mergeable attribute for prerequisites. The mergeable attributes includes
246
- # both cascaded and independent attributes. The selection block filters for independent
247
- # domain objects which don't have an identifier.
248
- @mergeable.call(stbl).each_pair do |attr, attr_md|
249
- # Cascaded attributes are not prerequisite, since they are created when the owner is created.
250
- # Note that each non-prerequisite cascaded reference is still visited in order to ensure
251
- # that each independent object referenced by a cascaded reference is recognized as a
252
- # candidate prerequisite.
253
- next if attr_md.cascaded?
254
- # add qualified prerequisite attribute references
255
- stbl.send(attr).enumerate do |ref|
256
- # Add the prerequisite if it satisfies the prerequisite? condition.
257
- prereqs << ref if prerequisite?(ref, obj, attr)
258
- end
259
- end
260
- end
261
- prereqs
262
- end
263
-
264
- # A referenced object is a target object save prerequisite if none of the follwing is true:
265
- # * it is the target object
266
- # * it was already created
267
- # * it is in an immediate or recursive dependent of the target object
268
- # * the current save operation is in the context of creating the referenced object
269
- #
270
- # @param [Resource] ref the reference to check
271
- # @param [Resource] obj the object being stored
272
- # @param [Symbol] attribute the reference attribute
273
- # @return [Boolean] whether the reference should exist before storing the object
274
- def prerequisite?(ref, obj, attribute)
275
- not (ref == obj or ref.identifier or ref.owner_ancestor?(obj))
276
- end
277
- end
278
- end
data/lib/caruby/domain.rb DELETED
@@ -1,193 +0,0 @@
1
- require 'fileutils'
2
- require 'caruby/util/collection'
3
- require 'caruby/util/log'
4
- require 'caruby/domain/importer'
5
-
6
- module CaRuby
7
- class JavaImportError < StandardError; end;
8
-
9
- # The application and database connection access command line options.
10
- ACCESS_OPTS = [
11
- [:user, "--user USER", "the application login user"],
12
- [:password, "--password PSWD", "the application login password"],
13
- [:host, "--host HOST", "the application host name"],
14
- [:port, "--port PORT", "the application port number"],
15
- [:classpath, "--classpath PATH", "the application client classpath"],
16
- [:database_host, "--database_host HOST", "the database host name"],
17
- [:database_type, "--database_type TYPE", "the database type (mysql or oracle)"],
18
- [:database_driver, "--database_driver DRIVER", "the database driver string"],
19
- [:database_driver_class, "--database_driver_class CLASS", "the database driver class name"],
20
- [:database_port, "--database_port PORT", Integer, "the database port number"],
21
- [:database, "--database NAME", "the database name"],
22
- [:database_user, "--database_user USER", "the database login user"],
23
- [:database_password, "--database_password PSWD", "the database login password"]
24
- ]
25
-
26
- # Domain extends a Module with Java class {Metadata} support.
27
- #
28
- # A Java class is imported into Ruby either by including the given Resource module
29
- # or by referenceing the class name for the first time. For example, the
30
- # +ClinicalTrials+ wrapper for Java package +org.nci.ctms+ classes and
31
- # Ruby class definitions in the +domain+ subdirectory is enabled as follows:
32
- # module ClinicalTrials
33
- # PKG = 'org.nci.ctms'
34
- # SRC_DIR = File.join(File.dirname(__FILE__), 'domain')
35
- # CaRuby::Domain.extend_module(self, :package => PKG, :directory => SRC_DIR)
36
- #
37
- # The first reference by name to +ClinicalTrials::Subject+ imports the Java class
38
- # +org.nci.ctms.Subject+ into +ClinicalTrials+. The +Subject+ Java property meta-data
39
- # is introspected and the {Resource} module is included.
40
- module Domain
41
- # Extends the given module with importable Java class {Metadata} support.
42
- #
43
- # @param [Module] mod the module to extend
44
- # @param [{Symbol => Object}] opts the extension options
45
- # @option opts [Module] :metadata the optional {Metadata} extension (default {Metadata})
46
- # @option opts [Module] :mixin the optional mix-in module (default {Resource})
47
- # @option opts [String] :package the required Java package name
48
- # @option opts [String] :directory the optional directory of source class definitions to load
49
- def self.extend_module(mod, opts)
50
- mod.extend(self)
51
- Importer.extend_module(mod, opts)
52
- end
53
-
54
- # Loads the {#access_properties} and adds the +path+ property items to the Java classpath.
55
- #
56
- # @param [Module] mod the module to extend
57
- def self.extended(mod)
58
- super
59
- mod.ensure_classpath_defined
60
- end
61
-
62
- # Loads the application start-up properties on demand. The properties are defined in the properties
63
- # file or as environment variables.
64
- # The properties file path is a period followed by the lower-case application name in the home directory,
65
- # e.g. +~/.clincaltrials+ for the +ClinicalTrials+ application.
66
- #
67
- # The property file format is a series of property definitions in the form _property_: _value_.
68
- # The supported properties include the following:
69
- # * +host+ - the application server host (default +localhost+)
70
- # * +port+ - the application server port (default +8080+)
71
- # * +user+ - the application server login
72
- # * +password+ - the application service password
73
- # * +path+ or +classpath+ - the application client Java directories
74
- # * +database+ - the application database name
75
- # * +database_user+ - the application database connection userid
76
- # * +database_password+ - the application database connection password
77
- # * +database_host+ - the application database connection host (default +localhost+)
78
- # * +database_type+ - the application database type, + mysql+ or +oracle+ (default +mysql+)
79
- # * +database_driver+ - the application database connection driver (default is the database type default)
80
- # * +database_port+ - the application database connection port (default is the database type default)
81
- #
82
- # The +path+ value is one or more directories separated by a semi-colon(;) or colon (:)
83
- # Each path directory and all jar files within the directory are added to the caRuby execution
84
- # Java classpath.
85
- #
86
- # Each property has an environment variable counterpart given by
87
- #
88
- # @return [{Symbol => Object}] the caBIG application access properties
89
- def access_properties
90
- @rsc_props ||= load_access_properties
91
- end
92
-
93
- # Ensures that the application client classpath is defined. The classpath is defined
94
- # in the {#access_properties}. This method is called when a module extends this
95
- # Domain, before any application Java domain class is imported into JRuby.
96
- def ensure_classpath_defined
97
- # Loading the access properties on demand sets the classpath.
98
- access_properties
99
- end
100
-
101
- private
102
-
103
- # Loads the application start-up properties in the given file path.
104
- #
105
- # @return (see #access_properties)
106
- def load_access_properties
107
- # the properties file
108
- file = default_properties_file
109
- # the access properties
110
- props = file && File.exists?(file) ? load_properties_file(file) : {}
111
- # Look for environment overrides preceded by the uppercase module name,
112
- # e.g. CATISSUE_USER for the CaTissue module.
113
- load_environment_properties(props)
114
-
115
- # load the Java application jar path
116
- path = props[:classpath] || props[:path]
117
- if path then
118
- logger.info("Defining application classpath #{path}...")
119
- Java.add_path(path)
120
- end
121
-
122
- props
123
- end
124
-
125
- def load_properties_file(file)
126
- props = {}
127
- logger.info("Loading application properties from #{file}...")
128
- File.open(file).map do |line|
129
- # match the tolerant property definition
130
- match = PROP_DEF_REGEX.match(line.chomp.strip) || next
131
- # the property [name, value] tokens
132
- tokens = match.captures
133
- pname = tokens.first.to_sym
134
- # path is deprecated
135
- name = pname == :path ? :classpath : pname
136
- value = tokens.last
137
- # capture the property
138
- props[name] = value
139
- end
140
- props
141
- end
142
-
143
- def load_environment_properties(props)
144
- ACCESS_OPTS.each do |spec|
145
- # the access option symbol is the first specification item
146
- opt = spec[0]
147
- # the envvar value
148
- value = environment_property(opt) || next
149
- # override the file property with the envar value
150
- props[opt] = value
151
- logger.info("Set application property #{opt} from environment variable #{ev}.")
152
- end
153
- end
154
-
155
- # @param [Symbol] opt the property symbol, e.g. :user
156
- # @return [String, nil] the value of the corresponding environment variable, e.g. +CATISSUE_USER+
157
- def environment_property(opt)
158
- @env_prefix ||= name.gsub('::', '_').upcase
159
- ev = "#{@env_prefix}_#{opt.to_s.upcase}"
160
- value = ENV[ev]
161
- # If no classpath envvar, then try the deprecated path envvar.
162
- if value.nil? and opt == :classpath then
163
- environment_property(:path)
164
- else
165
- value
166
- end
167
- end
168
-
169
- # The property/value matcher, e.g.:
170
- # host: jacardi
171
- # host = jacardi
172
- # host=jacardi
173
- # name: J. Edgar Hoover
174
- # but not:
175
- # # host: jacardi
176
- # The captures are the trimmed property and value.
177
- PROP_DEF_REGEX = /^(\w+)(?:\s*[:=]\s*)([^#]+)/
178
-
179
- # @return [String] the default application properties file, given by +~/.+_name_,
180
- # where _name_ is the underscore unqualified module name, e.g. +~/.catissue+
181
- # for module +CaTissue+
182
- def default_properties_file
183
- home = ENV['HOME'] || ENV['USERPROFILE'] || '~'
184
- file = File.expand_path("#{home}/.#{name[/\w+$/].downcase}")
185
- if File.exists?(file) then
186
- file
187
- else
188
- logger.warn("The default #{name} application property file was not found: #{file}.")
189
- nil
190
- end
191
- end
192
- end
193
- end