jinx-migrate 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/History.md +4 -0
- data/lib/jinx/csv/csvio.rb +1 -1
- data/lib/jinx/migration/filter.rb +1 -1
- data/lib/jinx/migration/migrator.rb +35 -97
- data/lib/jinx/migration/version.rb +1 -1
- metadata +4 -6
- data/Gemfile.lock +0 -38
data/Gemfile
CHANGED
data/History.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
This history lists major release themes. See the GitHub commits (https://github.com/jinx/migrate)
|
2
2
|
for change details.
|
3
3
|
|
4
|
+
2.1.2 / 2012-06-12
|
5
|
+
------------------
|
6
|
+
* Support caSmall web service.
|
7
|
+
|
4
8
|
2.1.1 / 2012-04-13
|
5
9
|
------------------
|
6
10
|
* Initial public release spun off from caruby/core.
|
data/lib/jinx/csv/csvio.rb
CHANGED
@@ -110,7 +110,7 @@ module Jinx
|
|
110
110
|
when Enumerable then
|
111
111
|
''.parse_csv(:headers => hdr_opt, :header_converters => :symbol, :return_headers => true)
|
112
112
|
else
|
113
|
-
|
113
|
+
raise ArgumentError.new("CSV headers option value not supported: #{hdr_opt}")
|
114
114
|
end
|
115
115
|
# The field value accessors consist of the header row headers converted to a symbol.
|
116
116
|
@accessors = hdr_row.headers
|
@@ -139,7 +139,7 @@ module Jinx
|
|
139
139
|
reopt = if opt then
|
140
140
|
case opt
|
141
141
|
when 'i' then Regexp::IGNORECASE
|
142
|
-
else
|
142
|
+
else raise MigrationError.new("Migration value filter regular expression #{k} qualifier not supported: expected 'i', found '#{opt}'")
|
143
143
|
end
|
144
144
|
end
|
145
145
|
# the Regexp object
|
@@ -7,7 +7,6 @@ require 'jinx/helpers/lazy_hash'
|
|
7
7
|
require 'jinx/helpers/log'
|
8
8
|
require 'jinx/helpers/inflector'
|
9
9
|
require 'jinx/helpers/pretty_print'
|
10
|
-
require 'jinx/helpers/stopwatch'
|
11
10
|
require 'jinx/helpers/transitive_closure'
|
12
11
|
require 'jinx/migration/migratable'
|
13
12
|
require 'jinx/migration/reader'
|
@@ -20,15 +19,13 @@ module Jinx
|
|
20
19
|
class Migrator
|
21
20
|
include Enumerable
|
22
21
|
|
23
|
-
# Creates a new Migrator
|
24
|
-
# implements the +open+ method, which returns a {Migration::Reader}
|
22
|
+
# Creates a new Migrator from the given options.
|
25
23
|
#
|
26
24
|
# @param [{Symbol => Object}] opts the migration options
|
27
|
-
# @option opts [
|
25
|
+
# @option opts [Class] :target the required target domain class
|
28
26
|
# @option opts [<String>, String] :mapping the required input field => caTissue attribute mapping file(s)
|
29
27
|
# @option opts [String, Migration::Reader] :input the required input file name or an adapter which
|
30
28
|
# implements the {Migration::Reader} methods
|
31
|
-
# @option opts [Jinx::Database] :database the optional destination +Jinx::Database+
|
32
29
|
# @option opts [<String>, String] :defaults the optional caTissue attribute => value default mapping file(s)
|
33
30
|
# @option opts [<String>, String] :filters the optional caTissue attribute input value => caTissue value filter file(s)
|
34
31
|
# @option opts [<String>, String] :shims the optional shim file(s) to load
|
@@ -48,30 +45,13 @@ module Jinx
|
|
48
45
|
parse_options(opts)
|
49
46
|
build
|
50
47
|
end
|
51
|
-
|
52
|
-
# Imports this migrator's file into the database with the given connect options.
|
53
|
-
# This method creates or updates the domain objects mapped from the import source.
|
54
|
-
# If a block is given to this method, then the block is called on each stored
|
55
|
-
# migration target object.
|
56
|
-
#
|
57
|
-
# If the +:create+ option is set, then an input record for a target object which already
|
58
|
-
# exists in the database is noted in a debug log message and ignored rather than updated.
|
59
|
-
#
|
60
|
-
# @yield (see #migrate)
|
61
|
-
# @yieldparam (see #migrate)
|
62
|
-
# @return (see #migrate)
|
63
|
-
def migrate_to_database(&block)
|
64
|
-
# migrate with save
|
65
|
-
tm = Stopwatch.measure { execute_save(&block) }.elapsed
|
66
|
-
logger.debug { format_migration_time_log_message(tm) }
|
67
|
-
end
|
68
48
|
|
69
49
|
# Imports this migrator's CSV file and calls the given block on each migrated target
|
70
50
|
# domain object. If no block is given, then this method returns an array of the
|
71
51
|
# migrated target objects.
|
72
52
|
#
|
73
|
-
# @yield [target, row]
|
74
|
-
# @yieldparam [
|
53
|
+
# @yield [target, row] operates on the migration target
|
54
|
+
# @yieldparam [Resource] target the migrated target domain object
|
75
55
|
# @yieldparam [{Symbol => Object}] row the migration source record
|
76
56
|
def migrate(&block)
|
77
57
|
unless block_given? then
|
@@ -150,46 +130,10 @@ module Jinx
|
|
150
130
|
end
|
151
131
|
end
|
152
132
|
|
153
|
-
# {#migrate} with a {#save} block on the migration target. Each migrated object
|
154
|
-
# is created, if necessary, after the target save.
|
155
|
-
def execute_save
|
156
|
-
if @database.nil? then
|
157
|
-
Jinx.fail(MigrationError, "Migrator cannot save records since the database option was not specified.")
|
158
|
-
end
|
159
|
-
@database.open do |db|
|
160
|
-
migrate do |tgt, rec|
|
161
|
-
save(tgt, db)
|
162
|
-
# Ensure that each migrated object is created if necessary.
|
163
|
-
@migrated.each do |obj|
|
164
|
-
next if obj.identifier
|
165
|
-
logger.debug { "The migrator is saving the migrated #{obj}..." }
|
166
|
-
save(obj, db)
|
167
|
-
logger.debug { "The migrator saved the migrated #{obj}." }
|
168
|
-
end
|
169
|
-
yield(tgt, rec) if block_given?
|
170
|
-
db.clear
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
# @return a log message String for the given migration time in seconds
|
176
|
-
def format_migration_time_log_message(time)
|
177
|
-
# the database execution time
|
178
|
-
dt = @database.execution_time
|
179
|
-
if time > 120 then
|
180
|
-
time /= 60
|
181
|
-
dt /= 60
|
182
|
-
unit = "minutes"
|
183
|
-
else
|
184
|
-
unit = "seconds"
|
185
|
-
end
|
186
|
-
"Migration took #{'%.2f' % time} #{unit}, of which #{'%.2f' % dt} were database operations."
|
187
|
-
end
|
188
|
-
|
189
133
|
def parse_options(opts)
|
190
134
|
@fld_map_files = opts[:mapping]
|
191
135
|
if @fld_map_files.nil? then
|
192
|
-
|
136
|
+
raise MigrationError.new("Migrator missing required field mapping file parameter")
|
193
137
|
end
|
194
138
|
@def_files = opts[:defaults]
|
195
139
|
@flt_files = opts[:filters]
|
@@ -200,12 +144,11 @@ module Jinx
|
|
200
144
|
@from = opts[:from] ||= 1
|
201
145
|
@input = opts[:input]
|
202
146
|
if @input.nil? then
|
203
|
-
|
147
|
+
raise MigrationError.new("Migrator missing required source file parameter")
|
204
148
|
end
|
205
|
-
@database = opts[:database]
|
206
149
|
@target_class = opts[:target]
|
207
150
|
if @target_class.nil? then
|
208
|
-
|
151
|
+
raise MigrationError.new("Migrator missing required target class parameter")
|
209
152
|
end
|
210
153
|
@bad_file = opts[:bad]
|
211
154
|
@extract = opts[:extract]
|
@@ -225,7 +168,7 @@ module Jinx
|
|
225
168
|
|
226
169
|
def build
|
227
170
|
# the current source class => instance map
|
228
|
-
|
171
|
+
raise MigrationError.new("No file to migrate") if @input.nil?
|
229
172
|
|
230
173
|
# If the input is a file name, then make a CSV loader which only converts input fields
|
231
174
|
# corresponding to non-String attributes.
|
@@ -258,7 +201,7 @@ module Jinx
|
|
258
201
|
# An abstract class cannot be instantiated.
|
259
202
|
@creatable_classes.each do |klass|
|
260
203
|
if klass.abstract? then
|
261
|
-
|
204
|
+
raise MigrationError.new("Migrator cannot create the abstract class #{klass}; specify a subclass instead in the mapping file.")
|
262
205
|
end
|
263
206
|
end
|
264
207
|
|
@@ -544,7 +487,7 @@ module Jinx
|
|
544
487
|
@rejects.flush
|
545
488
|
logger.debug("Invalid record #{rec_no} was written to the rejects file #{@bad_file}.")
|
546
489
|
else
|
547
|
-
|
490
|
+
raise MigrationError.new("Migration not performed on record #{rec_no}")
|
548
491
|
end
|
549
492
|
# Bump the record count.
|
550
493
|
@rec_cnt += 1
|
@@ -593,7 +536,7 @@ module Jinx
|
|
593
536
|
# create an instance for each creatable class
|
594
537
|
created = Set.new
|
595
538
|
# the migrated objects
|
596
|
-
migrated = @creatable_classes.map { |klass|
|
539
|
+
migrated = @creatable_classes.map { |klass| create_instance(klass, row, created) }
|
597
540
|
# migrate each object from the input row
|
598
541
|
migrated.each do |obj|
|
599
542
|
# First uniquify the object if necessary.
|
@@ -691,7 +634,7 @@ module Jinx
|
|
691
634
|
# @param [{Symbol => Object}] row the input row
|
692
635
|
# @param [<Resource>] created the migrated instances for this row
|
693
636
|
# @return [Resource] the new instance
|
694
|
-
def
|
637
|
+
def create_instance(klass, row, created)
|
695
638
|
# the new object
|
696
639
|
logger.debug { "The migrator is building #{klass.qp}..." }
|
697
640
|
created << obj = klass.new
|
@@ -758,7 +701,7 @@ module Jinx
|
|
758
701
|
# @return the new object
|
759
702
|
def create_reference(obj, property, row, created)
|
760
703
|
if property.type.abstract? then
|
761
|
-
|
704
|
+
raise MigrationError.new("Cannot create #{obj.qp} #{property} with abstract type #{property.type}")
|
762
705
|
end
|
763
706
|
ref = property.type.new
|
764
707
|
ref.migrate(row, Array::EMPTY_ARRAY)
|
@@ -785,8 +728,8 @@ module Jinx
|
|
785
728
|
# set the attribute
|
786
729
|
begin
|
787
730
|
obj.send(property.writer, value)
|
788
|
-
rescue Exception
|
789
|
-
|
731
|
+
rescue Exception
|
732
|
+
raise MigrationError.new("Could not set #{obj.qp} #{property} to #{value.qp} - " + $!)
|
790
733
|
end
|
791
734
|
logger.debug { "Migrated #{obj.qp} #{property} to #{value}." }
|
792
735
|
end
|
@@ -810,20 +753,6 @@ module Jinx
|
|
810
753
|
flts = @attr_flt_hash[obj.class] || return
|
811
754
|
flts[attribute]
|
812
755
|
end
|
813
|
-
|
814
|
-
# @param [Resource] obj the domain object to save in the database
|
815
|
-
# @return [Resource, nil] obj if the save is successful, nil otherwise
|
816
|
-
def save(obj, database)
|
817
|
-
if @create then
|
818
|
-
logger.debug { "Migrator creating #{obj}..." }
|
819
|
-
database.create(obj)
|
820
|
-
logger.debug { "Migrator created #{obj}." }
|
821
|
-
else
|
822
|
-
logger.debug { "Migrator saving #{obj}..." }
|
823
|
-
database.save(obj)
|
824
|
-
logger.debug { "Migrator saved #{obj}." }
|
825
|
-
end
|
826
|
-
end
|
827
756
|
|
828
757
|
def current_record
|
829
758
|
@rec_cnt + 1
|
@@ -878,7 +807,7 @@ module Jinx
|
|
878
807
|
begin
|
879
808
|
config = YAML.load_file(file)
|
880
809
|
rescue
|
881
|
-
|
810
|
+
raise MigrationError.new("Could not read field map file #{file}: " + $!)
|
882
811
|
end
|
883
812
|
populate_field_map(config, hash)
|
884
813
|
end
|
@@ -892,7 +821,7 @@ module Jinx
|
|
892
821
|
# the header accessor method for the field
|
893
822
|
header = @reader.accessor(field)
|
894
823
|
if header.nil? then
|
895
|
-
|
824
|
+
raise MigrationError.new("Field defined in migration configuration not found in input file #{@input} headers: #{field}")
|
896
825
|
end
|
897
826
|
# associate each attribute path in the property value with the header
|
898
827
|
attr_list.split(/,\s*/).each do |path_s|
|
@@ -921,7 +850,7 @@ module Jinx
|
|
921
850
|
begin
|
922
851
|
config = YAML::load_file(file)
|
923
852
|
rescue
|
924
|
-
|
853
|
+
raise MigrationError.new("Could not read defaults file #{file}: " + $!)
|
925
854
|
end
|
926
855
|
# collect the class => path => value entries
|
927
856
|
config.each do |path_s, value|
|
@@ -952,13 +881,15 @@ module Jinx
|
|
952
881
|
begin
|
953
882
|
config = YAML::load_file(file)
|
954
883
|
rescue
|
955
|
-
|
884
|
+
raise MigrationError.new("Could not read filter file #{file}: " + $!)
|
956
885
|
end
|
957
886
|
config.each do |path_s, flt|
|
958
887
|
next if flt.nil_or_empty?
|
959
888
|
klass, path = create_attribute_path(path_s)
|
960
|
-
|
961
|
-
|
889
|
+
if path.empty? then
|
890
|
+
raise MigrationError.new("Migration filter configuration path does not include a property: #{path_s}")
|
891
|
+
elsif path.size > 1 then
|
892
|
+
raise MigrationError.new("Migration filter configuration path with more than one property is not supported: #{path_s}")
|
962
893
|
end
|
963
894
|
pa = klass.standard_attribute(path.first.to_sym)
|
964
895
|
flt_hash = hash[klass] ||= {}
|
@@ -973,10 +904,10 @@ module Jinx
|
|
973
904
|
names = path_s.split('.')
|
974
905
|
# If the path starts with a capitalized class name, then resolve the class.
|
975
906
|
# Otherwise, the target class is the start of the path.
|
976
|
-
klass = names.first =~ /^[A-Z]/ ?
|
907
|
+
klass = names.first =~ /^[A-Z]/ ? class_for_name(names.shift) : @target_class
|
977
908
|
# There must be at least one attribute.
|
978
909
|
if names.empty? then
|
979
|
-
|
910
|
+
raise MigrationError.new("Property entry in migration configuration is not in <class>.<attribute> format: #{path_s}")
|
980
911
|
end
|
981
912
|
|
982
913
|
# Build the attribute path.
|
@@ -985,11 +916,11 @@ module Jinx
|
|
985
916
|
pa = name.to_sym
|
986
917
|
prop = begin
|
987
918
|
parent.property(pa)
|
988
|
-
rescue NameError
|
989
|
-
|
919
|
+
rescue NameError
|
920
|
+
raise MigrationError.new("Migration field mapping attribute #{parent}.#{pa} not found - " + $!)
|
990
921
|
end
|
991
922
|
if prop.collection? then
|
992
|
-
|
923
|
+
raise MigrationError.new("Migration field mapping attribute #{parent}.#{prop} is a collection, which is not supported")
|
993
924
|
end
|
994
925
|
path << prop
|
995
926
|
prop.type
|
@@ -1008,6 +939,13 @@ module Jinx
|
|
1008
939
|
def context_module
|
1009
940
|
@target_class.domain_module
|
1010
941
|
end
|
942
|
+
|
943
|
+
# @param [String] the class name to resolve in the context of this migrator
|
944
|
+
# @return [Class] the corresponding class
|
945
|
+
# @raise [NameError] if the name cannot be resolved
|
946
|
+
def class_for_name(name)
|
947
|
+
context_module.module_for_name(name)
|
948
|
+
end
|
1011
949
|
|
1012
950
|
# @return a new class => [paths] hash from the migration fields configuration map
|
1013
951
|
def create_class_paths_hash(fld_map, def_map)
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: jinx-migrate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 2.1.
|
5
|
+
version: 2.1.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- OHSU
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-
|
14
|
-
default_executable:
|
13
|
+
date: 2012-06-12 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: rack
|
@@ -81,7 +80,6 @@ files:
|
|
81
80
|
- .rspec
|
82
81
|
- .yardopts
|
83
82
|
- Gemfile
|
84
|
-
- Gemfile.lock
|
85
83
|
- History.md
|
86
84
|
- LEGAL
|
87
85
|
- LICENSE
|
@@ -141,7 +139,6 @@ files:
|
|
141
139
|
- test/fixtures/csv/data/empty.csv
|
142
140
|
- test/fixtures/csv/data/variety.csv
|
143
141
|
- test/lib/csv/csvio_test.rb
|
144
|
-
has_rdoc: yard
|
145
142
|
homepage: http://github.com/jinx/migrate
|
146
143
|
licenses:
|
147
144
|
- MIT
|
@@ -165,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
162
|
requirements: []
|
166
163
|
|
167
164
|
rubyforge_project: jinx
|
168
|
-
rubygems_version: 1.
|
165
|
+
rubygems_version: 1.8.15
|
169
166
|
signing_key:
|
170
167
|
specification_version: 3
|
171
168
|
summary: Jinx JSON plug-in.
|
@@ -204,3 +201,4 @@ test_files:
|
|
204
201
|
- spec/unique/parents.csv
|
205
202
|
- spec/unique/shims.rb
|
206
203
|
- spec/unique/unique_spec.rb
|
204
|
+
has_rdoc: yard
|
data/Gemfile.lock
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
jinx-migrate (2.1.1)
|
5
|
-
bundler
|
6
|
-
fastercsv
|
7
|
-
rack
|
8
|
-
|
9
|
-
PATH
|
10
|
-
remote: /Users/loneyf/workspace/jinx/core
|
11
|
-
specs:
|
12
|
-
jinx (2.1.1)
|
13
|
-
bundler
|
14
|
-
|
15
|
-
GEM
|
16
|
-
remote: http://rubygems.org/
|
17
|
-
specs:
|
18
|
-
diff-lcs (1.1.3)
|
19
|
-
fastercsv (1.5.4)
|
20
|
-
rack (1.4.1)
|
21
|
-
rake (0.9.2.2)
|
22
|
-
rspec (2.9.0)
|
23
|
-
rspec-core (~> 2.9.0)
|
24
|
-
rspec-expectations (~> 2.9.0)
|
25
|
-
rspec-mocks (~> 2.9.0)
|
26
|
-
rspec-core (2.9.0)
|
27
|
-
rspec-expectations (2.9.1)
|
28
|
-
diff-lcs (~> 1.1.3)
|
29
|
-
rspec-mocks (2.9.0)
|
30
|
-
|
31
|
-
PLATFORMS
|
32
|
-
java
|
33
|
-
|
34
|
-
DEPENDENCIES
|
35
|
-
jinx!
|
36
|
-
jinx-migrate!
|
37
|
-
rake
|
38
|
-
rspec (>= 2.6)
|