ar_loader 0.0.6 → 0.0.8
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/LICENSE +9 -9
- data/README.markdown +268 -221
- data/Rakefile +76 -76
- data/lib/VERSION +1 -1
- data/lib/ar_loader.rb +87 -66
- data/lib/ar_loader/exceptions.rb +2 -0
- data/lib/{engine → ar_loader}/file_definitions.rb +353 -353
- data/lib/{engine → ar_loader}/mapping_file_definitions.rb +87 -87
- data/lib/ar_loader/method_detail.rb +257 -0
- data/lib/ar_loader/method_mapper.rb +213 -0
- data/lib/helpers/jruby/jexcel_file.rb +187 -0
- data/lib/{engine → helpers/jruby}/word.rb +79 -70
- data/lib/helpers/spree_helper.rb +85 -0
- data/lib/loaders/csv_loader.rb +87 -0
- data/lib/loaders/excel_loader.rb +132 -0
- data/lib/loaders/loader_base.rb +205 -73
- data/lib/loaders/spree/image_loader.rb +45 -41
- data/lib/loaders/spree/product_loader.rb +140 -91
- data/lib/to_b.rb +24 -24
- data/spec/csv_loader_spec.rb +27 -0
- data/spec/database.yml +19 -6
- data/spec/db/migrate/20110803201325_create_test_bed.rb +78 -0
- data/spec/excel_loader_spec.rb +113 -98
- data/spec/fixtures/BadAssociationName.xls +0 -0
- data/spec/fixtures/DemoNegativeTesting.xls +0 -0
- data/spec/fixtures/DemoTestModelAssoc.xls +0 -0
- data/spec/fixtures/ProjectsMultiCategories.xls +0 -0
- data/spec/fixtures/SimpleProjects.xls +0 -0
- data/spec/fixtures/SpreeProducts.xls +0 -0
- data/spec/fixtures/SpreeZoneExample.csv +5 -0
- data/spec/fixtures/SpreeZoneExample.xls +0 -0
- data/spec/loader_spec.rb +116 -0
- data/spec/logs/test.log +5000 -0
- data/spec/method_mapper_spec.rb +222 -0
- data/spec/models.rb +55 -0
- data/spec/spec_helper.rb +85 -18
- data/spec/spree_loader_spec.rb +223 -157
- data/tasks/config/seed_fu_product_template.erb +15 -15
- data/tasks/config/tidy_config.txt +12 -12
- data/tasks/db_tasks.rake +64 -64
- data/tasks/excel_loader.rake +63 -113
- data/tasks/file_tasks.rake +36 -37
- data/tasks/loader.rake +45 -0
- data/tasks/spree/image_load.rake +108 -107
- data/tasks/spree/product_loader.rake +49 -107
- data/tasks/word_to_seedfu.rake +166 -166
- metadata +66 -61
- data/lib/engine/jruby/jexcel_file.rb +0 -182
- data/lib/engine/jruby/method_mapper_excel.rb +0 -44
- data/lib/engine/method_detail.rb +0 -140
- data/lib/engine/method_mapper.rb +0 -157
- data/lib/engine/method_mapper_csv.rb +0 -28
- data/spec/db/migrate/20110803201325_create_testbed.rb +0 -25
@@ -0,0 +1,187 @@
|
|
1
|
+
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
+
# Author :: Tom Statter
|
3
|
+
# Date :: Aug 2010
|
4
|
+
# License:: MIT
|
5
|
+
#
|
6
|
+
# An Excel file helper. Create and populate XSL files
|
7
|
+
#
|
8
|
+
# The maximum number of columns and rows in an Excel file is fixed at 256 Columns and 65536 Rows
|
9
|
+
#
|
10
|
+
# POI jar location needs to be added to class path.
|
11
|
+
#
|
12
|
+
# TODO - Check out http://poi.apache.org/poi-ruby.html
|
13
|
+
#
|
14
|
+
if(Guards::jruby?)
|
15
|
+
|
16
|
+
class Object
|
17
|
+
def add_to_classpath(path)
|
18
|
+
$CLASSPATH << File.join( ArLoader.root_path, 'lib', path.gsub("\\", "/") )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'java'
|
23
|
+
require 'rubygems'
|
24
|
+
|
25
|
+
add_to_classpath 'java/poi-3.6.jar'
|
26
|
+
|
27
|
+
class JExcelFile
|
28
|
+
include_class 'org.apache.poi.poifs.filesystem.POIFSFileSystem'
|
29
|
+
include_class 'org.apache.poi.hssf.usermodel.HSSFCell'
|
30
|
+
include_class 'org.apache.poi.hssf.usermodel.HSSFWorkbook'
|
31
|
+
include_class 'org.apache.poi.hssf.usermodel.HSSFCellStyle'
|
32
|
+
include_class 'org.apache.poi.hssf.usermodel.HSSFDataFormat'
|
33
|
+
|
34
|
+
include_class 'java.io.ByteArrayOutputStream'
|
35
|
+
include_class 'java.util.Date'
|
36
|
+
include_class 'java.io.FileInputStream'
|
37
|
+
|
38
|
+
attr_accessor :book, :row, :current_sheet
|
39
|
+
|
40
|
+
attr_reader :sheet
|
41
|
+
|
42
|
+
MAX_COLUMNS = 256.freeze
|
43
|
+
MAX_ROWS = 65536.freeze
|
44
|
+
|
45
|
+
# The HSSFWorkbook uses 0 based indexes
|
46
|
+
|
47
|
+
def initialize()
|
48
|
+
@book = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def open(filename)
|
52
|
+
inp = FileInputStream.new(filename)
|
53
|
+
|
54
|
+
@book = HSSFWorkbook.new(inp)
|
55
|
+
|
56
|
+
sheet(0) # also sets @current_sheet
|
57
|
+
end
|
58
|
+
|
59
|
+
def create(sheet_name)
|
60
|
+
@book = HSSFWorkbook.new()
|
61
|
+
@sheet = @book.createSheet(sheet_name.gsub(" ", ''))
|
62
|
+
date_style = @book.createCellStyle()
|
63
|
+
date_style.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"))
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return the current or specified HSSFSheet
|
67
|
+
def sheet(i = nil)
|
68
|
+
@current_sheet = i if i
|
69
|
+
@sheet = @book.getSheetAt(@current_sheet)
|
70
|
+
@sheet
|
71
|
+
end
|
72
|
+
|
73
|
+
def num_rows
|
74
|
+
@sheet.getPhysicalNumberOfRows
|
75
|
+
end
|
76
|
+
|
77
|
+
# Process each row. (type is org.apache.poi.hssf.usermodel.HSSFRow)
|
78
|
+
|
79
|
+
def each_row
|
80
|
+
@sheet.rowIterator.each { |row| yield row }
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Create new row, bring index in line with POI usage (our 1 is their 0)
|
85
|
+
def create_row(index)
|
86
|
+
@row = @sheet.createRow(index)
|
87
|
+
@row
|
88
|
+
end
|
89
|
+
|
90
|
+
def set_cell(row, column, data)
|
91
|
+
@row = @sheet.getRow(row) || create_row(row)
|
92
|
+
@row.createCell(column).setCellValue(data)
|
93
|
+
end
|
94
|
+
|
95
|
+
def value(row, column)
|
96
|
+
raise TypeError, "Expect row argument of type HSSFRow" unless row.is_a?(Java::OrgApachePoiHssfUsermodel::HSSFRow)
|
97
|
+
#puts "DEBUG - CELL VALUE : #{column} => #{ cell_value( row.getCell(column) ).inspect}"
|
98
|
+
cell_value( row.getCell(column) )
|
99
|
+
end
|
100
|
+
|
101
|
+
def cell_value(cell)
|
102
|
+
return nil unless cell
|
103
|
+
#puts "DEBUG CELL TYPE : #{cell} => #{cell.getCellType().inspect}"
|
104
|
+
case (cell.getCellType())
|
105
|
+
when HSSFCell::CELL_TYPE_FORMULA then return cell.getCellFormula()
|
106
|
+
when HSSFCell::CELL_TYPE_NUMERIC then return cell.getNumericCellValue()
|
107
|
+
when HSSFCell::CELL_TYPE_STRING then return cell.getStringCellValue()
|
108
|
+
when HSSFCell::CELL_TYPE_BOOLEAN then return cell.getBooleanCellValue()
|
109
|
+
when HSSFCell::CELL_TYPE_BLANK then return ""
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def save( filename )
|
114
|
+
File.open( filename, 'w') {|f| f.write(to_s) }
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# The internal representation of a Excel File
|
119
|
+
|
120
|
+
def to_s
|
121
|
+
outs = ByteArrayOutputStream.new
|
122
|
+
@book.write(outs);
|
123
|
+
outs.close();
|
124
|
+
String.from_java_bytes(outs.toByteArray)
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
module ExcelHelper
|
130
|
+
require 'java'
|
131
|
+
|
132
|
+
include_class 'org.apache.poi.poifs.filesystem.POIFSFileSystem'
|
133
|
+
include_class 'org.apache.poi.hssf.usermodel.HSSFCell'
|
134
|
+
include_class 'org.apache.poi.hssf.usermodel.HSSFWorkbook'
|
135
|
+
include_class 'org.apache.poi.hssf.usermodel.HSSFCellStyle'
|
136
|
+
include_class 'org.apache.poi.hssf.usermodel.HSSFDataFormat'
|
137
|
+
include_class 'java.io.ByteArrayOutputStream'
|
138
|
+
include_class 'java.util.Date'
|
139
|
+
|
140
|
+
# ActiveRecord Helper - Export model data to XLS file format
|
141
|
+
#
|
142
|
+
def to_xls(items=[])
|
143
|
+
|
144
|
+
@excel = ExcelFile.new(items[0].class.name)
|
145
|
+
|
146
|
+
@excel.create_row(0)
|
147
|
+
|
148
|
+
sheet = @excel.sheet
|
149
|
+
|
150
|
+
# header row
|
151
|
+
if !items.empty?
|
152
|
+
row = sheet.createRow(0)
|
153
|
+
cell_index = 0
|
154
|
+
items[0].class.columns.each do |column|
|
155
|
+
row.createCell(cell_index).setCellValue(column.name)
|
156
|
+
cell_index += 1
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# value rows
|
161
|
+
row_index = 1
|
162
|
+
items.each do |item|
|
163
|
+
row = sheet.createRow(row_index);
|
164
|
+
|
165
|
+
cell_index = 0
|
166
|
+
item.class.columns.each do |column|
|
167
|
+
cell = row.createCell(cell_index)
|
168
|
+
if column.sql_type =~ /date/ then
|
169
|
+
millis = item.send(column.name).to_f * 1000
|
170
|
+
cell.setCellValue(Date.new(millis))
|
171
|
+
cell.setCellStyle(dateStyle);
|
172
|
+
elsif column.sql_type =~ /int/ then
|
173
|
+
cell.setCellValue(item.send(column.name).to_i)
|
174
|
+
else
|
175
|
+
value = item.send(column.name)
|
176
|
+
cell.setCellValue(item.send(column.name)) unless value.nil?
|
177
|
+
end
|
178
|
+
cell_index += 1
|
179
|
+
end
|
180
|
+
row_index += 1
|
181
|
+
end
|
182
|
+
@excel.to_s
|
183
|
+
end
|
184
|
+
end
|
185
|
+
else
|
186
|
+
raise "Bad Platform - Sorry can only access Excel files via JRuby"
|
187
|
+
end
|
@@ -1,70 +1,79 @@
|
|
1
|
-
# Author:: Tom Statter
|
2
|
-
# License:: MIT ?
|
3
|
-
#
|
4
|
-
# NOTES ON INVESTIGATING OLE METHODS in irb
|
5
|
-
#
|
6
|
-
# visible = @word_app.ole_method_help( 'Visible' ) # Get a Method Object
|
7
|
-
|
8
|
-
# log( visible.return_type_detail.to_s ) # => ["BOOL"]
|
9
|
-
# log( visible.invoke_kind.to_s ) # => "PROPERTYGET"
|
10
|
-
# log( visible.params.to_s ) # => []
|
11
|
-
|
12
|
-
# @fc.ole_method_help( 'Report' ).params[1].ole_type_detail
|
13
|
-
#
|
14
|
-
# prefs = @word_app.Preferences.Strings.ole_method_help( 'Set' ).params
|
15
|
-
# => [index, newVal]
|
16
|
-
#
|
17
|
-
# WORD_OLE_CONST.constants
|
18
|
-
#
|
19
|
-
# WORD_OLE_CONST.constants.sort.grep /CR/
|
20
|
-
# => ["ClHideCRLF", "LesCR", "LesCRLF"]
|
21
|
-
#
|
22
|
-
# WORD_OLE_CONST.const_get( 'LesCR' ) or WORD_OLE_CONST::LesCR
|
23
|
-
# => 1
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
1
|
+
# Author:: Tom Statter
|
2
|
+
# License:: MIT ?
|
3
|
+
#
|
4
|
+
# NOTES ON INVESTIGATING OLE METHODS in irb
|
5
|
+
#
|
6
|
+
# visible = @word_app.ole_method_help( 'Visible' ) # Get a Method Object
|
7
|
+
|
8
|
+
# log( visible.return_type_detail.to_s ) # => ["BOOL"]
|
9
|
+
# log( visible.invoke_kind.to_s ) # => "PROPERTYGET"
|
10
|
+
# log( visible.params.to_s ) # => []
|
11
|
+
|
12
|
+
# @fc.ole_method_help( 'Report' ).params[1].ole_type_detail
|
13
|
+
#
|
14
|
+
# prefs = @word_app.Preferences.Strings.ole_method_help( 'Set' ).params
|
15
|
+
# => [index, newVal]
|
16
|
+
#
|
17
|
+
# WORD_OLE_CONST.constants
|
18
|
+
#
|
19
|
+
# WORD_OLE_CONST.constants.sort.grep /CR/
|
20
|
+
# => ["ClHideCRLF", "LesCR", "LesCRLF"]
|
21
|
+
#
|
22
|
+
# WORD_OLE_CONST.const_get( 'LesCR' ) or WORD_OLE_CONST::LesCR
|
23
|
+
# => 1
|
24
|
+
|
25
|
+
if(Guards::windows?)
|
26
|
+
|
27
|
+
require 'win32ole'
|
28
|
+
|
29
|
+
# Module for constants to be loaded int
|
30
|
+
|
31
|
+
module WORD_OLE_CONST
|
32
|
+
end
|
33
|
+
|
34
|
+
class Word
|
35
|
+
|
36
|
+
attr_reader :wd, :doc
|
37
|
+
|
38
|
+
def initialize( visible )
|
39
|
+
@wd = WIN32OLE.new('Word.Application')
|
40
|
+
|
41
|
+
WIN32OLE.const_load(@wd, WORD_OLE_CONST) if WORD_OLE_CONST.constants.empty?
|
42
|
+
|
43
|
+
@wd.Visible = visible
|
44
|
+
end
|
45
|
+
|
46
|
+
def open(file)
|
47
|
+
@doc = @wd.Documents.Open(file)
|
48
|
+
@doc
|
49
|
+
end
|
50
|
+
|
51
|
+
def save()
|
52
|
+
@doc.Save()
|
53
|
+
@doc
|
54
|
+
end
|
55
|
+
|
56
|
+
# Format : From WORD_OLE_CONST e.g WORD_OLE_CONST::WdFormatHTML
|
57
|
+
#
|
58
|
+
def save_as(name, format)
|
59
|
+
@doc.SaveAs(name, format)
|
60
|
+
return @doc
|
61
|
+
end
|
62
|
+
|
63
|
+
# WdFormatFilteredHTML
|
64
|
+
# WdFormatHTML
|
65
|
+
def save_as_html(name)
|
66
|
+
@doc.SaveAs(name, WORD_OLE_CONST::WdFormatHTML)
|
67
|
+
return @doc
|
68
|
+
end
|
69
|
+
|
70
|
+
def quit
|
71
|
+
@wd.quit()
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
else
|
76
|
+
|
77
|
+
class Word
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
+
# Author :: Tom Statter
|
3
|
+
# Date :: Aug 2011
|
4
|
+
# License:: MIT
|
5
|
+
#
|
6
|
+
# Details:: Spree Helper mixing in Support for testing or loading Rails Spree e-commerce.
|
7
|
+
#
|
8
|
+
# Since ar_loader gem is not a Rails app or a Spree App, provides utilities to internally
|
9
|
+
# create a Spree Database, and to load Spree components, enabling standalone testing.
|
10
|
+
#
|
11
|
+
module Spree
|
12
|
+
|
13
|
+
|
14
|
+
def self.root
|
15
|
+
Gem.loaded_specs['spree_core'] ? Gem.loaded_specs['spree_core'].full_gem_path : ""
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.lib_root
|
19
|
+
File.join(root, 'lib')
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.app_root
|
23
|
+
File.join(root, '/app')
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.load
|
27
|
+
gem 'rails'
|
28
|
+
|
29
|
+
gem 'spree'
|
30
|
+
require 'spree'
|
31
|
+
|
32
|
+
gem 'paperclip'
|
33
|
+
gem 'nested_set'
|
34
|
+
|
35
|
+
require 'nested_set'
|
36
|
+
require 'paperclip'
|
37
|
+
require 'acts_as_list'
|
38
|
+
|
39
|
+
CollectiveIdea::Acts::NestedSet::Railtie.extend_active_record
|
40
|
+
ActiveRecord::Base.send(:include, Paperclip::Glue)
|
41
|
+
|
42
|
+
gem 'activemerchant'
|
43
|
+
require 'active_merchant'
|
44
|
+
require 'active_merchant/billing/gateway'
|
45
|
+
|
46
|
+
ActiveRecord::Base.send(:include, ActiveMerchant::Billing)
|
47
|
+
|
48
|
+
$LOAD_PATH << lib_root << app_root << File.join(app_root, 'models')
|
49
|
+
|
50
|
+
load_models
|
51
|
+
|
52
|
+
Dir[lib_root + '/*.rb'].each do |r|
|
53
|
+
begin
|
54
|
+
require r if File.file?(r)
|
55
|
+
rescue => e
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
Dir[lib_root + '/**/*.rb'].each do |r|
|
60
|
+
begin
|
61
|
+
require r if File.file?(r) && ! r.include?('testing') && ! r.include?('generators')
|
62
|
+
rescue => e
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
load_models
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.load_models
|
70
|
+
puts 'load from', root
|
71
|
+
Dir[root + '/app/models/**/*.rb'].each {|r|
|
72
|
+
begin
|
73
|
+
require r if File.file?(r)
|
74
|
+
rescue => e
|
75
|
+
#puts 'failed to load', r, e.inspect
|
76
|
+
end
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.migrate_up
|
81
|
+
load
|
82
|
+
ActiveRecord::Migrator.up( File.join(root, 'db/migrate') )
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
+
# Author :: Tom Statter
|
3
|
+
# Date :: Aug 2011
|
4
|
+
# License:: MIT
|
5
|
+
#
|
6
|
+
# Details:: Specific loader to support CSV files.
|
7
|
+
#
|
8
|
+
#
|
9
|
+
require 'loaders/loader_base'
|
10
|
+
require 'ar_loader/exceptions'
|
11
|
+
require 'ar_loader/method_mapper'
|
12
|
+
|
13
|
+
module ARLoader
|
14
|
+
|
15
|
+
class CsvLoader < LoaderBase
|
16
|
+
|
17
|
+
def initialize(klass, object = nil, options = {})
|
18
|
+
super( klass, object, options )
|
19
|
+
raise "Cannot load - failed to create a #{klass}" unless @load_object
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def load(file_name, options = {})
|
24
|
+
|
25
|
+
require "csv"
|
26
|
+
|
27
|
+
# TODO - abstract out what a 'parsed file' is - so a common object can represent excel,csv etc
|
28
|
+
# then we can make load() more generic
|
29
|
+
|
30
|
+
@parsed_file = CSV.read(file_name)
|
31
|
+
|
32
|
+
|
33
|
+
@headers = @parsed_file.shift
|
34
|
+
|
35
|
+
@method_mapper = ARLoader::MethodMapper.new
|
36
|
+
|
37
|
+
# Convert the list of headers into suitable calls on the Active Record class
|
38
|
+
@method_mapper.populate_methods( load_object_class, @headers )
|
39
|
+
|
40
|
+
unless(@method_mapper.missing_methods.empty?)
|
41
|
+
puts "WARNING: Following column headings could not be mapped : #{@method_mapper.missing_methods.inspect}"
|
42
|
+
raise MappingDefinitionError, "ERROR: Missing mappings for #{@method_mapper.missing_methods.size} column headings"
|
43
|
+
end
|
44
|
+
|
45
|
+
#if(options[:verbose])
|
46
|
+
puts "\n\n\nLoading from CSV file: #{file_name}"
|
47
|
+
puts "Processing #{@parsed_file.size} rows"
|
48
|
+
# end
|
49
|
+
|
50
|
+
load_object_class.transaction do
|
51
|
+
@loaded_objects = []
|
52
|
+
|
53
|
+
@parsed_file.each do |row|
|
54
|
+
|
55
|
+
# TODO - Smart sorting of column processing order ....
|
56
|
+
# Does not currently ensure mandatory columns (for valid?) processed first but model needs saving
|
57
|
+
# before associations can be processed so user should ensure mandatory columns are prior to associations
|
58
|
+
|
59
|
+
# as part of this we also attempt to save early, for example before assigning to
|
60
|
+
# has_and_belongs_to associations which require the load_object has an id for the join table
|
61
|
+
|
62
|
+
# Iterate over the columns method_mapper found in Excel,
|
63
|
+
# pulling data out of associated column
|
64
|
+
@method_mapper.method_details.each_with_index do |method_detail, col|
|
65
|
+
|
66
|
+
value = row[col]
|
67
|
+
|
68
|
+
process(method_detail, value)
|
69
|
+
end
|
70
|
+
|
71
|
+
# TODO - handle when it's not valid ?
|
72
|
+
# Process rest and dump out an exception list of Products ??
|
73
|
+
|
74
|
+
puts "SAVING ROW #{row} : #{load_object.inspect}" #if options[:verbose]
|
75
|
+
|
76
|
+
save
|
77
|
+
|
78
|
+
# don't forget to reset the object or we'll update rather than create
|
79
|
+
new_load_object
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|