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.
Files changed (53) hide show
  1. data/LICENSE +9 -9
  2. data/README.markdown +268 -221
  3. data/Rakefile +76 -76
  4. data/lib/VERSION +1 -1
  5. data/lib/ar_loader.rb +87 -66
  6. data/lib/ar_loader/exceptions.rb +2 -0
  7. data/lib/{engine → ar_loader}/file_definitions.rb +353 -353
  8. data/lib/{engine → ar_loader}/mapping_file_definitions.rb +87 -87
  9. data/lib/ar_loader/method_detail.rb +257 -0
  10. data/lib/ar_loader/method_mapper.rb +213 -0
  11. data/lib/helpers/jruby/jexcel_file.rb +187 -0
  12. data/lib/{engine → helpers/jruby}/word.rb +79 -70
  13. data/lib/helpers/spree_helper.rb +85 -0
  14. data/lib/loaders/csv_loader.rb +87 -0
  15. data/lib/loaders/excel_loader.rb +132 -0
  16. data/lib/loaders/loader_base.rb +205 -73
  17. data/lib/loaders/spree/image_loader.rb +45 -41
  18. data/lib/loaders/spree/product_loader.rb +140 -91
  19. data/lib/to_b.rb +24 -24
  20. data/spec/csv_loader_spec.rb +27 -0
  21. data/spec/database.yml +19 -6
  22. data/spec/db/migrate/20110803201325_create_test_bed.rb +78 -0
  23. data/spec/excel_loader_spec.rb +113 -98
  24. data/spec/fixtures/BadAssociationName.xls +0 -0
  25. data/spec/fixtures/DemoNegativeTesting.xls +0 -0
  26. data/spec/fixtures/DemoTestModelAssoc.xls +0 -0
  27. data/spec/fixtures/ProjectsMultiCategories.xls +0 -0
  28. data/spec/fixtures/SimpleProjects.xls +0 -0
  29. data/spec/fixtures/SpreeProducts.xls +0 -0
  30. data/spec/fixtures/SpreeZoneExample.csv +5 -0
  31. data/spec/fixtures/SpreeZoneExample.xls +0 -0
  32. data/spec/loader_spec.rb +116 -0
  33. data/spec/logs/test.log +5000 -0
  34. data/spec/method_mapper_spec.rb +222 -0
  35. data/spec/models.rb +55 -0
  36. data/spec/spec_helper.rb +85 -18
  37. data/spec/spree_loader_spec.rb +223 -157
  38. data/tasks/config/seed_fu_product_template.erb +15 -15
  39. data/tasks/config/tidy_config.txt +12 -12
  40. data/tasks/db_tasks.rake +64 -64
  41. data/tasks/excel_loader.rake +63 -113
  42. data/tasks/file_tasks.rake +36 -37
  43. data/tasks/loader.rake +45 -0
  44. data/tasks/spree/image_load.rake +108 -107
  45. data/tasks/spree/product_loader.rake +49 -107
  46. data/tasks/word_to_seedfu.rake +166 -166
  47. metadata +66 -61
  48. data/lib/engine/jruby/jexcel_file.rb +0 -182
  49. data/lib/engine/jruby/method_mapper_excel.rb +0 -44
  50. data/lib/engine/method_detail.rb +0 -140
  51. data/lib/engine/method_mapper.rb +0 -157
  52. data/lib/engine/method_mapper_csv.rb +0 -28
  53. 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
- require 'win32ole'
25
-
26
- # Module for constants to be loaded int
27
-
28
- module WORD_OLE_CONST
29
- end
30
-
31
- class Word
32
-
33
- attr_reader :wd, :doc
34
-
35
- def initialize( visible )
36
- @wd = WIN32OLE.new('Word.Application')
37
-
38
- WIN32OLE.const_load(@wd, WORD_OLE_CONST) if WORD_OLE_CONST.constants.empty?
39
-
40
- @wd.Visible = visible
41
- end
42
-
43
- def open(file)
44
- @doc = @wd.Documents.Open(file)
45
- @doc
46
- end
47
-
48
- def save()
49
- @doc.Save()
50
- @doc
51
- end
52
-
53
- # Format : From WORD_OLE_CONST e.g WORD_OLE_CONST::WdFormatHTML
54
- #
55
- def save_as(name, format)
56
- @doc.SaveAs(name, format)
57
- return @doc
58
- end
59
-
60
- # WdFormatFilteredHTML
61
- # WdFormatHTML
62
- def save_as_html(name)
63
- @doc.SaveAs(name, WORD_OLE_CONST::WdFormatHTML)
64
- return @doc
65
- end
66
-
67
- def quit
68
- @wd.quit()
69
- end
70
- end
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