caruby-core 1.4.6 → 1.4.7
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.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.
|