caruby-core 1.4.9 → 1.5.1

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 (61) hide show
  1. data/History.md +48 -0
  2. data/lib/caruby/cli/command.rb +2 -1
  3. data/lib/caruby/csv/csv_mapper.rb +8 -8
  4. data/lib/caruby/database/persistable.rb +44 -65
  5. data/lib/caruby/database/persistence_service.rb +12 -9
  6. data/lib/caruby/database/persistifier.rb +14 -14
  7. data/lib/caruby/database/reader.rb +53 -51
  8. data/lib/caruby/database/search_template_builder.rb +9 -10
  9. data/lib/caruby/database/store_template_builder.rb +58 -58
  10. data/lib/caruby/database/writer.rb +96 -96
  11. data/lib/caruby/database.rb +19 -19
  12. data/lib/caruby/domain/attribute.rb +581 -0
  13. data/lib/caruby/domain/attributes.rb +615 -0
  14. data/lib/caruby/domain/dependency.rb +240 -0
  15. data/lib/caruby/domain/importer.rb +183 -0
  16. data/lib/caruby/domain/introspection.rb +176 -0
  17. data/lib/caruby/domain/inverse.rb +173 -0
  18. data/lib/caruby/domain/inversible.rb +1 -2
  19. data/lib/caruby/domain/java_attribute.rb +173 -0
  20. data/lib/caruby/domain/merge.rb +13 -10
  21. data/lib/caruby/domain/metadata.rb +141 -0
  22. data/lib/caruby/domain/mixin.rb +35 -0
  23. data/lib/caruby/domain/reference_visitor.rb +5 -3
  24. data/lib/caruby/domain.rb +340 -0
  25. data/lib/caruby/import/java.rb +29 -25
  26. data/lib/caruby/migration/migratable.rb +5 -5
  27. data/lib/caruby/migration/migrator.rb +19 -15
  28. data/lib/caruby/migration/resource_module.rb +1 -1
  29. data/lib/caruby/resource.rb +39 -30
  30. data/lib/caruby/util/collection.rb +94 -33
  31. data/lib/caruby/util/coordinate.rb +28 -2
  32. data/lib/caruby/util/log.rb +4 -4
  33. data/lib/caruby/util/module.rb +12 -28
  34. data/lib/caruby/util/partial_order.rb +9 -10
  35. data/lib/caruby/util/pretty_print.rb +46 -26
  36. data/lib/caruby/util/topological_sync_enumerator.rb +10 -4
  37. data/lib/caruby/util/transitive_closure.rb +2 -2
  38. data/lib/caruby/util/visitor.rb +1 -1
  39. data/lib/caruby/version.rb +1 -1
  40. data/test/lib/caruby/database/persistable_test.rb +1 -1
  41. data/test/lib/caruby/domain/domain_test.rb +14 -28
  42. data/test/lib/caruby/domain/inversible_test.rb +1 -1
  43. data/test/lib/caruby/import/java_test.rb +5 -0
  44. data/test/lib/caruby/migration/test_case.rb +0 -1
  45. data/test/lib/caruby/test_case.rb +9 -10
  46. data/test/lib/caruby/util/collection_test.rb +23 -5
  47. data/test/lib/caruby/util/module_test.rb +10 -14
  48. data/test/lib/caruby/util/partial_order_test.rb +16 -15
  49. data/test/lib/caruby/util/visitor_test.rb +1 -1
  50. data/test/lib/examples/galena/clinical_trials/migration/test_case.rb +1 -1
  51. metadata +16 -15
  52. data/History.txt +0 -44
  53. data/lib/caruby/domain/attribute_metadata.rb +0 -551
  54. data/lib/caruby/domain/java_attribute_metadata.rb +0 -183
  55. data/lib/caruby/domain/resource_attributes.rb +0 -565
  56. data/lib/caruby/domain/resource_dependency.rb +0 -217
  57. data/lib/caruby/domain/resource_introspection.rb +0 -160
  58. data/lib/caruby/domain/resource_inverse.rb +0 -151
  59. data/lib/caruby/domain/resource_metadata.rb +0 -155
  60. data/lib/caruby/domain/resource_module.rb +0 -370
  61. data/lib/caruby/yard/resource_metadata_handler.rb +0 -8
