caruby-core 1.4.9 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
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