caruby-core 1.4.3 → 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
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: