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
@@ -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
|