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.
- 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)
|