ar_loader 0.0.6 → 0.0.8

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