@@ -1,370 +0,0 @@
1
- require 'fileutils'
2
- require 'caruby/util/collection'
3
- require 'caruby/util/log'
4
-
5
- module CaRuby
6
- class JavaIncludeError < StandardError; end;
7
-
8
- # The application and database connection access command line options.
9
- ACCESS_OPTS = [
10
- [:user, "--user USER", "the application login user"],
11
- [:password, "--password PSWD", "the application login password"],
12
- [:host, "--host HOST", "the application host name"],
13
- [:port, "--port PORT", "the application port number"],
14
- [:classpath, "--classpath PATH", "the application client classpath"],
15
- [:database_host, "--database_host HOST", "the database host name"],
16
- [:database_type, "--database_type TYPE", "the database type (mysql or oracle)"],
17
- [:database_driver, "--database_driver DRIVER", "the database driver string"],
18
- [:database_driver_class, "--database_driver_class CLASS", "the database driver class name"],
19
- [:database_port, "--database_port PORT", Integer, "the database port number"],
20
- [:database, "--database NAME", "the database name"],
21
- [:database_user, "--database_user USER", "the database login user"],
22
- [:database_password, "--database_password PSWD", "the database login password"]
23
- ]
24
-
25
- # Importable extends a Module with Java class support.
26
- # The calling module must implement the following instance variables:
27
- # * +*java_package+ - the module Java package name
28
- # * +@mixin+ - the module which scopes the domain objects
29
- # See the ClinicalTrials module for an example.
30
- #
31
- # The application properties hash specifies the startup information,
32
- # including the user, password and application Java jar library path.
33
- # The properties are read from a property file. See {Properties} for
34
- # more information.
35
- #
36
- # A Java class is imported into Ruby either by directly calling the extended
37
- # module {#resource_import} method or on demand by referencing the class name.
38
- # Import on demand is induced by a reference to the class, e.g.:
39
- # module ClinicalTrials
40
- # extend CaRuby::ResourceModule
41
- #
42
- # @java_package = 'org.nci.ctms'
43
- # ...
44
- # enables references by name to a +ClinicalTrials+ Ruby class wrapper of a
45
- # +org.nci.ctms+ Java class without an import statement, e.g.:
46
- # ClinicalTrials::Participant.new
47
- # without defining the +Participant+ Ruby class.
48
- module ResourceModule
49
- # Loads the {#access_properties} and adds the path property items to the Java classpath.
50
- # @param [Module] mod the module to extend
51
- def self.extended(mod)
52
- super
53
- mod.ensure_classpath_defined
54
- end
55
-
56
- # Loads the application start-up properties on demand. The properties are defined in the properties
57
- # file or as environment variables.
58
- # The properties file path is a period followed by the lower-case application name in the home directory,
59
- # e.g. +~/.clincaltrials+ for the +ClinicalTrials+ application.
60
- #
61
- # The property file format is a series of property definitions in the form _property_: _value_.
62
- # The supported properties include the following:
63
- # * +host+ - the application server host (default +localhost+)
64
- # * +port+ - the application server port (default +8080+)
65
- # * +user+ - the application server login
66
- # * +password+ - the application service password
67
- # * +path+ or +classpath+ - the application client Java directories
68
- # * +database+ - the application database name
69
- # * +database_user+ - the application database connection userid
70
- # * +database_password+ - the application database connection password
71
- # * +database_host+ - the application database connection host (default +localhost+)
72
- # * +database_type+ - the application database type, + mysql+ or +oracle+ (default +mysql+)
73
- # * +database_driver+ - the application database connection driver (default is the database type default)
74
- # * +database_port+ - the application database connection port (default is the database type default)
75
- #
76
- # The +path+ value is one or more directories separated by a semi-colon(;) or colon (:)
77
- # Each path directory and all jar files within the directory are added to the caRuby execution
78
- # Java classpath.
79
- #
80
- # Each property has an environment variable counterpart given by
81
- #
82
- # @return [{Symbol => Object}] the caBIG application access properties
83
- def access_properties
84
- @rsc_props ||= load_access_properties
85
- end
86
-
87
- # Ensures that the application client classpath is defined. The classpath is defined
88
- # in the {#access_properties}. This method is called when a module extends this
89
- # ResourceModule, before any application Java domain class is imported into JRuby.
90
- def ensure_classpath_defined
91
- # Loading the access properties on demand sets the classpath.
92
- access_properties
93
- end
94
-
95
- # @return [Module] the resource mix-in module (default {Resouce})
96
- def mixin
97
- @mixin || Resource
98
- end
99
-
100
- # Adds the given class to this ResourceModule. The class is extended with ResourceMetadata methods.
101
- #
102
- # @param [Class] the {Resource} class to add
103
- def add_class(klass)
104
- logger.debug { "Adding #{klass.java_class.name} to #{qp}..." }
105
- @rsc_classes ||= Set.new
106
- # add superclass if necessary
107
- sc = klass.superclass
108
- unless @rsc_classes.include?(sc) then
109
- # the domain module includes the superclass on demand
110
- sc_pkg, sc_sym = Java.split_class_name(sc)
111
- if const_defined?(sc_sym) or sc_pkg == @java_package then
112
- const_get(sc_sym)
113
- else
114
- mod = mixin
115
- klass.class_eval { include mod }
116
- end
117
- end
118
- ResourceMetadata.extend_class(klass, self)
119
- @rsc_classes << klass
120
- class_added(klass)
121
- logger.debug { "#{klass.java_class.name} added to #{qp}." }
122
- end
123
-
124
- # Auto-loads the Ruby source files in the given directory.
125
- #
126
- # @param [String] dir the source directory
127
- def load_dir(dir)
128
- # the domain class definitions
129
- sources = Dir.glob(File.join(dir, "*.rb"))
130
-
131
- # autoload the domain classes to ensure that definitions are picked up on demand in class hierarchy order
132
- sym_file_hash = {}
133
- sources.each do |file|
134
- base_name = File.basename(file, ".rb")
135
- sym = base_name.camelize.to_sym
136
- sym_file_hash[sym] = file
137
- autoload(sym, file)
138
- end
139
-
140
- # load the domain class definitions
141
- sym_file_hash.to_a.each do |sym, file|
142
- require file
143
- end
144
-
145
- # print the loaded classes to the log
146
- sym_file_hash.each_key do |sym|
147
- # it is not an error if the inferred class name is not loaded, since only the Java application classes
148
- # are required to be the camel-case form of the file names.
149
- klass = const_get(sym) rescue next
150
- logger.info("#{klass.pp_s}")
151
- end
152
- end
153
-
154
- # @param [Class, String] class_or_name the class to import into this module
155
- # @return [Class] the imported class
156
- def java_import(class_or_name)
157
- # JRuby 1.4.x does not support a class argument
158
- Class === class_or_name ? super(class_or_name.java_class.name) : super
159
- end
160
-
161
- # @param [Class, String] class_or_name the class to import into this module
162
- # @return [Class] the imported {Resource} class
163
- def resource_import(class_or_name)
164
- klass = java_import(class_or_name)
165
- mod = mixin
166
- klass.instance_eval { include mod }
167
- add_class(klass)
168
- klass
169
- end
170
-
171
- # Imports a class constant on demand. See the class documentation for details.
172
- #
173
- # @param [Symbol] symbol the missing constant
174
- def const_missing(symbol)
175
- autoload?(symbol) ? super : import_domain_class(symbol)
176
- end
177
-
178
- # @param [String] the class name to check
179
- # @eturn [Class, nil] the domain class for the class name, or nil if none in this module
180
- def domain_type_with_name(name)
181
- pkg, base = Java.split_class_name(name)
182
- return unless pkg.nil? or pkg == @java_package
183
- begin
184
- type = const_get(base)
185
- rescue JavaIncludeError
186
- # no such domain type; not an error.
187
- # other exceptions indicate that there was a domain type but could not be loaded; these exceptions propagate up the call stack
188
- logger.debug("#{base} is not a #{qp} Java class.")
189
- return
190
- end
191
- type if type < Resource
192
- end
193
-
194
- private
195
-
196
- # Callback invoked after the given domain class is added to this domain module.
197
- #
198
- # @param [Class] klass the class that was added
199
- def class_added(klass); end
200
-
201
- # Loads the application start-up properties in the given file path.
202
- #
203
- # @return (see #access_properties)
204
- def load_access_properties
205
- # the properties file
206
- file = default_properties_file
207
- # the access properties
208
- props = file && File.exists?(file) ? load_properties_file(file) : {}
209
- # Look for environment overrides preceded by the uppercase module name,
210
- # e.g. CATISSUE_USER for the CaTissue module.
211
- load_environment_properties(props)
212
-
213
- # load the Java application jar path
214
- path = props[:classpath] || props[:path]
215
- if path then
216
- logger.info("Defining application classpath #{path}...")
217
- Java.add_path(path)
218
- end
219
-
220
- props
221
- end
222
-
223
- def load_properties_file(file)
224
- props = {}
225
- logger.info("Loading application properties from #{file}...")
226
- File.open(file).map do |line|
227
- # match the tolerant property definition
228
- match = PROP_DEF_REGEX.match(line.chomp) || next
229
- # the property [name, value] tokens
230
- tokens = match.captures
231
- pname = tokens.first.to_sym
232
- # path is deprecated
233
- name = pname == :path ? :classpath : pname
234
- value = tokens.last
235
- # capture the property
236
- props[name] = value
237
- end
238
- props
239
- end
240
-
241
- def load_environment_properties(props)
242
- ACCESS_OPTS.each do |spec|
243
- # the access option symbol is the first specification item
244
- opt = spec[0]
245
- # the envvar value
246
- value = environment_property(opt) || next
247
- # override the file property with the envar value
248
- props[opt] = value
249
- logger.info("Set application property #{opt} from environment variable #{ev}.")
250
- end
251
- end
252
-
253
- # @param [Symbol] opt the property symbol, e.g. :user
254
- # @return [String, nil] the value of the corresponding environment variable, e.g. +CATISSUE_USER+
255
- def environment_property(opt)
256
- @env_prefix ||= name.gsub('::', '_').upcase
257
- ev = "#{@env_prefix}_#{opt.to_s.upcase}"
258
- value = ENV[ev]
259
- # If no classpath envvar, then try the deprecated path envvar.
260
- if value.nil? and opt == :classpath then
261
- environment_property(:path)
262
- else
263
- value
264
- end
265
- end
266
-
267
- # Imports the domain Java class with specified class name_or_sym.
268
- # This method enables the domain class extensions for storing and retrieving domain objects.
269
- # The class is added to this module.
270
- #
271
- # The optional block overrides the native Java property access wrappers.
272
- # For example:
273
- # ClinicalTrials.java_import Java::edu.wustl.catissuecore.domain.Study do
274
- # def study_code=(value)
275
- # value = value.to_s if Integer === value
276
- # setStudyCode(value)
277
- # end
278
- # end
279
- # imports the ClinicalTrials Study class as ClinicalTrials::Study and overrides the
280
- # +study_code+ setter method.
281
- #
282
- # Convenience aliases are added to the imported class, e.g. attribute +studyParticipantCollection+
283
- # is aliased as +study_participants+. Specifically, each attribute reader and writer is aliased with
284
- # the lower-case, underscore equivalent and a name ending in 'Collection' is changed to plural.
285
- # Pluralization is smart, e.g. +studyCollection+ is aliased to +studies+ rather than +studys+.
286
- #
287
- # The optional aliases argument consists of additional alias => standard attribute associations.
288
- # The optional owner_attr argument is a non-Java annotation owner attribute.
289
- #
290
- # @param [Symbol] symbol the class symbol
291
- # @param [String, nil] pkg the Java class package name, or nil for the default module package
292
- def import_domain_class(symbol, pkg=nil)
293
- # check if the class is already defined
294
- if const_defined?(symbol) then
295
- klass = const_get(symbol)
296
- if domain?(klass) then
297
- logger.warn("Attempt to import #{self.qp} class twice: #{symbol}.") and return klass
298
- end
299
- end
300
-
301
- # import the Java class
302
- pkg ||= @java_package
303
- name = [pkg, symbol.to_s].join('.')
304
- logger.debug { "Detecting whether #{symbol} is a #{pkg} Java class..." }
305
- # Push each imported class onto a stack. When all referenced classes are imported,
306
- # each class on the stack is post-initialized and the class structure is printed to
307
- # the log.
308
- @import_stack ||= []
309
- @import_stack.push(symbol)
310
- begin
311
- resource_import(name)
312
- rescue Exception
313
- @import_stack.pop
314
- if symbol.to_s =~ /Annotation/ then raise end
315
- raise JavaIncludeError.new("#{symbol} is not a #{qp} Java class - #{$!}")
316
- end
317
-
318
- # the imported Java class is registered as a constant in this module
319
- klass = const_get(symbol)
320
- # if we are back to top of the stack, then print the imported Resources
321
- if symbol == @import_stack.first then
322
- # a referenced class could be imported on demand in the course of printing a referencing class;
323
- # the referenced class is then pushed onto the stack. thus, the stack can grow during the
324
- # course of printing, but each imported class is consumed and printed in the end.
325
- until @import_stack.empty? do
326
- # the class constant
327
- sym = @import_stack.pop
328
- # the imported class
329
- kls = const_get(sym)
330
- # invoke the call-back
331
- imported(kls)
332
- # print the class structure to the log
333
- logger.info(kls.pp_s)
334
- end
335
- end
336
-
337
- klass
338
- end
339
-
340
- # Call-back to perform post-import actions. This method is called after the
341
- # given class and each of its referenced domain classes are introspected.
342
- #
343
- # @param [Class] the imported class
344
- def imported(klass); end
345
-
346
- # The property/value matcher, e.g.:
347
- # host: jacardi
348
- # host = jacardi
349
- # host=jacardi
350
- # name: J. Edgar Hoover
351
- # but not:
352
- # # host: jacardi
353
- # The captures are the trimmed property and value
354
- PROP_DEF_REGEX = /^(\w+)(?:\s*[:=]\s*)([^#]+)/
355
-
356
- # @return [String] the default application properties file, given by +~/.+_name_,
357
- # where _name_ is the underscore unqualified module name, e.g. +~/.catissue+
358
- # for module +CaTissue+
359
- def default_properties_file
360
- home = ENV['HOME'] || '~'
361
- file = File.expand_path("#{home}/.#{name[/\w+$/].downcase}")
362
- if File.exists?(file) then
363
- file
364
- else
365
- logger.debug { "Default #{name} application property file not found: #{file}." }
366
- nil
367
- end
368
- end
369
- end
370
- end
@@ -1,8 +0,0 @@
1
- class ResourceMetadataHandler < YARD::Handlers::Ruby::Legacy::AttributeHandler
2
- handles method_call(/\Aqualify_attribute\b/)
3
- namespace_only
4
-
5
- def process
6
- push_state(:scope => :class) { super }
7
- end
8
- end