caruby-core 1.4.6 → 1.4.7
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -6
- data/README.md +0 -5
- data/lib/caruby/database/fetched_matcher.rb +8 -3
- data/lib/caruby/database/lazy_loader.rb +1 -1
- data/lib/caruby/database/persistence_service.rb +15 -3
- data/lib/caruby/database/saved_matcher.rb +0 -4
- data/lib/caruby/database/writer.rb +2 -0
- data/lib/caruby/database.rb +4 -2
- data/lib/caruby/domain/resource_attributes.rb +14 -8
- data/lib/caruby/domain/resource_module.rb +22 -12
- data/lib/caruby/import/java.rb +18 -4
- data/lib/caruby/migration/migratable.rb +31 -37
- data/lib/caruby/migration/migrator.rb +11 -2
- data/lib/caruby/resource.rb +26 -13
- data/lib/caruby/util/collection.rb +7 -0
- data/lib/caruby/util/coordinate.rb +3 -2
- data/lib/caruby/util/roman.rb +28 -0
- data/lib/caruby/version.rb +1 -1
- data/test/lib/caruby/util/roman_test.rb +18 -0
- metadata +18 -5
data/History.txt
CHANGED
@@ -1,29 +1,33 @@
|
|
1
1
|
=== 1.4.1 / 2010-11-23
|
2
2
|
|
3
|
-
* Initial public release
|
3
|
+
* Initial public release.
|
4
4
|
|
5
5
|
=== 1.4.2 / 2010-11-30
|
6
6
|
|
7
|
-
* Minor Migrator fixes
|
7
|
+
* Minor Migrator fixes.
|
8
8
|
|
9
9
|
=== 1.4.3 / 2011-02-18
|
10
10
|
|
11
11
|
* Refactor Persistifier
|
12
12
|
|
13
|
-
* Add attribute filters
|
13
|
+
* Add attribute filters.
|
14
14
|
|
15
15
|
=== 1.4.4 / 2011-02-25
|
16
16
|
|
17
|
-
* Support default migration option
|
17
|
+
* Support default migration option.
|
18
18
|
|
19
19
|
* Merge nondomain collection value properly.
|
20
20
|
|
21
21
|
=== 1.4.5 / 2011-02-26
|
22
22
|
|
23
|
-
* Fix default option
|
23
|
+
* Fix default option.
|
24
24
|
|
25
25
|
=== 1.4.6 / 2011-02-26
|
26
26
|
|
27
|
-
* Upgrade to JRuby 1.5
|
27
|
+
* Upgrade to JRuby 1.5.
|
28
|
+
|
29
|
+
=== 1.4.7 / 2011-03-04
|
30
|
+
|
31
|
+
* Support annotation migration.
|
28
32
|
|
29
33
|
|
data/README.md
CHANGED
@@ -4,12 +4,14 @@ require 'caruby/util/collection'
|
|
4
4
|
module CaRuby
|
5
5
|
class Database
|
6
6
|
# Proc that matches fetched sources to targets.
|
7
|
-
class FetchedMatcher
|
7
|
+
class FetchedMatcher
|
8
8
|
# Initializes a new FetchedMatcher.
|
9
|
-
def
|
10
|
-
|
9
|
+
def match(srcs, tgts)
|
10
|
+
match_fetched(srcs, tgts)
|
11
11
|
end
|
12
12
|
|
13
|
+
alias :call :match
|
14
|
+
|
13
15
|
private
|
14
16
|
|
15
17
|
# Returns a target => source match hash for the given targets and sources using
|
@@ -27,6 +29,7 @@ module CaRuby
|
|
27
29
|
attr_md = klass.attribute_metadata(attr)
|
28
30
|
attr_md.domain? and not attr_md.owner?
|
29
31
|
end
|
32
|
+
|
30
33
|
# fetch the non-owner secondary key domain attributes as necessary
|
31
34
|
unless attrs.empty? then
|
32
35
|
sources.each do |src|
|
@@ -39,6 +42,7 @@ module CaRuby
|
|
39
42
|
end
|
40
43
|
end
|
41
44
|
end
|
45
|
+
|
42
46
|
# match source => target based on the secondary key
|
43
47
|
unmatched = Set === sources ? sources.dup : sources.to_set
|
44
48
|
matches = {}
|
@@ -48,6 +52,7 @@ module CaRuby
|
|
48
52
|
matches[src] = tgt
|
49
53
|
unmatched.delete(src)
|
50
54
|
end
|
55
|
+
|
51
56
|
matches
|
52
57
|
end
|
53
58
|
end
|
@@ -93,7 +93,7 @@ module CaRuby
|
|
93
93
|
return oldval if fetched.nil_or_empty?
|
94
94
|
# merge the fetched into the attribute
|
95
95
|
logger.debug { "Merging #{subject.qp} fetched #{attribute} value #{fetched.qp}#{' into ' + oldval.qp if oldval}..." }
|
96
|
-
matches = @matcher.
|
96
|
+
matches = @matcher.match(fetched.to_enum, oldval.to_enum)
|
97
97
|
subject.merge_attribute(attribute, fetched, matches)
|
98
98
|
end
|
99
99
|
end
|
@@ -23,8 +23,10 @@ module CaRuby
|
|
23
23
|
@name = name
|
24
24
|
ver_opt = opts[:version]
|
25
25
|
@version = ver_opt.to_s.to_version if ver_opt
|
26
|
-
@host = opts[:host] ||
|
26
|
+
@host = opts[:host] || default_host
|
27
|
+
@port = opts[:port] || 8080
|
27
28
|
@timer = Stopwatch.new
|
29
|
+
logger.debug { "Created persistence service #{name} at #{@host}:#{@port}." }
|
28
30
|
end
|
29
31
|
|
30
32
|
## Database access methods ##
|
@@ -81,7 +83,7 @@ module CaRuby
|
|
81
83
|
|
82
84
|
# @return [ApplicationServiceProvider] the CaCORE service provider wrapped by this PersistenceService
|
83
85
|
def app_service
|
84
|
-
url = "http://#{@host}
|
86
|
+
url = "http://#{@host}:#{@port}/#{@name}/http/remoteService"
|
85
87
|
logger.debug { "Connecting to service provider at #{url}..." }
|
86
88
|
ApplicationServiceProvider.remote_instance(url)
|
87
89
|
end
|
@@ -109,7 +111,17 @@ module CaRuby
|
|
109
111
|
def dispatch
|
110
112
|
time { yield app_service }
|
111
113
|
end
|
112
|
-
|
114
|
+
|
115
|
+
# @return [String] the default host name
|
116
|
+
def default_host
|
117
|
+
# # TODO - extract from the service config file
|
118
|
+
# xml = JRuby.runtime.jruby_class_loader.getResourceAsStream('remoteService.xml')
|
119
|
+
# if xml then
|
120
|
+
# # parse xml file
|
121
|
+
# end
|
122
|
+
'localhost'
|
123
|
+
end
|
124
|
+
|
113
125
|
def query_hql(hql)
|
114
126
|
logger.debug { "Building HQLCriteria..." }
|
115
127
|
criteria = HQLCriteria.new(hql)
|
@@ -549,6 +549,8 @@ module CaRuby
|
|
549
549
|
#
|
550
550
|
# @param (see #sync_saved)
|
551
551
|
def sync_save_result(source, target)
|
552
|
+
# Bail if the result is the same as the source, as occurs, e.g., with caTissue annotations.
|
553
|
+
return if source == target
|
552
554
|
logger.debug { "Synchronizing #{target} save result #{source} with the database..." }
|
553
555
|
# If the target was created, then refetch and merge the source if necessary to reflect auto-generated
|
554
556
|
# non-domain attribute values.
|
data/lib/caruby/database.rb
CHANGED
@@ -80,6 +80,7 @@ module CaRuby
|
|
80
80
|
#
|
81
81
|
# @param [String] service_name the name of the default {PersistenceService}
|
82
82
|
# @param [{Symbol => String}] opts access options
|
83
|
+
# @option opts [String] :host application service host name
|
83
84
|
# @option opts [String] :login application service login user
|
84
85
|
# @option opts [String] :password application service login password
|
85
86
|
# @example
|
@@ -91,9 +92,10 @@ module CaRuby
|
|
91
92
|
@defaults = {}
|
92
93
|
@user = Options.get(:user, opts)
|
93
94
|
@password = Options.get(:password, opts)
|
94
|
-
|
95
|
+
host = Options.get(:host, opts)
|
96
|
+
port = Options.get(:port, opts)
|
95
97
|
# class => service hash; default is the catissuecore app service
|
96
|
-
@def_persist_svc = PersistenceService.new(service_name, :host =>
|
98
|
+
@def_persist_svc = PersistenceService.new(service_name, :host => host, :port => port)
|
97
99
|
@persistence_services = [@def_persist_svc].to_set
|
98
100
|
@cls_svc_hash = Hash.new(@def_persist_svc)
|
99
101
|
# the create/update nested operations
|
@@ -73,6 +73,8 @@ module CaRuby
|
|
73
73
|
def java_attributes
|
74
74
|
@java_attrs ||= attribute_filter { |attr_md| attr_md.java_property? }
|
75
75
|
end
|
76
|
+
|
77
|
+
alias :printable_attributes :java_attributes
|
76
78
|
|
77
79
|
# @return [<Symbol>] the domain attributes
|
78
80
|
def domain_attributes
|
@@ -366,19 +368,19 @@ module CaRuby
|
|
366
368
|
# @yield [attr_md] the attribute selector
|
367
369
|
# @yieldparam [AttributeMetadata] attr_md the candidate attribute
|
368
370
|
def attribute_filter(&filter)
|
369
|
-
|
371
|
+
Filter.new(@attr_md_hash, &filter)
|
370
372
|
end
|
371
373
|
|
372
374
|
# Initializes the attribute meta-data structures.
|
373
375
|
def init_attributes
|
374
376
|
@local_std_attr_hash = {}
|
375
|
-
@alias_std_attr_map =
|
377
|
+
@alias_std_attr_map = append_ancestor_enum(@local_std_attr_hash) { |par| par.alias_standard_attribute_hash }
|
376
378
|
@local_attr_md_hash = {}
|
377
|
-
@attr_md_hash =
|
379
|
+
@attr_md_hash = append_ancestor_enum(@local_attr_md_hash) { |par| par.attribute_metadata_hash }
|
378
380
|
@attributes = Enumerable::Enumerator.new(@attr_md_hash, :each_key)
|
379
381
|
@local_mndty_attrs = Set.new
|
380
382
|
@local_defaults = {}
|
381
|
-
@defaults =
|
383
|
+
@defaults = append_ancestor_enum(@local_defaults) { |par| par.defaults }
|
382
384
|
end
|
383
385
|
|
384
386
|
# Creates the given aliases to attributes.
|
@@ -472,9 +474,13 @@ module CaRuby
|
|
472
474
|
@local_std_attr_hash[aliaz.to_sym] = std_attr
|
473
475
|
end
|
474
476
|
|
475
|
-
#
|
476
|
-
#
|
477
|
-
|
477
|
+
# Appends to the given enumerable the result of evaluating the block given to this method
|
478
|
+
# on the superclass, if the superclass is a {Resource}.
|
479
|
+
#
|
480
|
+
# @param [Enumerable] enum the base collection
|
481
|
+
# @return [Enumerable] the {Enumerable#union} of the base collection with the superclass
|
482
|
+
# collection, if applicable
|
483
|
+
def append_ancestor_enum(enum)
|
478
484
|
superclass < Resource ? enum.union(yield(superclass)) : enum
|
479
485
|
end
|
480
486
|
|
@@ -506,7 +512,7 @@ module CaRuby
|
|
506
512
|
# remove autogenerated or optional attributes
|
507
513
|
mandatory.delete_if { |attr| attribute_metadata(attr).autogenerated? or attribute_metadata(attr).optional? }
|
508
514
|
@local_mndty_attrs.merge!(mandatory)
|
509
|
-
|
515
|
+
append_ancestor_enum(@local_mndty_attrs) { |par| par.mandatory_attributes }
|
510
516
|
end
|
511
517
|
|
512
518
|
# Raises a NameError. Domain classes can override this method to dynamically create a new reference attribute.
|
@@ -10,6 +10,7 @@ module CaRuby
|
|
10
10
|
[:user, "--user USER", "the application login user"],
|
11
11
|
[:password, "--password PSWD", "the application login password"],
|
12
12
|
[:host, "--host HOST", "the application host name"],
|
13
|
+
[:port, "--port PORT", "the application port number"],
|
13
14
|
[:database_host, "--database_host HOST", "the database host name"],
|
14
15
|
[:database_type, "--database_type TYPE", "the database type (mysql or oracle)"],
|
15
16
|
[:database_driver, "--database_driver DRIVER", "the database driver"],
|
@@ -27,7 +28,7 @@ module CaRuby
|
|
27
28
|
#
|
28
29
|
# The application properties hash specifies the startup information,
|
29
30
|
# including the user, password and application Java jar library path.
|
30
|
-
# The properties are read from a property file. See
|
31
|
+
# The properties are read from a property file. See {Properties} for
|
31
32
|
# more information.
|
32
33
|
#
|
33
34
|
# A Java class is imported into Ruby either by directly calling the
|
@@ -45,7 +46,9 @@ module CaRuby
|
|
45
46
|
# ClinicalTrials::Participant.new
|
46
47
|
# without defining the +Participant+ Ruby class.
|
47
48
|
module ResourceModule
|
48
|
-
# Adds the given
|
49
|
+
# Adds the given class to this ResourceModule. The class is extended with ResourceMetadata methods.
|
50
|
+
#
|
51
|
+
# @param [Class] the {Resource} class to add
|
49
52
|
def add_class(klass)
|
50
53
|
@rsc_classes ||= Set.new
|
51
54
|
# add superclass if necessary
|
@@ -65,7 +68,7 @@ module CaRuby
|
|
65
68
|
# @return [{Symbol => Object}] the caBIG application access properties
|
66
69
|
# @see #load_access_properties
|
67
70
|
def access_properties
|
68
|
-
@
|
71
|
+
@rsc_props ||= load_access_properties
|
69
72
|
end
|
70
73
|
|
71
74
|
# Loads the application start-up properties in the given file path.
|
@@ -97,7 +100,7 @@ module CaRuby
|
|
97
100
|
raise ArgumentError.new("Application access properties file does not exist: #{file}")
|
98
101
|
end
|
99
102
|
# the access properties
|
100
|
-
|
103
|
+
props ||= {}
|
101
104
|
# If no file was specified, then try the default.
|
102
105
|
# If the default does not exist, then use the empty properties hash.
|
103
106
|
# It is not an error to omit access properties, since the application domain classes
|
@@ -113,9 +116,8 @@ module CaRuby
|
|
113
116
|
name = tokens.first.to_sym
|
114
117
|
value = tokens.last
|
115
118
|
# capture the property
|
116
|
-
|
119
|
+
props[name] = value
|
117
120
|
end
|
118
|
-
|
119
121
|
# Look for environment overrides preceded by the uppercase module name, e.g. CATISSUE
|
120
122
|
# for the CaTissue module.
|
121
123
|
env_prefix = name[/\w+$/].upcase
|
@@ -127,22 +129,22 @@ module CaRuby
|
|
127
129
|
# the envvar value
|
128
130
|
value = ENV[ev] || next
|
129
131
|
# override the file property with the envar value
|
130
|
-
|
132
|
+
props[opt] = value
|
131
133
|
logger.info("Set application property #{opt} from environment variable #{ev}.")
|
132
134
|
end
|
133
135
|
|
134
136
|
# load the Java application jar path
|
135
137
|
path_ev = "#{env_prefix}_PATH"
|
136
|
-
path = ENV[path_ev] ||
|
138
|
+
path = ENV[path_ev] || props[:path]
|
137
139
|
Java.add_path(path) if path
|
138
140
|
|
139
|
-
|
141
|
+
props
|
140
142
|
end
|
141
143
|
|
142
144
|
# Loads the Ruby source files in the given directory.
|
143
145
|
def load_dir(dir)
|
144
146
|
# load the properties on demand
|
145
|
-
load_access_properties if @
|
147
|
+
load_access_properties if @rsc_props.nil?
|
146
148
|
# the domain class definitions
|
147
149
|
sources = Dir.glob(File.join(dir, "*.rb"))
|
148
150
|
|
@@ -156,7 +158,7 @@ module CaRuby
|
|
156
158
|
end
|
157
159
|
|
158
160
|
# load the domain class definitions
|
159
|
-
sym_file_hash.each do |sym, file|
|
161
|
+
sym_file_hash.to_a.each do |sym, file|
|
160
162
|
require file
|
161
163
|
end
|
162
164
|
|
@@ -168,6 +170,11 @@ module CaRuby
|
|
168
170
|
logger.info("#{klass.pp_s}")
|
169
171
|
end
|
170
172
|
end
|
173
|
+
|
174
|
+
def java_import(klass)
|
175
|
+
# JRuby 1.4.x does not support a class argument
|
176
|
+
Class === klass ? super(klass.java_class.name) : super
|
177
|
+
end
|
171
178
|
|
172
179
|
# Extends the mod module with Java class support. See the class documentation for details.
|
173
180
|
#
|
@@ -199,7 +206,7 @@ module CaRuby
|
|
199
206
|
#
|
200
207
|
# The optional block overrides the native Java property access wrappers.
|
201
208
|
# For example:
|
202
|
-
# ClinicalTrials.java_import
|
209
|
+
# ClinicalTrials.java_import Java::edu.wustl.catissuecore.domain.Study do
|
203
210
|
# def study_code=(value)
|
204
211
|
# value = value.to_s if Integer === value
|
205
212
|
# setStudyCode(value)
|
@@ -271,6 +278,9 @@ module CaRuby
|
|
271
278
|
# The captures are the trimmed property and value
|
272
279
|
PROP_DEF_REGEX = /^(\w+)(?:\s*[:=]\s*)([^#]+)/
|
273
280
|
|
281
|
+
# @return [String] the default application properties file, given by +~/.+_name_,
|
282
|
+
# where _name_ is the underscore unqualified module name, e.g. +~/.catissue+
|
283
|
+
# for module +CaTissue+
|
274
284
|
def default_properties_file
|
275
285
|
home = ENV['HOME'] || '~'
|
276
286
|
file = File.expand_path("#{home}/.#{name[/\w+$/].downcase}")
|
data/lib/caruby/import/java.rb
CHANGED
@@ -15,22 +15,36 @@ module Java
|
|
15
15
|
# Adds the directories in the given path and all Java jar files contained in the directories
|
16
16
|
# to the execution classpath.
|
17
17
|
#
|
18
|
-
# @param path the colon or semi-colon separated directories
|
18
|
+
# @param [String] path the colon or semi-colon separated directories
|
19
19
|
def self.add_path(path)
|
20
20
|
# the path directories
|
21
21
|
dirs = path.split(/[:;]/).map { |dir| File.expand_path(dir) }
|
22
22
|
# Add all jars found anywhere within the directories to the the classpath.
|
23
23
|
add_jars(*dirs)
|
24
24
|
# Add the directories to the the classpath.
|
25
|
-
dirs.each { |dir|
|
25
|
+
dirs.each { |dir| add_to_classpath(dir) }
|
26
26
|
end
|
27
27
|
|
28
28
|
# Adds the jars in the directories to the execution class path.
|
29
29
|
#
|
30
|
-
# @param directories the directories containing jars to add
|
30
|
+
# @param [<String>] directories the directories containing jars to add
|
31
31
|
def self.add_jars(*directories)
|
32
32
|
directories.each do |dir|
|
33
|
-
Dir[File.join(dir , "**", "*.jar")].each { |jar|
|
33
|
+
Dir[File.join(dir , "**", "*.jar")].each { |jar| add_to_classpath(jar) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Adds the given jar file or directory to the classpath.
|
38
|
+
#
|
39
|
+
# @param [String] file the jar file or directory to add
|
40
|
+
def self.add_to_classpath(file)
|
41
|
+
if file =~ /.jar$/ then
|
42
|
+
# require is preferred to classpath append for a jar file
|
43
|
+
require file
|
44
|
+
else
|
45
|
+
# A directory must end in a slash since JRuby uses an URLClassLoader.
|
46
|
+
if File.directory?(file) and not file =~ /\/$/ then file = file + '/' end
|
47
|
+
$CLASSPATH << file
|
34
48
|
end
|
35
49
|
end
|
36
50
|
|
@@ -118,55 +118,49 @@ module CaRuby
|
|
118
118
|
# @param [{Symbol => String}] mth_hash a hash that associates this domain object's
|
119
119
|
# attributes to migration method names
|
120
120
|
def migrate_references(row, migrated, mth_hash=nil)
|
121
|
-
self.class.saved_independent_attributes
|
122
|
-
|
123
|
-
migratable__set_reference(attr, ref, row, mth_hash) if ref
|
124
|
-
end
|
125
|
-
self.class.unidirectional_dependent_attributes.each do |attr|
|
126
|
-
ref = migratable__reference_value(attr, migrated)
|
127
|
-
migratable__set_reference(attr, ref, row, mth_hash) if ref
|
128
|
-
end
|
121
|
+
migratable__set_references(self.class.saved_independent_attributes, row, migrated, mth_hash)
|
122
|
+
migratable__set_references(self.class.unidirectional_dependent_attributes, row, migrated, mth_hash)
|
129
123
|
end
|
130
124
|
|
131
125
|
private
|
132
126
|
|
133
|
-
# @param [
|
127
|
+
# @param [AttributeMetadata::Filter] the attributes to set
|
128
|
+
# @param row (see #migrate_references)
|
129
|
+
# @param migrated (see #migrate_references)
|
130
|
+
# @param mth_hash (see #migrate_references)
|
131
|
+
def migratable__set_references(attr_filter, row, migrated, mth_hash=nil)
|
132
|
+
attr_filter.each_pair do |attr, attr_md|
|
133
|
+
# the target value
|
134
|
+
ref = migratable__target_value(attr_md, row, migrated, mth_hash) || next
|
135
|
+
if attr_md.collection? then
|
136
|
+
# the current value
|
137
|
+
value = send(attr_md.reader) || next
|
138
|
+
value << ref
|
139
|
+
logger.debug { "Added migrated #{ref.qp} to #{qp} #{attribute}." }
|
140
|
+
else
|
141
|
+
set_attribute(attr, ref)
|
142
|
+
logger.debug { "Set #{qp} #{attr} to migrated #{ref.qp}." }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# @param [AttributeMetadata] attr_md the reference attribute
|
148
|
+
# @param row (see #migrate_references)
|
134
149
|
# @param migrated (see #migrate_references)
|
135
|
-
# @
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
# the attribute metadata, used for type information
|
140
|
-
attr_md = self.class.attribute_metadata(attribute)
|
141
|
-
# skip collection attributes
|
142
|
-
return if attr_md.collection?
|
150
|
+
# @param mth_hash (see #migrate_references)
|
151
|
+
# @return [Resource, nil] the migrated instance of the given class, or nil if there is not
|
152
|
+
# exactly one such instance
|
153
|
+
def migratable__target_value(attr_md, row, migrated, mth_hash=nil)
|
143
154
|
# the migrated references which are instances of the attribute type
|
144
155
|
refs = migrated.select { |other| other != self and attr_md.type === other }
|
145
156
|
# skip ambiguous references
|
146
157
|
return unless refs.size == 1
|
147
158
|
# the single reference
|
148
159
|
ref = refs.first
|
149
|
-
|
150
|
-
|
151
|
-
# Sets the given migrated domain object attribute to the given reference.
|
152
|
-
#
|
153
|
-
# If the attribute is associated to a method in mth_hash, then that method is called on
|
154
|
-
# the migrated instance and input row. The attribute is set to the method return value.
|
155
|
-
# mth_hash includes an entry for each +migrate_+_attribute_ method defined by this
|
156
|
-
# Resource's class.
|
157
|
-
#
|
158
|
-
# @param [Symbol] (see #migratable__reference_value)
|
159
|
-
# @param [Resource] ref the migrated reference
|
160
|
-
# @param row (see #migrate_references)
|
161
|
-
# @param mth_hash (see #migrate_references)
|
162
|
-
def migratable__set_reference(attribute, ref, row, mth_hash=nil)
|
163
|
-
# the shim method, if any
|
164
|
-
mth = mth_hash[attribute] if mth_hash
|
160
|
+
# the shim method, if any
|
161
|
+
mth = mth_hash[attr_md.to_sym] if mth_hash
|
165
162
|
# if there is a shim method, then call it
|
166
|
-
|
167
|
-
return if ref.nil?
|
168
|
-
logger.debug { "Setting #{qp} #{attribute} to migrated #{ref.qp}..." }
|
169
|
-
set_attribute(attribute, ref)
|
163
|
+
mth && respond_to?(mth) ? send(mth, ref, row) : ref
|
170
164
|
end
|
171
165
|
end
|
172
166
|
end
|
@@ -99,6 +99,7 @@ module CaRuby
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def parse_options(opts)
|
102
|
+
logger.debug { "Migrator options: #{opts.qp}" }
|
102
103
|
@fld_map_file = opts[:mapping]
|
103
104
|
raise MigrationError.new("Migrator missing required field mapping file parameter") if @fld_map_file.nil?
|
104
105
|
@def_file = opts[:defaults]
|
@@ -145,6 +146,7 @@ module CaRuby
|
|
145
146
|
end
|
146
147
|
logger.info { "Migration paths:\n#{print_hash.pp_s}" }
|
147
148
|
logger.info { "Migration creatable classes: #{@creatable_classes.qp}." }
|
149
|
+
unless @def_hash.empty? then logger.info { "Migration defaults: #{@def_hash.qp}." } end
|
148
150
|
|
149
151
|
# add shim modifiers
|
150
152
|
load_shims(@shims)
|
@@ -540,7 +542,7 @@ module CaRuby
|
|
540
542
|
# collect the class => path => value entries
|
541
543
|
map = LazyHash.new { Hash.new }
|
542
544
|
config.each do |path_s, value|
|
543
|
-
next if value.
|
545
|
+
next if value.nil_or_empty?
|
544
546
|
klass, path = create_attribute_path(path_s)
|
545
547
|
map[klass][path] = value
|
546
548
|
end
|
@@ -555,7 +557,7 @@ module CaRuby
|
|
555
557
|
names = path_s.split('.')
|
556
558
|
# if the path starts with a capitalized class name, then resolve the class.
|
557
559
|
# otherwise, the target class is the start of the path.
|
558
|
-
klass = names.first =~ /^[A-Z]/ ?
|
560
|
+
klass = names.first =~ /^[A-Z]/ ? class_for_name(names.shift) : @target_class
|
559
561
|
# there must be at least one attribute
|
560
562
|
if names.empty? then
|
561
563
|
raise MigrationError.new("Attribute entry in migration configuration is not in <class>.<attribute> format: #{value}")
|
@@ -574,6 +576,13 @@ module CaRuby
|
|
574
576
|
# important, since the class must be instantiated.
|
575
577
|
[klass, path]
|
576
578
|
end
|
579
|
+
|
580
|
+
def class_for_name(name)
|
581
|
+
# navigate through the scope to the final class
|
582
|
+
name.split('::').inject(@target_class.domain_module) do |scope, cnm|
|
583
|
+
scope.const_get(cnm)
|
584
|
+
end
|
585
|
+
end
|
577
586
|
|
578
587
|
# @return a new class => [paths] hash from the migration fields configuration map
|
579
588
|
def create_class_paths_hash(fld_map, def_map)
|
data/lib/caruby/resource.rb
CHANGED
@@ -33,6 +33,8 @@ module CaRuby
|
|
33
33
|
"#{self.class.qp}@#{proxy_object_id}"
|
34
34
|
end
|
35
35
|
|
36
|
+
alias :qp :print_class_and_id
|
37
|
+
|
36
38
|
# Sets the default attribute values for this domain object and its dependents. If this Resource
|
37
39
|
# does not have an identifier, then missing attributes are set to the values defined by
|
38
40
|
# {ResourceAttributes#add_attribute_defaults}.
|
@@ -54,25 +56,22 @@ module CaRuby
|
|
54
56
|
end
|
55
57
|
|
56
58
|
# Validates this domain object and its #{ResourceAttributes.unproxied_cascaded_attributes}
|
57
|
-
# for completeness prior to a database create
|
58
|
-
#
|
59
|
-
# Objects which have already been validated are skipped.
|
59
|
+
# for completeness prior to a database create operation.
|
60
|
+
# An object without an identifer is valid if it contains a non-nil value for each mandatory property.
|
61
|
+
# Objects which have an identifier or have already been validated are skipped.
|
62
|
+
#
|
63
|
+
# Subclasses should not override this method, but override the private {#local_validate} instead.
|
60
64
|
#
|
61
65
|
# @return [Resource] this domain object
|
62
|
-
# @raise
|
66
|
+
# @raise (see #local_validate)
|
63
67
|
def validate
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
unless invalid.empty? then
|
68
|
-
logger.error("Validation of #{qp} unsuccessful - missing #{invalid.join(', ')}:\n#{dump}")
|
69
|
-
raise ValidationError.new("Required attribute value missing for #{self}: #{invalid.join(', ')}")
|
70
|
-
end
|
68
|
+
if identifier.nil? and not @validated then
|
69
|
+
validate_local
|
70
|
+
@validated = true
|
71
71
|
end
|
72
72
|
self.class.unproxied_cascaded_attributes.each do |attr|
|
73
73
|
send(attr).enumerate { |dep| dep.validate }
|
74
74
|
end
|
75
|
-
@validated = true
|
76
75
|
self
|
77
76
|
end
|
78
77
|
|
@@ -550,6 +549,20 @@ module CaRuby
|
|
550
549
|
merge_attributes(self.class.defaults)
|
551
550
|
end
|
552
551
|
|
552
|
+
# Validates that this domain contains a non-nil value for each mandatory property.
|
553
|
+
#
|
554
|
+
# Subclasses can override this method for additional validation, but should call super first.
|
555
|
+
#
|
556
|
+
# @raise [ValidationError] if a mandatory attribute value is missing
|
557
|
+
def validate_local
|
558
|
+
logger.debug { "Validating #{qp} required attributes #{self.mandatory_attributes.to_a.to_series}..." }
|
559
|
+
invalid = missing_mandatory_attributes
|
560
|
+
unless invalid.empty? then
|
561
|
+
logger.error("Validation of #{qp} unsuccessful - missing #{invalid.join(', ')}:\n#{dump}")
|
562
|
+
raise ValidationError.new("Required attribute value missing for #{self}: #{invalid.join(', ')}")
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
553
566
|
# Enumerates the dependents for setting defaults. Subclasses can override if the
|
554
567
|
# dependents must be visited in a certain order.
|
555
568
|
alias :each_defaults_dependent :each_dependent
|
@@ -574,7 +587,7 @@ module CaRuby
|
|
574
587
|
def initialize(base, visited=Set.new, &selector)
|
575
588
|
@base = base
|
576
589
|
@visited = visited << base
|
577
|
-
@selector = selector || Proc.new { |ref| ref.class.
|
590
|
+
@selector = selector || Proc.new { |ref| ref.class.printable_attributes }
|
578
591
|
end
|
579
592
|
|
580
593
|
def pretty_print(q)
|
@@ -92,6 +92,13 @@ module Enumerable
|
|
92
92
|
detect { true }
|
93
93
|
end
|
94
94
|
|
95
|
+
# Returns the last enumerated item in this Enumerable, or nil if this Enumerable is empty.
|
96
|
+
#
|
97
|
+
# This method is functionally equivalent to +to_a.last+ but is more concise and efficient.
|
98
|
+
def last
|
99
|
+
detect { true }
|
100
|
+
end
|
101
|
+
|
95
102
|
# Returns the count of items enumerated in this Enumerable.
|
96
103
|
#
|
97
104
|
# This method is functionally equivalent to +to_a.size+ but is more concise and efficient
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
# JRuby alert - SyncEnumerator moved from generator to REXML in JRuby 1.5
|
2
|
+
require 'rexml/document'
|
2
3
|
|
3
4
|
# A Coordinate is a convenience Array wrapper class with aliased #x, #y and {#z} dimensions.
|
4
5
|
class Coordinate < Array
|
@@ -58,7 +59,7 @@ class Coordinate < Array
|
|
58
59
|
return true if equal?(other)
|
59
60
|
raise TypeError.new("Can't compare #{self} with #{other} since it is not a Coordinate") unless Coordinate === other
|
60
61
|
raise ArgumentError.new("Can't compare #{self} with #{other} since it has a different dimension count") unless size == other.size
|
61
|
-
SyncEnumerator.new(self.reverse, other.reverse).each_with_index do |pair, index|
|
62
|
+
REXML::SyncEnumerator.new(self.reverse, other.reverse).each_with_index do |pair, index|
|
62
63
|
dim = pair.first
|
63
64
|
odim = pair.last
|
64
65
|
raise ArgumentError.new("Can't compare #{self} with missing dimension #{index} to #{other}") unless dim
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class String
|
2
|
+
# @return [Integer] the integer equivalent of this roman numeral
|
3
|
+
# @raise ArgumentError if this String is not a roman numeral in the range I-X
|
4
|
+
def to_arabic
|
5
|
+
case self
|
6
|
+
when /^(I{0,3})$/ then $1.size
|
7
|
+
when /^(I{0,3})(V|X)$/ then ROMAN_UNITS[$2] - $1.size
|
8
|
+
when /^(V)(I{0,3})$/ then ROMAN_UNITS[$1] + $2.size
|
9
|
+
else raise ArgumentError.new("#{self} is not a roman numeral in the range I-X")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
ROMAN_UNITS = {'I' => 1, 'V' => 5, 'X' => 10}
|
16
|
+
end
|
17
|
+
|
18
|
+
class Integer
|
19
|
+
# @return [String] the roman numeral equivalent of this integer
|
20
|
+
def to_roman
|
21
|
+
if self < 1 or self > 10 then raise ArgumentError.new("#{self} cannot be converted to a roman numeral in the range I-X")
|
22
|
+
elsif self < 4 then 'I' * self
|
23
|
+
elsif self < 6 then ('I' * (5 - self)) + 'V'
|
24
|
+
elsif self < 9 then 'V' + ('I' * (self - 5))
|
25
|
+
else ('I' * (10 - self)) + 'X'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/caruby/version.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift 'lib'
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
require 'caruby/util/roman'
|
5
|
+
|
6
|
+
class RomanTest < Test::Unit::TestCase
|
7
|
+
def test_to_arabic
|
8
|
+
['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ].each_with_index do |s, n|
|
9
|
+
assert_equal(n + 1, s.to_arabic, "Conversion of #{s} incorrect")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_to_roman
|
14
|
+
['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ].each_with_index do |s, n|
|
15
|
+
assert_equal(s, (n + 1).to_roman, "Conversion of #{n + 1} incorrect")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: caruby-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.4.
|
5
|
+
version: 1.4.7
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- OHSU
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-03-04 00:00:00 -08:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
type: :runtime
|
26
26
|
version_requirements: *id001
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: dbd-jdbc
|
29
29
|
prerelease: false
|
30
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
31
31
|
none: false
|
@@ -36,7 +36,7 @@ dependencies:
|
|
36
36
|
type: :runtime
|
37
37
|
version_requirements: *id002
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
|
-
name:
|
39
|
+
name: fastercsv
|
40
40
|
prerelease: false
|
41
41
|
requirement: &id003 !ruby/object:Gem::Requirement
|
42
42
|
none: false
|
@@ -46,6 +46,17 @@ dependencies:
|
|
46
46
|
version: "0"
|
47
47
|
type: :runtime
|
48
48
|
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: uom
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
type: :runtime
|
59
|
+
version_requirements: *id004
|
49
60
|
description: " caRuby is a JRuby facade for interaction with caBIG applications.\n"
|
50
61
|
email: caruby.org@gmail.com
|
51
62
|
executables: []
|
@@ -117,6 +128,7 @@ files:
|
|
117
128
|
- lib/caruby/util/person.rb
|
118
129
|
- lib/caruby/util/pretty_print.rb
|
119
130
|
- lib/caruby/util/properties.rb
|
131
|
+
- lib/caruby/util/roman.rb
|
120
132
|
- lib/caruby/util/stopwatch.rb
|
121
133
|
- lib/caruby/util/topological_sync_enumerator.rb
|
122
134
|
- lib/caruby/util/transitive_closure.rb
|
@@ -151,6 +163,7 @@ files:
|
|
151
163
|
- test/lib/caruby/util/person_test.rb
|
152
164
|
- test/lib/caruby/util/pretty_print_test.rb
|
153
165
|
- test/lib/caruby/util/properties_test.rb
|
166
|
+
- test/lib/caruby/util/roman_test.rb
|
154
167
|
- test/lib/caruby/util/stopwatch_test.rb
|
155
168
|
- test/lib/caruby/util/topological_sync_enumerator_test.rb
|
156
169
|
- test/lib/caruby/util/transitive_closure_test.rb
|
@@ -188,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
201
|
requirements: []
|
189
202
|
|
190
203
|
rubyforge_project: caruby
|
191
|
-
rubygems_version: 1.5.
|
204
|
+
rubygems_version: 1.5.3
|
192
205
|
signing_key:
|
193
206
|
specification_version: 3
|
194
207
|
summary: Ruby facade for caBIG applications.
|