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.
- data/History.md +48 -0
- data/lib/caruby/cli/command.rb +2 -1
- data/lib/caruby/csv/csv_mapper.rb +8 -8
- data/lib/caruby/database/persistable.rb +44 -65
- data/lib/caruby/database/persistence_service.rb +12 -9
- data/lib/caruby/database/persistifier.rb +14 -14
- data/lib/caruby/database/reader.rb +53 -51
- data/lib/caruby/database/search_template_builder.rb +9 -10
- data/lib/caruby/database/store_template_builder.rb +58 -58
- data/lib/caruby/database/writer.rb +96 -96
- data/lib/caruby/database.rb +19 -19
- data/lib/caruby/domain/attribute.rb +581 -0
- data/lib/caruby/domain/attributes.rb +615 -0
- data/lib/caruby/domain/dependency.rb +240 -0
- data/lib/caruby/domain/importer.rb +183 -0
- data/lib/caruby/domain/introspection.rb +176 -0
- data/lib/caruby/domain/inverse.rb +173 -0
- data/lib/caruby/domain/inversible.rb +1 -2
- data/lib/caruby/domain/java_attribute.rb +173 -0
- data/lib/caruby/domain/merge.rb +13 -10
- data/lib/caruby/domain/metadata.rb +141 -0
- data/lib/caruby/domain/mixin.rb +35 -0
- data/lib/caruby/domain/reference_visitor.rb +5 -3
- data/lib/caruby/domain.rb +340 -0
- data/lib/caruby/import/java.rb +29 -25
- data/lib/caruby/migration/migratable.rb +5 -5
- data/lib/caruby/migration/migrator.rb +19 -15
- data/lib/caruby/migration/resource_module.rb +1 -1
- data/lib/caruby/resource.rb +39 -30
- data/lib/caruby/util/collection.rb +94 -33
- data/lib/caruby/util/coordinate.rb +28 -2
- data/lib/caruby/util/log.rb +4 -4
- data/lib/caruby/util/module.rb +12 -28
- data/lib/caruby/util/partial_order.rb +9 -10
- data/lib/caruby/util/pretty_print.rb +46 -26
- data/lib/caruby/util/topological_sync_enumerator.rb +10 -4
- data/lib/caruby/util/transitive_closure.rb +2 -2
- data/lib/caruby/util/visitor.rb +1 -1
- data/lib/caruby/version.rb +1 -1
- data/test/lib/caruby/database/persistable_test.rb +1 -1
- data/test/lib/caruby/domain/domain_test.rb +14 -28
- data/test/lib/caruby/domain/inversible_test.rb +1 -1
- data/test/lib/caruby/import/java_test.rb +5 -0
- data/test/lib/caruby/migration/test_case.rb +0 -1
- data/test/lib/caruby/test_case.rb +9 -10
- data/test/lib/caruby/util/collection_test.rb +23 -5
- data/test/lib/caruby/util/module_test.rb +10 -14
- data/test/lib/caruby/util/partial_order_test.rb +16 -15
- data/test/lib/caruby/util/visitor_test.rb +1 -1
- data/test/lib/examples/galena/clinical_trials/migration/test_case.rb +1 -1
- metadata +16 -15
- data/History.txt +0 -44
- data/lib/caruby/domain/attribute_metadata.rb +0 -551
- data/lib/caruby/domain/java_attribute_metadata.rb +0 -183
- data/lib/caruby/domain/resource_attributes.rb +0 -565
- data/lib/caruby/domain/resource_dependency.rb +0 -217
- data/lib/caruby/domain/resource_introspection.rb +0 -160
- data/lib/caruby/domain/resource_inverse.rb +0 -151
- data/lib/caruby/domain/resource_metadata.rb +0 -155
- data/lib/caruby/domain/resource_module.rb +0 -370
- data/lib/caruby/yard/resource_metadata_handler.rb +0 -8
@@ -0,0 +1,340 @@
|
|
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
|
+
# # @param [Class, String] class_or_name the class to import into this module
|
102
|
+
# # @return [Class] the imported {Resource} class
|
103
|
+
# def resource_import(class_or_name)
|
104
|
+
# java_import(class_or_name)
|
105
|
+
# klass = Class.to_ruby(class_or_name)
|
106
|
+
# add_class(klass)
|
107
|
+
# klass
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# # @param [String] the class name to check
|
111
|
+
# # @eturn [Class, nil] the domain class for the class name, or nil if none in this module
|
112
|
+
# def domain_type_with_name(name)
|
113
|
+
# pkg, base = Java.split_class_name(name)
|
114
|
+
# return unless pkg.nil? or pkg == @java_package
|
115
|
+
# begin
|
116
|
+
# type = const_get(base)
|
117
|
+
# rescue JavaImportError => e
|
118
|
+
# # no such domain type; not an error.
|
119
|
+
# # other exceptions indicate that there was a domain type but could not be loaded; these exceptions propagate up the call stack
|
120
|
+
# logger.debug { "#{$!}" }
|
121
|
+
# return
|
122
|
+
# end
|
123
|
+
# type if type < Resource
|
124
|
+
# end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
# # Adds the given class to this domain module. The class is extended with {Metadata} methods.
|
129
|
+
# #
|
130
|
+
# # @param [Class] the {Resource} class to add
|
131
|
+
# def add_class(klass)
|
132
|
+
# # This domain module's known Resource classes.
|
133
|
+
# @rsc_classes ||= Set.new
|
134
|
+
# # Bail if already added.
|
135
|
+
# return if @rsc_classes.include?(klass)
|
136
|
+
#
|
137
|
+
# logger.debug { "Adding #{klass.java_class.name} to #{qp}..." }
|
138
|
+
# # Add metadata to the class. This is done before adding the superclass and
|
139
|
+
# # referenced metadata, since they in turn might reference the current class
|
140
|
+
# # metadata.
|
141
|
+
# Metadata.extend_class(klass, self)
|
142
|
+
# # Add the class to the known Resource class set.
|
143
|
+
# @rsc_classes << klass
|
144
|
+
#
|
145
|
+
# # Make the superclass a Resource, if necessary.
|
146
|
+
# sc = klass.superclass
|
147
|
+
# unless @rsc_classes.include?(sc) then
|
148
|
+
# # the domain module includes the superclass on demand
|
149
|
+
# pkg, sym = Java.split_class_name(sc)
|
150
|
+
# if pkg == @java_package then
|
151
|
+
# # Load the superclass on demand; don't need to make this class a Resource,
|
152
|
+
# # but do need to import the class.
|
153
|
+
# const_get(sym)
|
154
|
+
# else
|
155
|
+
# # Superclass is not a member of the domain package; make this class a Resource.
|
156
|
+
# mod = @mixin
|
157
|
+
# klass.class_eval { include mod }
|
158
|
+
# end
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# # Add referenced domain classes as necessary.
|
162
|
+
# klass.each_attribute_metadata do |attr_md|
|
163
|
+
# ref = attr_md.type
|
164
|
+
# next if @rsc_classes.include?(ref)
|
165
|
+
# pkg, sym = Java.split_class_name(ref)
|
166
|
+
# # This domain module adds a referenced Domain class on demand.
|
167
|
+
# if pkg == @java_package then
|
168
|
+
# puts "rm ac1 #{klass} #{attr_md} -> #{ref}..."
|
169
|
+
# logger.debug { "Loading #{klass.qp} #{attr_md} reference #{ref.qp}" }
|
170
|
+
# const_get(sym)
|
171
|
+
# end
|
172
|
+
# end
|
173
|
+
#
|
174
|
+
# # Invoke the callback.
|
175
|
+
# class_added(klass)
|
176
|
+
# logger.debug { "#{klass.java_class.name} added to #{qp}." }
|
177
|
+
# # print the class structure to the log
|
178
|
+
# logger.info(klass.pp_s)
|
179
|
+
# end
|
180
|
+
#
|
181
|
+
# # Callback invoked after the given domain class is added to this domain module.
|
182
|
+
# #
|
183
|
+
# # @param [Class] klass the class that was added
|
184
|
+
# def class_added(klass); end
|
185
|
+
|
186
|
+
# Loads the application start-up properties in the given file path.
|
187
|
+
#
|
188
|
+
# @return (see #access_properties)
|
189
|
+
def load_access_properties
|
190
|
+
# the properties file
|
191
|
+
file = default_properties_file
|
192
|
+
# the access properties
|
193
|
+
props = file && File.exists?(file) ? load_properties_file(file) : {}
|
194
|
+
# Look for environment overrides preceded by the uppercase module name,
|
195
|
+
# e.g. CATISSUE_USER for the CaTissue module.
|
196
|
+
load_environment_properties(props)
|
197
|
+
|
198
|
+
# load the Java application jar path
|
199
|
+
path = props[:classpath] || props[:path]
|
200
|
+
if path then
|
201
|
+
logger.info("Defining application classpath #{path}...")
|
202
|
+
Java.add_path(path)
|
203
|
+
end
|
204
|
+
|
205
|
+
props
|
206
|
+
end
|
207
|
+
|
208
|
+
def load_properties_file(file)
|
209
|
+
props = {}
|
210
|
+
logger.info("Loading application properties from #{file}...")
|
211
|
+
File.open(file).map do |line|
|
212
|
+
# match the tolerant property definition
|
213
|
+
match = PROP_DEF_REGEX.match(line.chomp.strip) || next
|
214
|
+
# the property [name, value] tokens
|
215
|
+
tokens = match.captures
|
216
|
+
pname = tokens.first.to_sym
|
217
|
+
# path is deprecated
|
218
|
+
name = pname == :path ? :classpath : pname
|
219
|
+
value = tokens.last
|
220
|
+
# capture the property
|
221
|
+
props[name] = value
|
222
|
+
end
|
223
|
+
props
|
224
|
+
end
|
225
|
+
|
226
|
+
def load_environment_properties(props)
|
227
|
+
ACCESS_OPTS.each do |spec|
|
228
|
+
# the access option symbol is the first specification item
|
229
|
+
opt = spec[0]
|
230
|
+
# the envvar value
|
231
|
+
value = environment_property(opt) || next
|
232
|
+
# override the file property with the envar value
|
233
|
+
props[opt] = value
|
234
|
+
logger.info("Set application property #{opt} from environment variable #{ev}.")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# @param [Symbol] opt the property symbol, e.g. :user
|
239
|
+
# @return [String, nil] the value of the corresponding environment variable, e.g. +CATISSUE_USER+
|
240
|
+
def environment_property(opt)
|
241
|
+
@env_prefix ||= name.gsub('::', '_').upcase
|
242
|
+
ev = "#{@env_prefix}_#{opt.to_s.upcase}"
|
243
|
+
value = ENV[ev]
|
244
|
+
# If no classpath envvar, then try the deprecated path envvar.
|
245
|
+
if value.nil? and opt == :classpath then
|
246
|
+
environment_property(:path)
|
247
|
+
else
|
248
|
+
value
|
249
|
+
end
|
250
|
+
end
|
251
|
+
#
|
252
|
+
# # Imports the domain Java class with specified class name_or_sym.
|
253
|
+
# # This method enables the domain class extensions for storing and retrieving domain objects.
|
254
|
+
# # The class is added to this module.
|
255
|
+
# #
|
256
|
+
# # The optional block overrides the native Java property access wrappers.
|
257
|
+
# # For example:
|
258
|
+
# # ClinicalTrials.java_import Java::edu.wustl.catissuecore.domain.Study do
|
259
|
+
# # def study_code=(value)
|
260
|
+
# # value = value.to_s if Integer === value
|
261
|
+
# # setStudyCode(value)
|
262
|
+
# # end
|
263
|
+
# # end
|
264
|
+
# # imports the ClinicalTrials Study class as ClinicalTrials::Study and overrides the
|
265
|
+
# # +study_code+ setter method.
|
266
|
+
# #
|
267
|
+
# # Convenience aliases are added to the imported class, e.g. attribute +studyParticipantCollection+
|
268
|
+
# # is aliased as +study_participants+. Specifically, each attribute reader and writer is aliased with
|
269
|
+
# # the lower-case, underscore equivalent and a name ending in 'Collection' is changed to plural.
|
270
|
+
# # Pluralization is smart, e.g. +studyCollection+ is aliased to +studies+ rather than +studys+.
|
271
|
+
# #
|
272
|
+
# # The optional aliases argument consists of additional alias => standard attribute associations.
|
273
|
+
# # The optional owner_attr argument is a non-Java annotation owner attribute.
|
274
|
+
# #
|
275
|
+
# # @param [Symbol] symbol the class symbol
|
276
|
+
# # @param [String, nil] pkg the Java class package name, or nil for the default module package
|
277
|
+
# # @return [Class] the imported domain class
|
278
|
+
# def import_domain_class(symbol, pkg=nil)
|
279
|
+
# # import the Java class
|
280
|
+
# pkg ||= @java_package
|
281
|
+
# name = [pkg, symbol.to_s].join('.')
|
282
|
+
# logger.debug { "Detecting whether #{symbol} is a #{pkg} Java class..." }
|
283
|
+
# # Push each imported class onto a stack. When all referenced classes are imported,
|
284
|
+
# # each class on the stack is post-initialized and the class structure is printed to
|
285
|
+
# # the log.
|
286
|
+
# @import_stack ||= []
|
287
|
+
# @import_stack.push(symbol)
|
288
|
+
# begin
|
289
|
+
# resource_import(name)
|
290
|
+
# ensure
|
291
|
+
# @import_stack.pop
|
292
|
+
# end
|
293
|
+
#
|
294
|
+
# # the imported Java class is registered as a constant in this module
|
295
|
+
# klass = const_get(symbol)
|
296
|
+
# add_class(klass)
|
297
|
+
#
|
298
|
+
# # if we are back to top of the stack, then print the imported Resources
|
299
|
+
# if symbol == @import_stack.first then
|
300
|
+
# # a referenced class could be imported on demand in the course of printing a referencing class;
|
301
|
+
# # the referenced class is then pushed onto the stack. thus, the stack can grow during the
|
302
|
+
# # course of printing, but each imported class is consumed and printed in the end.
|
303
|
+
# until @import_stack.empty? do
|
304
|
+
# # the class constant
|
305
|
+
# sym = @import_stack.pop
|
306
|
+
# # the imported class
|
307
|
+
# kls = const_get(sym)
|
308
|
+
# # print the class structure to the log
|
309
|
+
# logger.info(kls.pp_s)
|
310
|
+
# end
|
311
|
+
# end
|
312
|
+
#
|
313
|
+
# klass
|
314
|
+
# end
|
315
|
+
|
316
|
+
# The property/value matcher, e.g.:
|
317
|
+
# host: jacardi
|
318
|
+
# host = jacardi
|
319
|
+
# host=jacardi
|
320
|
+
# name: J. Edgar Hoover
|
321
|
+
# but not:
|
322
|
+
# # host: jacardi
|
323
|
+
# The captures are the trimmed property and value.
|
324
|
+
PROP_DEF_REGEX = /^(\w+)(?:\s*[:=]\s*)([^#]+)/
|
325
|
+
|
326
|
+
# @return [String] the default application properties file, given by +~/.+_name_,
|
327
|
+
# where _name_ is the underscore unqualified module name, e.g. +~/.catissue+
|
328
|
+
# for module +CaTissue+
|
329
|
+
def default_properties_file
|
330
|
+
home = ENV['HOME'] || ENV['USERPROFILE'] || '~'
|
331
|
+
file = File.expand_path("#{home}/.#{name[/\w+$/].downcase}")
|
332
|
+
if File.exists?(file) then
|
333
|
+
file
|
334
|
+
else
|
335
|
+
logger.warn("The default #{name} application property file was not found: #{file}.")
|
336
|
+
nil
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
data/lib/caruby/import/java.rb
CHANGED
@@ -168,27 +168,21 @@ module Java
|
|
168
168
|
end
|
169
169
|
|
170
170
|
class Date
|
171
|
-
# millisecond-to-day conversion factor
|
172
|
-
|
173
|
-
MILLIS_PER_DAY = MILLIS_PER_HR * 24
|
171
|
+
# The millisecond-to-day conversion factor.
|
172
|
+
MILLIS_PER_DAY = (60 * 60 * 1000) * 24
|
174
173
|
|
175
174
|
# Converts this Java Date to a Ruby DateTime.
|
176
175
|
#
|
177
|
-
# caTissue
|
178
|
-
#
|
179
|
-
#
|
180
|
-
#
|
181
|
-
# that
|
182
|
-
#
|
183
|
-
# date.to_ruby_date().to_java_date == date
|
176
|
+
# @quirk caTissue Bug #165: API CPR create date validation is time zone dependent.
|
177
|
+
# Since Java Date accounts for DST and Ruby DateTime doesn't, this method makes the
|
178
|
+
# DST adjustment by subtracting a compensatory one-hour DST offset from the
|
179
|
+
# Java Date time zone offset and using that to set the DateTime offset.
|
180
|
+
# This ensures that Date conversion is idempotent, i.e.
|
181
|
+
# date.to_ruby_date().to_java_date == date
|
184
182
|
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
# TODO: Revisit {CaRuby::Resource.value_equal?} which must resort to a
|
190
|
-
# date-as-string comparison, always a bad idea. If that can be fixed, then
|
191
|
-
# increment/decrement the hour field rather than the offset field.
|
183
|
+
# However, there can be adverse consequences for an application that assumes that the
|
184
|
+
# client time zone is the same as the server time zone, as described in caTissue
|
185
|
+
# Bug #165.
|
192
186
|
#
|
193
187
|
# @return [DateTime] the Ruby date
|
194
188
|
def to_ruby_date
|
@@ -199,9 +193,13 @@ module Java
|
|
199
193
|
time = Time.at(secs)
|
200
194
|
# convert UTC timezone millisecond offset to Rational fraction of a day
|
201
195
|
offset_millis = calendar.timeZone.getOffset(calendar.timeInMillis).to_f
|
202
|
-
|
203
|
-
|
204
|
-
|
196
|
+
if offset_millis.zero? then
|
197
|
+
offset = 0
|
198
|
+
else
|
199
|
+
offset_days = offset_millis / MILLIS_PER_DAY
|
200
|
+
offset_fraction = 1 / offset_days
|
201
|
+
offset = Rational(1, offset_fraction)
|
202
|
+
end
|
205
203
|
# convert to DateTime
|
206
204
|
DateTime.civil(time.year, time.mon, time.day, time.hour, time.min, time.sec, offset)
|
207
205
|
end
|
@@ -261,9 +259,13 @@ class Class
|
|
261
259
|
method_defined?(:java_class)
|
262
260
|
end
|
263
261
|
|
264
|
-
# Returns
|
265
|
-
# If
|
266
|
-
#
|
262
|
+
# Returns the Ruby class for the given class, as follows:
|
263
|
+
# * If the given class is already a Ruby class, then return the class.
|
264
|
+
# * If the class argument is a Java class or a Java class name, then
|
265
|
+
# the Ruby class is the JRuby wrapper for the Java class.
|
266
|
+
#
|
267
|
+
# @param [Class, String] the class or class name
|
268
|
+
# @return [Class] the corresponding Ruby class
|
267
269
|
def self.to_ruby(klass)
|
268
270
|
case klass
|
269
271
|
when Class then klass
|
@@ -347,10 +349,12 @@ class Class
|
|
347
349
|
newsym
|
348
350
|
end
|
349
351
|
|
350
|
-
#
|
352
|
+
# @quirk caCORE java.lang.Boolean is<name> is not introspected as a read method, since the type
|
353
|
+
# must be primitive, i.e. `boolean is`<name>.
|
354
|
+
#
|
355
|
+
# @return [Symbol] the property descriptor pd introspected or discovered Java read Method
|
351
356
|
def property_read_method(pd)
|
352
357
|
return pd.read_method if pd.read_method
|
353
|
-
# caCORE alert - java.lang.Boolean is<name> is not introspected as a read method, since type must be primitive boolean is<name>
|
354
358
|
return unless pd.get_property_type == Java::JavaLang::Boolean.java_class
|
355
359
|
rdr = java_class.java_method("is#{pd.name.capitalize_first}") rescue nil
|
356
360
|
logger.debug { "Discovered #{qp} #{pd.name} property non-introspected reader method #{rdr.name}." } if rdr
|
@@ -64,7 +64,7 @@ module CaRuby
|
|
64
64
|
# collection attributes mentioned above, or to fill in missing values.
|
65
65
|
#
|
66
66
|
# Note that there is an extensive set of attribute defaults defined in
|
67
|
-
# the CaRuby::
|
67
|
+
# the CaRuby::Metadata application domain classes. These defaults
|
68
68
|
# are applied in a migration database save action and need not be set in
|
69
69
|
# a migration shim. For example, if an acceptable default for a +Study+
|
70
70
|
# +active?+ flag is defined in the +Study+ meta-data, then the flag does not
|
@@ -102,8 +102,8 @@ module CaRuby
|
|
102
102
|
# for the _attribute_ to modify.
|
103
103
|
#
|
104
104
|
# The migratable reference attributes consist of the non-collection
|
105
|
-
# {
|
106
|
-
# {
|
105
|
+
# {Attributes#saved_independent_attributes} and
|
106
|
+
# {Attributes#unidirectional_dependent_attributes} which don't already have a value.
|
107
107
|
# For each such migratable attribute, if there is a single instance of the attribute
|
108
108
|
# type in the given migrated domain objects, then the attribute is set to that
|
109
109
|
# migrated instance.
|
@@ -146,7 +146,7 @@ module CaRuby
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
149
|
-
# @param [
|
149
|
+
# @param [Attribute::Filter] the attributes to set
|
150
150
|
# @param row (see #migrate_references)
|
151
151
|
# @param migrated (see #migrate_references)
|
152
152
|
# @param mth_hash (see #migrate_references)
|
@@ -168,7 +168,7 @@ module CaRuby
|
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
171
|
-
# @param [
|
171
|
+
# @param [Attribute] attr_md the reference attribute
|
172
172
|
# @param row (see #migrate_references)
|
173
173
|
# @param migrated (see #migrate_references)
|
174
174
|
# @param mth_hash (see #migrate_references)
|
@@ -38,6 +38,7 @@ module CaRuby
|
|
38
38
|
# @option opts [String] :bad optional invalid record file
|
39
39
|
# @option opts [Integer] :offset zero-based starting source record number to process (default 0)
|
40
40
|
# @option opts [Boolean] :quiet suppress output messages
|
41
|
+
# @option opts [Boolean] :verbose print progress
|
41
42
|
def initialize(opts)
|
42
43
|
@rec_cnt = 0
|
43
44
|
parse_options(opts)
|
@@ -120,7 +121,7 @@ module CaRuby
|
|
120
121
|
@create = opts[:create]
|
121
122
|
logger.info("Migration options: #{printable_options(opts).pp_s}.")
|
122
123
|
# flag indicating whether to print a progress monitor
|
123
|
-
@print_progress =
|
124
|
+
@print_progress = opts[:verbose]
|
124
125
|
end
|
125
126
|
|
126
127
|
def printable_options(opts)
|
@@ -209,7 +210,7 @@ module CaRuby
|
|
209
210
|
def add_owners(klass)
|
210
211
|
klass.owners.each do |owner|
|
211
212
|
next if @cls_paths_hash.detect_key { |other| other <= owner } or owner.abstract?
|
212
|
-
logger.debug { "Migrator adding #{klass.qp} owner #{owner
|
213
|
+
logger.debug { "Migrator adding #{klass.qp} owner #{owner}" }
|
213
214
|
@cls_paths_hash[owner] = Array::EMPTY_ARRAY
|
214
215
|
add_owners(owner)
|
215
216
|
end
|
@@ -297,7 +298,7 @@ module CaRuby
|
|
297
298
|
attr_mds.each do |attr_md|
|
298
299
|
# the attribute migration method
|
299
300
|
mth = attr_mth_hash[attr_md.to_sym]
|
300
|
-
# associate the
|
301
|
+
# associate the Attribute => method
|
301
302
|
@attr_md_mgt_mth_map[attr_md] ||= mth if mth
|
302
303
|
end
|
303
304
|
@mgt_mth_hash[klass] = attr_mth_hash
|
@@ -319,7 +320,9 @@ module CaRuby
|
|
319
320
|
end
|
320
321
|
|
321
322
|
# Migrates all rows in the input.
|
322
|
-
#
|
323
|
+
#
|
324
|
+
# @yield (see #migrate)
|
325
|
+
# @yieldparam (see #migrate)
|
323
326
|
def migrate_rows # :yields: target
|
324
327
|
# open an CSV output for bad records if the option is set
|
325
328
|
if @bad_rec_file then
|
@@ -357,8 +360,7 @@ module CaRuby
|
|
357
360
|
# clear the migration state
|
358
361
|
clear(target)
|
359
362
|
else
|
360
|
-
# If there is a bad file then warn, reject and continue.
|
361
|
-
# Otherwise, bail.
|
363
|
+
# If there is a bad file then warn, reject and continue. Otherwise, bail.
|
362
364
|
if @bad_rec_file then
|
363
365
|
logger.warn("Migration not performed on record #{rec_no}.")
|
364
366
|
@loader.reject(row)
|
@@ -366,16 +368,18 @@ module CaRuby
|
|
366
368
|
raise MigrationError.new("Migration not performed on record #{rec_no}")
|
367
369
|
end
|
368
370
|
end
|
371
|
+
# Bump the record count.
|
369
372
|
@rec_cnt += 1
|
370
373
|
end
|
371
374
|
logger.info("Migrated #{mgt_cnt} of #{@rec_cnt} records.")
|
372
375
|
end
|
373
376
|
|
374
|
-
# Prints a +\++ progress indicator to stdout.
|
377
|
+
# Prints a +\++ progress indicator to stdout if the count parameter is divisible by ten.
|
375
378
|
#
|
376
379
|
# @param [Integer] count the progress step count
|
377
380
|
def print_progress(count)
|
378
|
-
if count %
|
381
|
+
if count % 720 then puts end
|
382
|
+
if count % 10 == 0 then puts "+" else print "+" end
|
379
383
|
end
|
380
384
|
|
381
385
|
# Clears references to objects allocated for migration of a single row into the given target.
|
@@ -481,7 +485,7 @@ module CaRuby
|
|
481
485
|
end
|
482
486
|
end
|
483
487
|
|
484
|
-
# Fills the given reference
|
488
|
+
# Fills the given reference Attribute path starting at obj.
|
485
489
|
#
|
486
490
|
# @param row (see #create)
|
487
491
|
# @param created (see #create)
|
@@ -497,7 +501,7 @@ module CaRuby
|
|
497
501
|
# Sets the given migrated object's reference attribute to a new referenced domain object.
|
498
502
|
#
|
499
503
|
# @param [Resource] obj the domain object being migrated
|
500
|
-
# @param [
|
504
|
+
# @param [Attribute] attr_md the attribute being migrated
|
501
505
|
# @param row (see #create)
|
502
506
|
# @param created (see #create)
|
503
507
|
# @return the new object
|
@@ -513,7 +517,7 @@ module CaRuby
|
|
513
517
|
ref
|
514
518
|
end
|
515
519
|
|
516
|
-
# Sets the obj migratable
|
520
|
+
# Sets the obj migratable Attribute attr_md to value from the given input row.
|
517
521
|
def migrate_attribute(obj, attr_md, value, row)
|
518
522
|
# a single value can be used for both a Numeric and a String attribute; coerce the value if necessary
|
519
523
|
# if there is a shim migrate_<attribute> method, then call it on the input value
|
@@ -554,7 +558,7 @@ module CaRuby
|
|
554
558
|
end
|
555
559
|
|
556
560
|
# @param [String] file the migration fields configuration file
|
557
|
-
# @return [{Class => {
|
561
|
+
# @return [{Class => {Attribute => Symbol}}] the class => path => header hash
|
558
562
|
# loaded from the configuration file
|
559
563
|
def load_field_map(file)
|
560
564
|
# load the field mapping config file
|
@@ -621,7 +625,7 @@ module CaRuby
|
|
621
625
|
end
|
622
626
|
|
623
627
|
# @param [String] path_s a period-delimited path string path_s in the form _class_(._attribute_)+
|
624
|
-
# @return [<
|
628
|
+
# @return [<Attribute>] the corresponding attribute metadata path
|
625
629
|
# @raise [MigrationError] if the path string is malformed or an attribute is not found
|
626
630
|
def create_attribute_path(path_s)
|
627
631
|
names = path_s.split('.')
|
@@ -632,7 +636,7 @@ module CaRuby
|
|
632
636
|
if names.empty? then
|
633
637
|
raise MigrationError.new("Attribute entry in migration configuration is not in <class>.<attribute> format: #{value}")
|
634
638
|
end
|
635
|
-
# build the
|
639
|
+
# build the Attribute path
|
636
640
|
path = []
|
637
641
|
names.inject(klass) do |parent, name|
|
638
642
|
attr = name.to_sym
|
@@ -647,7 +651,7 @@ module CaRuby
|
|
647
651
|
path << attr_md
|
648
652
|
attr_md.type
|
649
653
|
end
|
650
|
-
# return the starting class and
|
654
|
+
# return the starting class and Attribute path.
|
651
655
|
# note that the starting class is not necessarily the first path attribute declarer, since the
|
652
656
|
# starting class could be the concrete target class rather than an abstract declarer. this is
|
653
657
|
# important, since the class must be instantiated.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module CaRuby
|
2
|
-
module
|
2
|
+
module Domain
|
3
3
|
# Declares the given classes which will be dynamically modified for migration.
|
4
4
|
# The Java caBIG classes are auto-loaded and wrapped as a CaRuby::Resource, if necessary, and enhanced in the migration shim.
|
5
5
|
def shims(*classes)
|