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 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**: November 23rd 2010
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
- - **November 23, 2010**: 1.4.1 release
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.
@@ -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? ? send(attr_md.writer, newval) : oldval
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 are not saved with a proxy
171
- # using the dependent saver_proxy method
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
- # collect the String input fields for the custom CSVLoader converter
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 klass instance
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[klass].each do |path|
387
- header = @header_map[path][klass]
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
- logger.debug { "Migrator created #{obj}." }
397
- obj
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
- # Returns an array of AttributeMetadata objects for the period-delimited path string path_s in the
504
- # form _class_(._attribute_)+.
505
- #
506
- # Raises MigrationError if the path string is malformed or an attribute is not found.
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 Migratable
6
- # Unique makes a Migratable Resource domain object unique within the scope its class.
5
+ module Resource
7
6
  module Unique
8
- include CaRuby::Resource::Unique
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 CaRuby::Migratable#migrate)
10
+ # @param (see Migratable#migrate)
13
11
  def migrate(row, migrated)
14
12
  super
15
13
  logger.debug { "Migrator making #{self} unique..." }
@@ -39,12 +39,12 @@ module CaRuby
39
39
  self
40
40
  end
41
41
 
42
- # Validates this domain object and its cascaded dependents for completeness prior to a
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.cascaded_attributes.each do |attr|
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
- (identifier.nil? or other.identifier.nil? or identifier == other.identifier)
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
@@ -1,3 +1,3 @@
1
1
  module CaRuby
2
- VERSION = "1.4.3"
2
+ VERSION = "1.4.4"
3
3
  end
metadata CHANGED
@@ -6,11 +6,11 @@ executables: []
6
6
 
7
7
  version: !ruby/object:Gem::Version
8
8
  prerelease: false
9
- version: 1.4.3
9
+ version: 1.4.4
10
10
  segments:
11
11
  - 1
12
12
  - 4
13
- - 3
13
+ - 4
14
14
  post_install_message:
15
15
  date: 2010-11-30 08:00:00 +00:00
16
16
  files: