datashift 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -0
- data/VERSION +1 -1
- data/datashift.gemspec +6 -4
- data/lib/datashift/method_detail.rb +2 -2
- data/lib/datashift/method_dictionary.rb +15 -2
- data/lib/datashift/method_mapper.rb +18 -5
- data/lib/exporters/excel_exporter.rb +1 -2
- data/lib/helpers/spree_helper.rb +18 -12
- data/lib/loaders/csv_loader.rb +1 -8
- data/lib/loaders/excel_loader.rb +7 -7
- data/lib/loaders/loader_base.rb +34 -6
- data/lib/loaders/spreadsheet_loader.rb +86 -78
- data/lib/loaders/spree/image_loader.rb +72 -24
- data/lib/loaders/spree/product_loader.rb +3 -31
- data/lib/thor/import_excel.thor +15 -16
- data/lib/thor/spree/products_images.thor +5 -3
- data/sandbox/config/application.rb +21 -5
- data/sandbox/config/database.yml +18 -32
- data/sandbox/config/environment.rb +1 -3
- data/sandbox/config/environments/development.rb +9 -2
- data/spec/Gemfile +16 -5
- data/spec/fixtures/datashift_Spree_db.sqlite +0 -0
- data/spec/fixtures/datashift_test_models_db.sqlite +0 -0
- data/spec/fixtures/spree/{SpreeProductImages.xls → SpreeImages.xls} +0 -0
- data/spec/fixtures/spree/SpreeProductsWithImages.csv +3 -3
- data/spec/fixtures/spree/SpreeProductsWithImages.xls +0 -0
- data/spec/fixtures/test_model_defs.rb +1 -0
- data/spec/spec_helper.rb +13 -11
- data/spec/spree_exporter_spec.rb +6 -4
- data/spec/spree_generator_spec.rb +14 -10
- data/spec/spree_images_loader_spec.rb +59 -24
- data/spec/spree_loader_spec.rb +12 -10
- data/spec/spree_method_mapping_spec.rb +16 -9
- data/tasks/spree/image_load.rake +2 -8
- metadata +15 -6
data/Rakefile
CHANGED
@@ -38,6 +38,9 @@ Jeweler::Tasks.new do |gem|
|
|
38
38
|
gem.email = "rubygems@autotelik.co.uk"
|
39
39
|
gem.authors = ["Thomas Statter"]
|
40
40
|
# dependencies defined in Gemfile
|
41
|
+
gem.files.exclude ['sandbox']
|
42
|
+
|
43
|
+
gem.add_dependency ['spreadsheet']
|
41
44
|
end
|
42
45
|
Jeweler::RubygemsDotOrgTasks.new
|
43
46
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/datashift.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "datashift"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Thomas Statter"]
|
12
|
-
s.date = "2012-05-
|
12
|
+
s.date = "2012-05-18"
|
13
13
|
s.description = "A suite of tools to move data between ActiveRecord models,databases,applications like Excel/Open Office, files and projects including Spree"
|
14
14
|
s.email = "rubygems@autotelik.co.uk"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -24,7 +24,6 @@ Gem::Specification.new do |s|
|
|
24
24
|
"README.rdoc",
|
25
25
|
"Rakefile",
|
26
26
|
"VERSION",
|
27
|
-
"datashift-0.5.0.gem",
|
28
27
|
"datashift.gemspec",
|
29
28
|
"lib/applications/jruby/jexcel_file.rb",
|
30
29
|
"lib/applications/jruby/word.rb",
|
@@ -122,7 +121,7 @@ Gem::Specification.new do |s|
|
|
122
121
|
"spec/fixtures/negative/SpreeProdMissManyMandatory.xls",
|
123
122
|
"spec/fixtures/simple_export_spec.xls",
|
124
123
|
"spec/fixtures/simple_template_spec.xls",
|
125
|
-
"spec/fixtures/spree/
|
124
|
+
"spec/fixtures/spree/SpreeImages.xls",
|
126
125
|
"spec/fixtures/spree/SpreeProducts.csv",
|
127
126
|
"spec/fixtures/spree/SpreeProducts.xls",
|
128
127
|
"spec/fixtures/spree/SpreeProductsDefaults.yml",
|
@@ -167,9 +166,12 @@ Gem::Specification.new do |s|
|
|
167
166
|
s.specification_version = 3
|
168
167
|
|
169
168
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
169
|
+
s.add_runtime_dependency(%q<spreadsheet>, [">= 0"])
|
170
170
|
else
|
171
|
+
s.add_dependency(%q<spreadsheet>, [">= 0"])
|
171
172
|
end
|
172
173
|
else
|
174
|
+
s.add_dependency(%q<spreadsheet>, [">= 0"])
|
173
175
|
end
|
174
176
|
end
|
175
177
|
|
@@ -72,8 +72,8 @@ module DataShift
|
|
72
72
|
nil
|
73
73
|
end
|
74
74
|
|
75
|
-
def operator?(name)
|
76
|
-
operator == name
|
75
|
+
def operator?(name, case_sensitive = false)
|
76
|
+
case_sensitive ? operator == name : operator.downcase == name.downcase
|
77
77
|
end
|
78
78
|
|
79
79
|
# Return the operator's expected class name, if can be derived, else nil
|
@@ -59,6 +59,7 @@ module DataShift
|
|
59
59
|
if(options[:instance_methods] == true)
|
60
60
|
setters = klass.instance_methods.grep(/\w+=/).collect {|x| x.to_s }
|
61
61
|
|
62
|
+
# TODO - Since 3.2 this seems to return lots more stuff including validations which might not be appropriate
|
62
63
|
if(klass.respond_to? :defined_activerecord_methods)
|
63
64
|
setters = setters - klass.defined_activerecord_methods.to_a
|
64
65
|
end
|
@@ -81,6 +82,13 @@ module DataShift
|
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
85
|
+
|
86
|
+
def self.add( klass, operator, type = :assignment)
|
87
|
+
method_details_mgr = get_method_details_mgr( klass )
|
88
|
+
md = MethodDetail.new(operator, klass, operator, type)
|
89
|
+
method_details_mgr << md
|
90
|
+
return md
|
91
|
+
end
|
84
92
|
|
85
93
|
# Build a thorough and usable picture of the operators by building dictionary of our MethodDetail
|
86
94
|
# objects which can be used to import/export data to objects of type 'klass'
|
@@ -115,7 +123,7 @@ module DataShift
|
|
115
123
|
#
|
116
124
|
def self.find_method_detail( klass, external_name )
|
117
125
|
|
118
|
-
|
126
|
+
method_details_mgr = get_method_details_mgr( klass )
|
119
127
|
|
120
128
|
# md_mgr.all_available_operators.each { |l| puts "DEBUG: Mapped Method : #{l.inspect}" }
|
121
129
|
|
@@ -136,7 +144,7 @@ module DataShift
|
|
136
144
|
# Try each association type, returning first that contains matching operator with name n
|
137
145
|
|
138
146
|
MethodDetail::supported_types_enum.each do |t|
|
139
|
-
method_detail =
|
147
|
+
method_detail = method_details_mgr.find(n, t)
|
140
148
|
return method_detail if(method_detail)
|
141
149
|
end
|
142
150
|
|
@@ -158,6 +166,11 @@ module DataShift
|
|
158
166
|
"#{klass.name}:#{column}"
|
159
167
|
end
|
160
168
|
|
169
|
+
def self.get_method_details_mgr( klass )
|
170
|
+
method_details_mgrs[klass] || MethodDetailsManager.new( klass )
|
171
|
+
end
|
172
|
+
|
173
|
+
|
161
174
|
def self.method_details_mgrs
|
162
175
|
@method_details_mgrs ||= {}
|
163
176
|
@method_details_mgrs
|
@@ -50,7 +50,10 @@ module DataShift
|
|
50
50
|
# specified may not be exactly as required e.g handles capitalisation, white space, _ etc
|
51
51
|
# Returns: Array of matching method_details
|
52
52
|
#
|
53
|
-
def map_inbound_to_methods( klass, columns )
|
53
|
+
def map_inbound_to_methods( klass, columns, options = {} )
|
54
|
+
|
55
|
+
forced = [*options[:force_inclusion]].compact
|
56
|
+
forced.collect! { |f| f.downcase }
|
54
57
|
|
55
58
|
@method_details, @missing_methods = [], []
|
56
59
|
|
@@ -60,16 +63,26 @@ module DataShift
|
|
60
63
|
next
|
61
64
|
end
|
62
65
|
|
63
|
-
|
66
|
+
operator, lookup = name.split(MethodMapper::column_delim)
|
64
67
|
#puts "DEBUG: Find Method Detail for #{x}"
|
65
|
-
md = MethodDictionary::find_method_detail( klass,
|
68
|
+
md = MethodDictionary::find_method_detail( klass, operator )
|
66
69
|
|
67
70
|
# TODO be nice if we could cheeck that the assoc on klass responds to the specified
|
68
71
|
# lookup key now (nice n early)
|
69
72
|
# active_record_helper = "find_by_#{lookup}"
|
73
|
+
if(md.nil? && forced.include?(operator.downcase))
|
74
|
+
md = MethodDictionary::add(klass, operator)
|
75
|
+
end
|
76
|
+
|
77
|
+
if(md)
|
78
|
+
|
79
|
+
md.find_by_operator = lookup if(lookup) # TODO and klass.x.respond_to?(active_record_helper))
|
80
|
+
|
81
|
+
@method_details << md
|
82
|
+
else
|
83
|
+
@missing_methods << operator
|
84
|
+
end
|
70
85
|
|
71
|
-
md.find_by_operator = lookup if(lookup) # TODO and klass.x.respond_to?(active_record_helper))
|
72
|
-
md ? @method_details << md : @missing_methods << x
|
73
86
|
end
|
74
87
|
#@method_details.compact! .. currently we may need to map via the index on @method_details so don't remove nils for now
|
75
88
|
@method_details
|
@@ -104,10 +104,9 @@ module DataShift
|
|
104
104
|
end # ExcelGenerator
|
105
105
|
|
106
106
|
else
|
107
|
-
class ExcelExporter <
|
107
|
+
class ExcelExporter < ExporterBase
|
108
108
|
|
109
109
|
def initialize(filename)
|
110
|
-
@filename = filename
|
111
110
|
raise DataShift::BadRuby, "Apologies but DataShift Excel facilities currently need JRuby. Please switch to, or install JRuby"
|
112
111
|
end
|
113
112
|
end
|
data/lib/helpers/spree_helper.rb
CHANGED
@@ -80,9 +80,7 @@ module DataShift
|
|
80
80
|
# => Will chdir into the sandbox to load environment as need to mimic being at root of a rails project
|
81
81
|
# chdir back after environment loaded
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
def self.boot( database_env)#, rails_version = '3.1.3' )
|
83
|
+
def self.boot( database_env)
|
86
84
|
|
87
85
|
if( ! is_namespace_version )
|
88
86
|
db_connect( database_env )
|
@@ -90,11 +88,8 @@ module DataShift
|
|
90
88
|
boot_pre_1
|
91
89
|
@dslog.info "Booted Spree using pre 1.0.0 version"
|
92
90
|
else
|
93
|
-
|
94
|
-
|
95
|
-
#em('rails', rails_version)
|
96
|
-
|
97
|
-
db_connect( database_env )#, rails_version )
|
91
|
+
|
92
|
+
db_connect( database_env )
|
98
93
|
|
99
94
|
@dslog.info "Booting Spree using version #{SpreeHelper::version}"
|
100
95
|
|
@@ -102,13 +97,24 @@ module DataShift
|
|
102
97
|
|
103
98
|
store_path = Dir.pwd
|
104
99
|
|
105
|
-
|
106
|
-
|
107
|
-
|
100
|
+
spree_sanbox_app = File.expand_path('../../../sandbox', __FILE__)
|
101
|
+
|
102
|
+
unless(File.exists?(spree_sanbox_app))
|
103
|
+
Dir.chdir( File.expand_path( "#{spree_sanbox_app}/..") )
|
104
|
+
system('rails new sandbox')
|
105
|
+
end
|
106
|
+
|
107
|
+
rails_root = spree_sanbox_app
|
108
108
|
|
109
109
|
$:.unshift rails_root
|
110
110
|
|
111
|
-
|
111
|
+
begin
|
112
|
+
require 'config/environment.rb'
|
113
|
+
rescue => e
|
114
|
+
#somethign in deface seems to blow up suddenly on 1.1
|
115
|
+
# puts e.backtrace
|
116
|
+
puts "Warning - Potential issue initializing Spree sanbox #{e.inspect}"
|
117
|
+
end
|
112
118
|
|
113
119
|
Dir.chdir( store_path )
|
114
120
|
|
data/lib/loaders/csv_loader.rb
CHANGED
@@ -28,16 +28,9 @@ module DataShift
|
|
28
28
|
|
29
29
|
@parsed_file = CSV.read(file_name)
|
30
30
|
|
31
|
-
@mandatory = options[:mandatory] || []
|
32
|
-
|
33
31
|
# Create a method_mapper which maps list of headers into suitable calls on the Active Record class
|
34
32
|
# For example if model has an attribute 'price' will map columns called Price, price, PRICE etc to this attribute
|
35
|
-
map_headers_to_operators( @parsed_file.shift, options
|
36
|
-
|
37
|
-
unless(@method_mapper.missing_methods.empty?)
|
38
|
-
logger.error("Following column headings could not be mapped :\n#{@method_mapper.missing_methods.inspect}")
|
39
|
-
raise MappingDefinitionError, "ERROR: Missing mappings for #{@method_mapper.missing_methods.size} column headings"
|
40
|
-
end
|
33
|
+
map_headers_to_operators( @parsed_file.shift, options)
|
41
34
|
|
42
35
|
#if(options[:verbose])
|
43
36
|
puts "\n\n\nLoading from CSV file: #{file_name}"
|
data/lib/loaders/excel_loader.rb
CHANGED
@@ -25,14 +25,14 @@ module DataShift
|
|
25
25
|
module ExcelLoading
|
26
26
|
|
27
27
|
# Options:
|
28
|
-
# [:
|
29
|
-
# [:
|
30
|
-
# [:
|
31
|
-
# [:
|
28
|
+
# [:sheet_number] : Default is 0. The index of the Excel Worksheet to use.
|
29
|
+
# [:header_row] : Default is 0. Use alternative row as header definition.
|
30
|
+
# [:mandatory] : Array of mandatory column names
|
31
|
+
# [:force_inclusion] : Array of inbound column names to force into mapping
|
32
|
+
# [:strict] : Raise exception when no mapping found for a column heading (non mandatory)
|
32
33
|
|
33
|
-
def perform_excel_load( file_name, options = {} )
|
34
34
|
|
35
|
-
|
35
|
+
def perform_excel_load( file_name, options = {} )
|
36
36
|
|
37
37
|
@excel = JExcelFile.new
|
38
38
|
|
@@ -64,7 +64,7 @@ module DataShift
|
|
64
64
|
|
65
65
|
# Create a method_mapper which maps list of headers into suitable calls on the Active Record class
|
66
66
|
# For example if model has an attribute 'price' will map columns called Price, price, PRICE etc to this attribute
|
67
|
-
map_headers_to_operators( @headers, options
|
67
|
+
map_headers_to_operators( @headers, options )
|
68
68
|
|
69
69
|
logger.info "Excel Loader prcoessing #{@excel.num_rows} rows"
|
70
70
|
load_object_class.transaction do
|
data/lib/loaders/loader_base.rb
CHANGED
@@ -120,8 +120,15 @@ module DataShift
|
|
120
120
|
# CSV files
|
121
121
|
#
|
122
122
|
# OPTIONS :
|
123
|
-
#
|
124
|
-
|
123
|
+
#
|
124
|
+
# strict : Raise an exception of any headers can't be mapped to an attribute/association
|
125
|
+
# ignore : List of column headers to ignore when building operator map
|
126
|
+
# mandatory : List of columns that must be present in headers
|
127
|
+
#
|
128
|
+
# force_inclusion : List of columns that do not map to any operator but should be includeed in processing.
|
129
|
+
# This provides the opportunity for loaders to provide specific methods to handle these fields
|
130
|
+
# when no direct operator is available on the modle or it's associations
|
131
|
+
#
|
125
132
|
def perform_load( file_name, options = {} )
|
126
133
|
|
127
134
|
raise DataShift::BadFile, "Cannot load #{file_name} file not found." unless(File.exists?(file_name))
|
@@ -139,21 +146,33 @@ module DataShift
|
|
139
146
|
end
|
140
147
|
|
141
148
|
|
149
|
+
|
142
150
|
# Core API - Given a list of free text column names from a file,
|
143
151
|
# map all headers to a method detail containing operator details.
|
144
152
|
#
|
145
153
|
# This is then available through @method_mapper.method_details.each
|
146
154
|
#
|
147
155
|
# Options:
|
148
|
-
# strict
|
156
|
+
# strict : Raise an exception of any headers can't be mapped to an attribute/association
|
157
|
+
# ignore : List of column headers to ignore when building operator map
|
158
|
+
# mandatory : List of columns that must be present in headers
|
159
|
+
#
|
160
|
+
# force_inclusion : List of columns that do not map to any operator but should be includeed in processing.
|
161
|
+
# This provides the opportunity for loaders to provide specific methods to handle these fields
|
162
|
+
# when no direct operator is available on the modle or it's associations
|
149
163
|
#
|
150
|
-
def map_headers_to_operators( headers,
|
164
|
+
def map_headers_to_operators( headers, options = {} )
|
151
165
|
@headers = headers
|
152
166
|
|
167
|
+
mandatory = options[:mandatory] || []
|
168
|
+
|
169
|
+
|
170
|
+
strict = (options[:strict] == true)
|
171
|
+
|
153
172
|
begin
|
154
|
-
|
173
|
+
@method_mapper.map_inbound_to_methods( load_object_class, @headers, options )
|
155
174
|
rescue => e
|
156
|
-
puts e.inspect
|
175
|
+
puts e.inspect, e.backtrace
|
157
176
|
logger.error("Failed to map header row to set of database operators : #{e.inspect}")
|
158
177
|
raise MappingDefinitionError, "Failed to map header row to set of database operators"
|
159
178
|
end
|
@@ -443,6 +462,15 @@ module DataShift
|
|
443
462
|
end
|
444
463
|
end
|
445
464
|
|
465
|
+
protected
|
466
|
+
|
467
|
+
# Take current column data and split into each association
|
468
|
+
# Supported Syntax :
|
469
|
+
# assoc_find_name:value | assoc2_find_name:value | etc
|
470
|
+
def get_each_assoc
|
471
|
+
current_value.to_s.split( LoaderBase::multi_assoc_delim )
|
472
|
+
end
|
473
|
+
|
446
474
|
private
|
447
475
|
|
448
476
|
def save_if_new
|
@@ -12,126 +12,134 @@
|
|
12
12
|
# i.e pulls data from each column and sends to object.
|
13
13
|
#
|
14
14
|
require 'datashift/exceptions'
|
15
|
-
|
15
|
+
|
16
16
|
module DataShift
|
17
|
+
|
18
|
+
unless(Guards::jruby?)
|
19
|
+
|
20
|
+
require 'loaders/loader_base'
|
17
21
|
|
18
|
-
|
22
|
+
module SpreadsheetLoading
|
19
23
|
|
20
|
-
|
24
|
+
gem 'spreadsheet'
|
25
|
+
require 'spreadsheet'
|
21
26
|
|
22
|
-
# Options:
|
23
|
-
# [:header_row] : Default is 0. Use alternative row as header definition.
|
24
|
-
# [:mandatory] : Array of mandatory column names
|
25
|
-
# [:strict] : Raise exception when no mapping found for a column heading (non mandatory)
|
26
|
-
# [:sheet_number]
|
27
27
|
|
28
|
-
|
28
|
+
# Spreadsheet.client_encoding = 'UTF-8'F
|
29
|
+
|
30
|
+
# Options:
|
31
|
+
# [:header_row] : Default is 0. Use alternative row as header definition.
|
32
|
+
# [:mandatory] : Array of mandatory column names
|
33
|
+
# [:strict] : Raise exception when no mapping found for a column heading (non mandatory)
|
34
|
+
# [:sheet_number]
|
29
35
|
|
30
|
-
|
36
|
+
def perform_spreadsheet_load( file_name, options = {} )
|
31
37
|
|
32
|
-
|
38
|
+
@mandatory = options[:mandatory] || []
|
33
39
|
|
34
|
-
|
40
|
+
@excel = Spreadsheet.open file_name
|
35
41
|
|
36
|
-
|
37
|
-
|
42
|
+
#if(options[:verbose])
|
43
|
+
puts "\n\n\nLoading from Excel file: #{file_name}"
|
38
44
|
|
39
|
-
|
45
|
+
sheet_number = options[:sheet_number] || 0
|
40
46
|
|
41
|
-
|
47
|
+
@sheet = @excel.sheet( sheet_number )
|
42
48
|
|
43
|
-
|
44
|
-
|
49
|
+
header_row_index = options[:header_row] || 0
|
50
|
+
@header_row = @sheet.getRow(header_row_index)
|
45
51
|
|
46
|
-
|
52
|
+
raise MissingHeadersError, "No headers found - Check Sheet #{@sheet} is complete and Row #{header_row_index} contains headers" unless(@header_row)
|
47
53
|
|
48
|
-
|
54
|
+
@headers = []
|
49
55
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
56
|
+
(0..JExcelFile::MAX_COLUMNS).each do |i|
|
57
|
+
cell = @header_row.getCell(i)
|
58
|
+
break unless cell
|
59
|
+
header = "#{@excel.cell_value(cell).to_s}".strip
|
60
|
+
break if header.empty?
|
61
|
+
@headers << header
|
62
|
+
end
|
57
63
|
|
58
|
-
|
64
|
+
raise MissingHeadersError, "No headers found - Check Sheet #{@sheet} is complete and Row #{header_row_index} contains headers" if(@headers.empty?)
|
59
65
|
|
60
|
-
|
61
|
-
|
66
|
+
# Create a method_mapper which maps list of headers into suitable calls on the Active Record class
|
67
|
+
map_headers_to_operators( @headers, options)
|
62
68
|
|
63
|
-
|
64
|
-
|
69
|
+
load_object_class.transaction do
|
70
|
+
@loaded_objects = []
|
65
71
|
|
66
|
-
|
72
|
+
(1..@excel.num_rows).collect do |row|
|
67
73
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
+
# Excel num_rows seems to return all 'visible' rows, which appears to be greater than the actual data rows
|
75
|
+
# (TODO - write spec to process .xls with a huge number of rows)
|
76
|
+
#
|
77
|
+
# This is rubbish but currently manually detect when actual data ends, this isn't very smart but
|
78
|
+
# got no better idea than ending once we hit the first completely empty row
|
79
|
+
break if @excel.sheet.getRow(row).nil?
|
74
80
|
|
75
|
-
|
81
|
+
contains_data = false
|
76
82
|
|
77
|
-
|
78
|
-
|
79
|
-
|
83
|
+
# TODO - Smart sorting of column processing order ....
|
84
|
+
# Does not currently ensure mandatory columns (for valid?) processed first but model needs saving
|
85
|
+
# before associations can be processed so user should ensure mandatory columns are prior to associations
|
80
86
|
|
81
|
-
|
82
|
-
|
87
|
+
# as part of this we also attempt to save early, for example before assigning to
|
88
|
+
# has_and_belongs_to associations which require the load_object has an id for the join table
|
83
89
|
|
84
|
-
|
85
|
-
|
86
|
-
|
90
|
+
# Iterate over the columns method_mapper found in Excel,
|
91
|
+
# pulling data out of associated column
|
92
|
+
@method_mapper.method_details.each_with_index do |method_detail, col|
|
87
93
|
|
88
|
-
|
94
|
+
value = value_at(row, col)
|
89
95
|
|
90
|
-
|
96
|
+
contains_data = true unless(value.nil? || value.to_s.empty?)
|
91
97
|
|
92
|
-
|
93
|
-
|
98
|
+
#puts "DEBUG: Excel process METHOD :#{method_detail.inspect}", value.inspect
|
99
|
+
prepare_data(method_detail, value)
|
94
100
|
|
95
|
-
|
96
|
-
|
101
|
+
process()
|
102
|
+
end
|
97
103
|
|
98
|
-
|
104
|
+
break unless(contains_data == true)
|
99
105
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
106
|
+
# TODO - requirements to handle not valid ?
|
107
|
+
# all or nothing or carry on and dump out the exception list at end
|
108
|
+
#puts "DEBUG: FINAL SAVE #{load_object.inspect}"
|
109
|
+
save
|
110
|
+
#puts "DEBUG: SAVED #{load_object.inspect}"
|
105
111
|
|
106
|
-
|
107
|
-
|
112
|
+
# don't forget to reset the object or we'll update rather than create
|
113
|
+
new_load_object
|
108
114
|
|
115
|
+
end
|
109
116
|
end
|
117
|
+
puts "Spreadsheet loading stage complete - #{loaded_objects.size} rows added."
|
110
118
|
end
|
111
|
-
puts "Spreadsheet loading stage complete - #{loaded_objects.size} rows added."
|
112
|
-
end
|
113
119
|
|
114
|
-
|
115
|
-
|
120
|
+
def value_at(row, column)
|
121
|
+
@excel.get_cell_value( @excel.sheet.getRow(row), column)
|
122
|
+
end
|
116
123
|
end
|
117
|
-
end
|
118
124
|
|
119
125
|
|
120
|
-
|
126
|
+
class SpreadsheetLoader < LoaderBase
|
121
127
|
|
122
|
-
|
128
|
+
include SpreadsheetLoading
|
123
129
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
130
|
+
def initialize(klass, object = nil, options = {})
|
131
|
+
super( klass, object, options )
|
132
|
+
raise "Cannot load - failed to create a #{klass}" unless @load_object
|
133
|
+
end
|
128
134
|
|
129
|
-
|
130
|
-
|
135
|
+
def perform_load( file_name, options = {} )
|
136
|
+
perform_spreadsheet_load( file_name, options )
|
131
137
|
|
132
|
-
|
133
|
-
|
138
|
+
puts "Spreadsheet loading stage complete - #{loaded_objects.size} rows added."
|
139
|
+
end
|
134
140
|
|
135
|
-
|
141
|
+
end
|
136
142
|
|
143
|
+
end
|
144
|
+
|
137
145
|
end
|