caruby-core 1.4.3 → 1.4.4
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 +6 -1
- data/README.md +4 -5
- data/lib/caruby/domain/merge.rb +7 -1
- data/lib/caruby/domain/resource_attributes.rb +2 -2
- data/lib/caruby/domain/uniquify.rb +12 -0
- data/lib/caruby/migration/migrator.rb +62 -14
- data/lib/caruby/migration/uniquify.rb +4 -6
- data/lib/caruby/resource.rb +6 -5
- data/lib/caruby/version.rb +1 -1
- metadata +2 -2
data/History.txt
CHANGED
@@ -6,13 +6,18 @@
|
|
6
6
|
|
7
7
|
* Minor Migrator fixes
|
8
8
|
|
9
|
-
|
10
9
|
=== 1.4.3 / 2011-02-18
|
11
10
|
|
12
11
|
* Refactor Persistifier
|
13
12
|
|
14
13
|
* Add attribute filters
|
15
14
|
|
15
|
+
=== 1.4.4 / 2011-02-25
|
16
|
+
|
17
|
+
* Support default migration option
|
18
|
+
|
19
|
+
* Merge nondomain collection value properly.
|
20
|
+
|
16
21
|
|
17
22
|
|
18
23
|
|
data/README.md
CHANGED
@@ -4,10 +4,10 @@ caRuby: Simplifying caBIG(TM)
|
|
4
4
|
**Home**: [http://caruby.rubyforge.org](http://caruby.rubyforge.org)
|
5
5
|
**Git**: [http://github.com/caruby/core](http://github.com/caruby/core)
|
6
6
|
**Author**: OHSU Knight Cancer Institute
|
7
|
-
**Copyright**: 2010
|
7
|
+
**Copyright**: 2010, 2011
|
8
8
|
**License**: MIT License
|
9
9
|
**Latest Version**: 1.4.1
|
10
|
-
**Release Date**:
|
10
|
+
**Release Date**: February 25th 2011
|
11
11
|
|
12
12
|
Synopsis
|
13
13
|
--------
|
@@ -40,12 +40,11 @@ See the project [Home](http://caruby.rubyforge.org) Page for usage examples.
|
|
40
40
|
Changelog
|
41
41
|
---------
|
42
42
|
|
43
|
-
-
|
44
|
-
- Initial public release
|
43
|
+
- See +History.txt+
|
45
44
|
|
46
45
|
Copyright
|
47
46
|
---------
|
48
47
|
|
49
|
-
caRuby © 2010 by [Oregon Health & Science University](http://www.ohsu.edu/xd/health/services/cancer/index.cfm).
|
48
|
+
caRuby © 2010, 2011 by [Oregon Health & Science University](http://www.ohsu.edu/xd/health/services/cancer/index.cfm).
|
50
49
|
caRuby is licensed under the MIT license. Please see the LICENSE and LEGAL
|
51
50
|
files for more information.
|
data/lib/caruby/domain/merge.rb
CHANGED
@@ -95,7 +95,13 @@ module CaRuby
|
|
95
95
|
|
96
96
|
# @see #merge_attribute
|
97
97
|
def merge_nondomain_attribute_value(attr_md, oldval, newval)
|
98
|
-
oldval.nil?
|
98
|
+
if oldval.nil? then
|
99
|
+
send(attr_md.writer, newval)
|
100
|
+
elsif attr_md.collection? then
|
101
|
+
oldval.merge(newval)
|
102
|
+
else
|
103
|
+
oldval
|
104
|
+
end
|
99
105
|
end
|
100
106
|
|
101
107
|
# @see #merge_attribute
|
@@ -167,8 +167,8 @@ module CaRuby
|
|
167
167
|
@px_cscd_attrs ||= cascaded_attributes.compose { |attr_md| attr_md.proxied_save? }
|
168
168
|
end
|
169
169
|
|
170
|
-
# @return [<Symbol>] the {#cascaded_attributes} which
|
171
|
-
#
|
170
|
+
# @return [<Symbol>] the {#cascaded_attributes} which do not have a
|
171
|
+
# #{AttributeMetadata#proxied_save?}
|
172
172
|
def unproxied_cascaded_attributes
|
173
173
|
@unpx_cscd_attrs ||= cascaded_attributes.compose { |attr_md| not attr_md.proxied_save? }
|
174
174
|
end
|
@@ -15,6 +15,18 @@ module CaRuby
|
|
15
15
|
end
|
16
16
|
ResourceUniquifier.instance.uniquify(self, value)
|
17
17
|
end
|
18
|
+
|
19
|
+
# Makes the secondary key unique by replacing each String key attribute value
|
20
|
+
# with a unique value.
|
21
|
+
def uniquify
|
22
|
+
self.class.secondary_key_attributes.each do |attr|
|
23
|
+
oldval = send(attr)
|
24
|
+
next unless String === oldval
|
25
|
+
newval = uniquify_value(oldval)
|
26
|
+
set_attribute(attr, newval)
|
27
|
+
logger.debug { "Reset #{qp} #{attr} from #{oldval} to unique value #{newval}." }
|
28
|
+
end
|
29
|
+
end
|
18
30
|
end
|
19
31
|
end
|
20
32
|
|
@@ -16,6 +16,7 @@ require 'caruby/util/pretty_print'
|
|
16
16
|
require 'caruby/util/properties'
|
17
17
|
require 'caruby/util/collection'
|
18
18
|
require 'caruby/migration/migratable'
|
19
|
+
require 'caruby/migration/uniquify'
|
19
20
|
|
20
21
|
module CaRuby
|
21
22
|
class MigrationError < RuntimeError; end
|
@@ -30,8 +31,11 @@ module CaRuby
|
|
30
31
|
# @option opts [String] :database required application {CaRuby::Database}
|
31
32
|
# @option opts [String] :target required target domain class
|
32
33
|
# @option opts [String] :mapping required input field => caTissue attribute mapping file
|
34
|
+
# @option opts [String] :defaults optional caTissue attribute => value default mapping file
|
33
35
|
# @option opts [String] :input required source file to migrate
|
34
36
|
# @option opts [String] :shims optional array of shim files to load
|
37
|
+
# @option opts [String] :unique ensures that migrated objects which include the {Resource::Unique}
|
38
|
+
# mix-in do not conflict with existing or future objects (used for testing)
|
35
39
|
# @option opts [String] :bad optional invalid record file
|
36
40
|
# @option opts [Integer] :offset zero-based starting source record number to process (default 0)
|
37
41
|
# @option opts [Boolean] :quiet suppress output messages
|
@@ -67,6 +71,8 @@ module CaRuby
|
|
67
71
|
|
68
72
|
private
|
69
73
|
|
74
|
+
UNIQUIFY_SHIM = File.join(File.dirname(__FILE__), 'uniquify.rb')
|
75
|
+
|
70
76
|
# Class {#migrate} with a {#save} block.
|
71
77
|
def execute_save
|
72
78
|
@database.open do |db|
|
@@ -95,6 +101,7 @@ module CaRuby
|
|
95
101
|
def parse_options(opts)
|
96
102
|
@fld_map_file = opts[:mapping]
|
97
103
|
raise MigrationError.new("Migrator missing required field mapping file parameter") if @fld_map_file.nil?
|
104
|
+
@def_file = opts[:defaults]
|
98
105
|
@shims = opts[:shims] ||= []
|
99
106
|
@offset = opts[:offset] ||= 0
|
100
107
|
@input = Options.get(:input, opts)
|
@@ -121,8 +128,10 @@ module CaRuby
|
|
121
128
|
|
122
129
|
# create the class => path => header hash
|
123
130
|
fld_map = load_field_map(@fld_map_file)
|
131
|
+
# create the class => path => default value hash
|
132
|
+
@def_hash = @def_file ? load_defaults(@def_file) : {}
|
124
133
|
# create the class => paths hash
|
125
|
-
@cls_paths_hash = create_class_paths_hash(fld_map)
|
134
|
+
@cls_paths_hash = create_class_paths_hash(fld_map, @def_hash)
|
126
135
|
# create the path => class => header hash
|
127
136
|
@header_map = create_header_map(fld_map)
|
128
137
|
# add missing owner classes (copy the keys rather than using each_key since the hash is updated)
|
@@ -143,7 +152,7 @@ module CaRuby
|
|
143
152
|
# the class => attribute migration methods hash
|
144
153
|
create_migration_method_hashes
|
145
154
|
|
146
|
-
#
|
155
|
+
# Collect the String input fields for the custom CSVLoader converter.
|
147
156
|
@nonstring_headers = Set.new
|
148
157
|
logger.info("Migration attributes:")
|
149
158
|
@header_map.each do |path, cls_hdr_hash|
|
@@ -376,15 +385,25 @@ module CaRuby
|
|
376
385
|
#
|
377
386
|
# @param [Class] klass
|
378
387
|
# @param [{Symbol => Object}] row the input row
|
379
|
-
# @param [<Resource>] the migrated instances for this row
|
380
|
-
# @return the new
|
388
|
+
# @param [<Resource>] created the migrated instances for this row
|
389
|
+
# @return [Resource] the new instance
|
381
390
|
def create(klass, row, created)
|
382
391
|
# the new object
|
383
392
|
created << obj = klass.new
|
393
|
+
migrate_attributes(obj, row, created)
|
394
|
+
add_defaults(obj, row, created)
|
395
|
+
logger.debug { "Migrator created #{obj}." }
|
396
|
+
obj
|
397
|
+
end
|
398
|
+
|
399
|
+
# @param [Resource] the migration object
|
400
|
+
# @param row (see #create)
|
401
|
+
# @param [<Resource>] created (see #create)
|
402
|
+
def migrate_attributes(obj, row, created)
|
384
403
|
# for each input header which maps to a migratable target attribute metadata path,
|
385
404
|
# set the target attribute, creating intermediate objects as needed.
|
386
|
-
@cls_paths_hash[
|
387
|
-
header = @header_map[path][
|
405
|
+
@cls_paths_hash[obj.class].each do |path|
|
406
|
+
header = @header_map[path][obj.class]
|
388
407
|
# the input value
|
389
408
|
value = row[header]
|
390
409
|
next if value.nil?
|
@@ -393,8 +412,18 @@ module CaRuby
|
|
393
412
|
# set the attribute
|
394
413
|
migrate_attribute(ref, path.last, value, row)
|
395
414
|
end
|
396
|
-
|
397
|
-
|
415
|
+
end
|
416
|
+
|
417
|
+
# @param [Resource] the migration object
|
418
|
+
# @param row (see #create)
|
419
|
+
# @param [<Resource>] created (see #create)
|
420
|
+
def add_defaults(obj, row, created)
|
421
|
+
@def_hash[obj.class].each do |path, value|
|
422
|
+
# fill the reference path
|
423
|
+
ref = fill_path(obj, path[0...-1], row, created)
|
424
|
+
# set the attribute to the default value unless there is already a value
|
425
|
+
ref.merge_attribute(path.last.to_sym, value)
|
426
|
+
end
|
398
427
|
end
|
399
428
|
|
400
429
|
# Fills the given reference AttributeMetadata path starting at obj.
|
@@ -472,7 +501,7 @@ module CaRuby
|
|
472
501
|
next if attr_list.blank?
|
473
502
|
# the header accessor method for the field
|
474
503
|
header = @loader.accessor(field)
|
475
|
-
raise MigrationError.new("Field defined in migration configuration not found: #{field}") if header.nil?
|
504
|
+
raise MigrationError.new("Field defined in migration configuration #{file} not found in input file #{@input} headers: #{field}") if header.nil?
|
476
505
|
attr_list.split(/,\s*/).each do |path_s|
|
477
506
|
klass, path = create_attribute_path(path_s)
|
478
507
|
map[klass][path] = header
|
@@ -499,11 +528,29 @@ module CaRuby
|
|
499
528
|
end
|
500
529
|
map
|
501
530
|
end
|
531
|
+
|
532
|
+
def load_defaults(file)
|
533
|
+
# load the field mapping config file
|
534
|
+
begin
|
535
|
+
config = YAML::load_file(file)
|
536
|
+
rescue
|
537
|
+
raise MigrationError.new("Could not read defaults file #{file}: " + $!)
|
538
|
+
end
|
502
539
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
540
|
+
# collect the class => path => value entries
|
541
|
+
map = LazyHash.new { Hash.new }
|
542
|
+
config.each do |path_s, value|
|
543
|
+
next if value.blank?
|
544
|
+
klass, path = create_attribute_path(path_s)
|
545
|
+
map[klass][path] = value
|
546
|
+
end
|
547
|
+
|
548
|
+
map
|
549
|
+
end
|
550
|
+
|
551
|
+
# @param [String] path_s a period-delimited path string path_s in the form _class_(._attribute_)+
|
552
|
+
# @return [<AttributeMetadata>] the corresponding attribute metadata path
|
553
|
+
# @raise [MigrationError] if the path string is malformed or an attribute is not found
|
507
554
|
def create_attribute_path(path_s)
|
508
555
|
names = path_s.split('.')
|
509
556
|
# if the path starts with a capitalized class name, then resolve the class.
|
@@ -529,9 +576,10 @@ module CaRuby
|
|
529
576
|
end
|
530
577
|
|
531
578
|
# @return a new class => [paths] hash from the migration fields configuration map
|
532
|
-
def create_class_paths_hash(fld_map)
|
579
|
+
def create_class_paths_hash(fld_map, def_map)
|
533
580
|
hash = {}
|
534
581
|
fld_map.each { |klass, path_hdr_hash| hash[klass] = path_hdr_hash.keys.to_set }
|
582
|
+
def_map.each { |klass, path_val_hash| (hash[klass] ||= Set.new).merge(path_val_hash.keys) }
|
535
583
|
hash
|
536
584
|
end
|
537
585
|
|
@@ -2,14 +2,12 @@ require 'caruby/migration/migratable'
|
|
2
2
|
require 'caruby/domain/uniquify'
|
3
3
|
|
4
4
|
module CaRuby
|
5
|
-
module
|
6
|
-
# Unique makes a Migratable Resource domain object unique within the scope its class.
|
5
|
+
module Resource
|
7
6
|
module Unique
|
8
|
-
|
9
|
-
|
10
|
-
# Augments the migration by making this Resource object unique in the scope of its class.
|
7
|
+
# Augments this {Unique} mix-in with a {Migratable#migrate} method which calls {Unique#uniquify}
|
8
|
+
# to make this Resource object unique in the scope of its class.
|
11
9
|
#
|
12
|
-
# @param (see
|
10
|
+
# @param (see Migratable#migrate)
|
13
11
|
def migrate(row, migrated)
|
14
12
|
super
|
15
13
|
logger.debug { "Migrator making #{self} unique..." }
|
data/lib/caruby/resource.rb
CHANGED
@@ -39,12 +39,12 @@ module CaRuby
|
|
39
39
|
self
|
40
40
|
end
|
41
41
|
|
42
|
-
# Validates this domain object and its
|
43
|
-
# database create or update operation.
|
42
|
+
# Validates this domain object and its #{ResourceAttributes.unproxied_cascaded_attributes}
|
43
|
+
# for completeness prior to a database create or update operation.
|
44
44
|
# The object is valid if it contains a non-nil value for each mandatory property.
|
45
45
|
# Objects which have already been validated are skipped.
|
46
|
-
# Returns this domain object.
|
47
46
|
#
|
47
|
+
# @return [Resource] this domain object
|
48
48
|
# @raise [ValidationError] if a mandatory attribute value is missing
|
49
49
|
def validate
|
50
50
|
unless @validated then
|
@@ -55,7 +55,7 @@ module CaRuby
|
|
55
55
|
raise ValidationError.new("Required attribute value missing for #{self}: #{invalid.join(', ')}")
|
56
56
|
end
|
57
57
|
end
|
58
|
-
self.class.
|
58
|
+
self.class.unproxied_cascaded_attributes.each do |attr|
|
59
59
|
send(attr).enumerate { |dep| dep.validate }
|
60
60
|
end
|
61
61
|
@validated = true
|
@@ -330,7 +330,7 @@ module CaRuby
|
|
330
330
|
# @return [Boolean] whether this Resource equals other
|
331
331
|
def minimal_match?(other)
|
332
332
|
self.class === other and
|
333
|
-
|
333
|
+
(identifier.nil? or other.identifier.nil? or identifier == other.identifier)
|
334
334
|
end
|
335
335
|
|
336
336
|
# Returns an enumerator on the transitive closure of the reference attributes.
|
@@ -649,6 +649,7 @@ module CaRuby
|
|
649
649
|
# @return [Boolean] whether the other domain object matches this domain object on a
|
650
650
|
# secondary key without owner attributes
|
651
651
|
def match_without_owner_attribute?(other)
|
652
|
+
return unless other.class == self.class
|
652
653
|
oattrs = self.class.owner_attributes
|
653
654
|
return if oattrs.empty?
|
654
655
|
# match on the secondary key
|
data/lib/caruby/version.rb
CHANGED