datashift 0.9.0 → 0.10.0

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 (163) hide show
  1. data/README.markdown +63 -64
  2. data/Rakefile +4 -7
  3. data/VERSION +1 -1
  4. data/datashift.gemspec +92 -62
  5. data/lib/applications/apache_poi_extensions.rb +62 -0
  6. data/lib/applications/excel.rb +78 -0
  7. data/lib/applications/excel_base.rb +65 -0
  8. data/lib/applications/jexcel_file.rb +222 -0
  9. data/lib/applications/jexcel_file_extensions.rb +244 -0
  10. data/lib/applications/jruby/{jexcel_file.rb → old_pre_proxy_jexcel_file.rb} +0 -0
  11. data/lib/applications/ruby_poi_translations.rb +64 -0
  12. data/lib/applications/spreadsheet_extensions.rb +31 -0
  13. data/lib/datashift/method_details_manager.rb +4 -0
  14. data/lib/exporters/csv_exporter.rb +3 -1
  15. data/lib/exporters/excel_exporter.rb +59 -74
  16. data/lib/generators/excel_generator.rb +70 -74
  17. data/lib/guards.rb +57 -0
  18. data/lib/loaders/excel_loader.rb +105 -105
  19. data/lib/loaders/loader_base.rb +43 -21
  20. data/lib/loaders/paperclip/attachment_loader.rb +104 -0
  21. data/lib/loaders/paperclip/datashift_paperclip.rb +78 -0
  22. data/lib/loaders/paperclip/{image_loader.rb → image_loading.rb} +2 -18
  23. data/lib/thor/{generate_excel.thor → generate.thor} +48 -0
  24. data/lib/thor/paperclip.thor +85 -0
  25. data/lib/thor/tools.thor +23 -2
  26. data/spec/Gemfile +1 -7
  27. data/spec/csv_exporter_spec.rb +4 -4
  28. data/spec/csv_loader_spec.rb +1 -1
  29. data/spec/excel_exporter_spec.rb +43 -45
  30. data/spec/excel_generator_spec.rb +132 -60
  31. data/spec/excel_loader_spec.rb +134 -140
  32. data/spec/excel_spec.rb +179 -0
  33. data/spec/fixtures/ProjectsMultiCategoriesHeaderLookup.xls +0 -0
  34. data/spec/fixtures/config/database.yml +2 -2
  35. data/spec/fixtures/db/datashift_test_models_db.sqlite +0 -0
  36. data/spec/{db → fixtures/db}/migrate/20110803201325_create_test_bed.rb +0 -0
  37. data/spec/fixtures/load_datashift.thor +3 -0
  38. data/spec/fixtures/models/category.rb +7 -0
  39. data/spec/fixtures/models/empty.rb +2 -0
  40. data/spec/fixtures/models/loader_release.rb +10 -0
  41. data/spec/fixtures/models/long_and_complex_table_linked_to_version.rb +6 -0
  42. data/spec/fixtures/models/milestone.rb +8 -0
  43. data/spec/fixtures/models/owner.rb +5 -0
  44. data/spec/fixtures/models/project.rb +26 -0
  45. data/spec/fixtures/models/test_model_defs.rb +67 -0
  46. data/spec/fixtures/models/version.rb +7 -0
  47. data/spec/loader_spec.rb +4 -3
  48. data/spec/method_dictionary_spec.rb +7 -6
  49. data/spec/method_mapper_spec.rb +3 -2
  50. data/spec/rails_sandbox/.gitignore +15 -0
  51. data/spec/rails_sandbox/Gemfile +40 -0
  52. data/spec/rails_sandbox/README.rdoc +261 -0
  53. data/spec/rails_sandbox/Rakefile +7 -0
  54. data/spec/rails_sandbox/app/assets/images/rails.png +0 -0
  55. data/spec/rails_sandbox/app/assets/javascripts/application.js +15 -0
  56. data/spec/rails_sandbox/app/assets/stylesheets/application.css +13 -0
  57. data/spec/rails_sandbox/app/controllers/application_controller.rb +3 -0
  58. data/spec/rails_sandbox/app/helpers/application_helper.rb +2 -0
  59. data/spec/rails_sandbox/app/mailers/.gitkeep +0 -0
  60. data/spec/rails_sandbox/app/models/.gitkeep +0 -0
  61. data/spec/rails_sandbox/app/models/category.rb +7 -0
  62. data/spec/rails_sandbox/app/models/empty.rb +2 -0
  63. data/spec/rails_sandbox/app/models/loader_release.rb +10 -0
  64. data/spec/rails_sandbox/app/models/long_and_complex_table_linked_to_version.rb +6 -0
  65. data/spec/rails_sandbox/app/models/milestone.rb +8 -0
  66. data/spec/rails_sandbox/app/models/owner.rb +5 -0
  67. data/spec/rails_sandbox/app/models/project.rb +26 -0
  68. data/spec/rails_sandbox/app/models/test_model_defs.rb +67 -0
  69. data/spec/rails_sandbox/app/models/version.rb +7 -0
  70. data/spec/rails_sandbox/app/views/layouts/application.html.erb +14 -0
  71. data/spec/rails_sandbox/config.ru +4 -0
  72. data/spec/rails_sandbox/config/application.rb +62 -0
  73. data/spec/rails_sandbox/config/boot.rb +6 -0
  74. data/spec/rails_sandbox/config/database.yml +20 -0
  75. data/spec/rails_sandbox/config/environment.rb +5 -0
  76. data/spec/rails_sandbox/config/environments/development.rb +37 -0
  77. data/spec/rails_sandbox/config/environments/production.rb +67 -0
  78. data/spec/rails_sandbox/config/environments/test.rb +37 -0
  79. data/spec/rails_sandbox/config/initializers/backtrace_silencers.rb +7 -0
  80. data/spec/rails_sandbox/config/initializers/inflections.rb +15 -0
  81. data/spec/rails_sandbox/config/initializers/mime_types.rb +5 -0
  82. data/spec/rails_sandbox/config/initializers/secret_token.rb +7 -0
  83. data/spec/rails_sandbox/config/initializers/session_store.rb +8 -0
  84. data/spec/rails_sandbox/config/initializers/wrap_parameters.rb +14 -0
  85. data/spec/rails_sandbox/config/locales/en.yml +5 -0
  86. data/spec/rails_sandbox/config/routes.rb +58 -0
  87. data/spec/rails_sandbox/db/migrate/20110803201325_create_test_bed.rb +96 -0
  88. data/spec/rails_sandbox/db/schema.rb +81 -0
  89. data/spec/rails_sandbox/db/seeds.rb +7 -0
  90. data/spec/rails_sandbox/lib/assets/.gitkeep +0 -0
  91. data/spec/rails_sandbox/lib/tasks/.gitkeep +0 -0
  92. data/spec/rails_sandbox/log/.gitkeep +0 -0
  93. data/spec/rails_sandbox/public/404.html +26 -0
  94. data/spec/rails_sandbox/public/422.html +26 -0
  95. data/spec/rails_sandbox/public/500.html +25 -0
  96. data/spec/rails_sandbox/public/favicon.ico +0 -0
  97. data/spec/rails_sandbox/public/index.html +241 -0
  98. data/spec/rails_sandbox/public/robots.txt +5 -0
  99. data/spec/rails_sandbox/script/rails +6 -0
  100. data/spec/rails_sandbox/test/fixtures/.gitkeep +0 -0
  101. data/spec/rails_sandbox/test/functional/.gitkeep +0 -0
  102. data/spec/rails_sandbox/test/integration/.gitkeep +0 -0
  103. data/spec/rails_sandbox/test/performance/browsing_test.rb +12 -0
  104. data/spec/rails_sandbox/test/test_helper.rb +13 -0
  105. data/spec/rails_sandbox/test/unit/.gitkeep +0 -0
  106. data/spec/rails_sandbox/vendor/assets/javascripts/.gitkeep +0 -0
  107. data/spec/rails_sandbox/vendor/assets/stylesheets/.gitkeep +0 -0
  108. data/spec/rails_sandbox/vendor/plugins/.gitkeep +0 -0
  109. data/spec/spec_helper.rb +144 -121
  110. data/spec/thor_spec.rb +34 -14
  111. metadata +207 -194
  112. data/lib/helpers/spree_helper.rb +0 -213
  113. data/lib/loaders/spreadsheet_loader.rb +0 -144
  114. data/lib/loaders/spree/image_loader.rb +0 -90
  115. data/lib/loaders/spree/product_loader.rb +0 -354
  116. data/lib/thor/spree/bootstrap_cleanup.thor +0 -61
  117. data/lib/thor/spree/products_images.thor +0 -252
  118. data/lib/thor/spree/reports.thor +0 -56
  119. data/public/spree/products/large/DEMO_001_ror_bag.jpeg +0 -0
  120. data/public/spree/products/large/DEMO_002_Powerstation.jpg +0 -0
  121. data/public/spree/products/large/DEMO_003_ror_mug.jpeg +0 -0
  122. data/public/spree/products/mini/DEMO_001_ror_bag.jpeg +0 -0
  123. data/public/spree/products/mini/DEMO_002_Powerstation.jpg +0 -0
  124. data/public/spree/products/mini/DEMO_003_ror_mug.jpeg +0 -0
  125. data/public/spree/products/original/DEMO_001_ror_bag.jpeg +0 -0
  126. data/public/spree/products/original/DEMO_002_Powerstation.jpg +0 -0
  127. data/public/spree/products/original/DEMO_003_ror_mug.jpeg +0 -0
  128. data/public/spree/products/product/DEMO_001_ror_bag.jpeg +0 -0
  129. data/public/spree/products/product/DEMO_002_Powerstation.jpg +0 -0
  130. data/public/spree/products/product/DEMO_003_ror_mug.jpeg +0 -0
  131. data/public/spree/products/small/DEMO_001_ror_bag.jpeg +0 -0
  132. data/public/spree/products/small/DEMO_002_Powerstation.jpg +0 -0
  133. data/public/spree/products/small/DEMO_003_ror_mug.jpeg +0 -0
  134. data/spec/fixtures/datashift_Spree_db.sqlite +0 -0
  135. data/spec/fixtures/datashift_test_models_db.sqlite +0 -0
  136. data/spec/fixtures/negative/SpreeProdMiss1Mandatory.csv +0 -4
  137. data/spec/fixtures/negative/SpreeProdMiss1Mandatory.xls +0 -0
  138. data/spec/fixtures/negative/SpreeProdMissManyMandatory.csv +0 -4
  139. data/spec/fixtures/negative/SpreeProdMissManyMandatory.xls +0 -0
  140. data/spec/fixtures/spree/SpreeImages.xls +0 -0
  141. data/spec/fixtures/spree/SpreeMultiVariant.csv +0 -4
  142. data/spec/fixtures/spree/SpreeProducts.csv +0 -4
  143. data/spec/fixtures/spree/SpreeProducts.xls +0 -0
  144. data/spec/fixtures/spree/SpreeProductsDefaults.yml +0 -15
  145. data/spec/fixtures/spree/SpreeProductsMandatoryOnly.xls +0 -0
  146. data/spec/fixtures/spree/SpreeProductsMultiColumn.csv +0 -4
  147. data/spec/fixtures/spree/SpreeProductsMultiColumn.xls +0 -0
  148. data/spec/fixtures/spree/SpreeProductsSimple.csv +0 -4
  149. data/spec/fixtures/spree/SpreeProductsSimple.xls +0 -0
  150. data/spec/fixtures/spree/SpreeProductsWithImages.csv +0 -4
  151. data/spec/fixtures/spree/SpreeProductsWithImages.xls +0 -0
  152. data/spec/fixtures/spree/SpreeZoneExample.csv +0 -5
  153. data/spec/fixtures/spree/SpreeZoneExample.xls +0 -0
  154. data/spec/spree_exporter_spec.rb +0 -72
  155. data/spec/spree_generator_spec.rb +0 -96
  156. data/spec/spree_images_loader_spec.rb +0 -107
  157. data/spec/spree_loader_spec.rb +0 -375
  158. data/spec/spree_method_mapping_spec.rb +0 -226
  159. data/spec/spree_variants_loader_spec.rb +0 -189
  160. data/tasks/export/excel_generator.rake +0 -102
  161. data/tasks/import/excel.rake +0 -75
  162. data/test/helper.rb +0 -18
  163. data/test/test_interact.rb +0 -7
data/lib/guards.rb ADDED
@@ -0,0 +1,57 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2010 - 2012 Tom Statter
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2010
4
+ # License:: Free, Open Source.
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+
26
+
27
+ # Details:: Active Record Loader
28
+ #
29
+ # To pull DataShift commands into your main application :
30
+ #
31
+ # require 'datashift'
32
+ #
33
+ # DataShift::load_commands
34
+ #
35
+ require 'rbconfig'
36
+
37
+ module DataShift
38
+
39
+ module Guards
40
+
41
+ def self.jruby?
42
+ return RUBY_PLATFORM == "java"
43
+ end
44
+ def self.mac?
45
+ RbConfig::CONFIG['target_os'] =~ /darwin/i
46
+ end
47
+
48
+ def self.linux?
49
+ RbConfig::CONFIG['target_os'] =~ /linux/i
50
+ end
51
+
52
+ def self.windows?
53
+ RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
54
+ end
55
+
56
+ end
57
+ end
@@ -15,149 +15,149 @@ require 'datashift/exceptions'
15
15
 
16
16
  module DataShift
17
17
 
18
- if(Guards::jruby?)
18
+ require 'loaders/loader_base'
19
19
 
20
- require 'loaders/loader_base'
20
+ require 'excel'
21
21
 
22
- require 'java'
23
- require 'jexcel_file'
22
+ module ExcelLoading
24
23
 
25
- module ExcelLoading
24
+ # Options:
25
+ # [:sheet_number] : Default is 0. The index of the Excel Worksheet to use.
26
+ # [:header_row] : Default is 0. Use alternative row as header definition.
27
+ # [:mandatory] : Array of mandatory column names
28
+ # [:force_inclusion] : Array of inbound column names to force into mapping
29
+ # [:strict] : Raise exception when no mapping found for a column heading (non mandatory)
26
30
 
27
- # Options:
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)
31
+ def perform_excel_load( file_name, options = {} )
33
32
 
33
+ raise MissingHeadersError, "Minimum row for Headers is 0 - passed #{options[:header_row]}" if(options[:header_row] && options[:header_row].to_i < 0)
34
+
35
+ @excel = Excel.new
34
36
 
35
- def perform_excel_load( file_name, options = {} )
36
-
37
- @excel = JExcelFile.new
38
-
39
- @excel.open(file_name)
37
+ @excel.open(file_name)
40
38
 
41
- #if(options[:verbose])
42
- puts "\n\n\nLoading from Excel file: #{file_name}"
39
+ #if(options[:verbose])
40
+ puts "\n\n\nLoading from Excel file: #{file_name}"
43
41
 
44
- sheet_number = options[:sheet_number] || 0
42
+ sheet_number = options[:sheet_number] || 0
45
43
 
46
- @sheet = @excel.sheet( sheet_number )
44
+
45
+ @sheet = @excel.worksheet( sheet_number )
47
46
 
48
- header_row_index = options[:header_row] || 0
49
- @header_row = @sheet.getRow(header_row_index)
47
+ header_row_index = options[:header_row] || 0
48
+ @header_row = @sheet.row(header_row_index)
50
49
 
51
- raise MissingHeadersError, "No headers found - Check Sheet #{@sheet} is complete and Row #{header_row_index} contains headers" unless(@header_row)
50
+ raise MissingHeadersError, "No headers found - Check Sheet #{@sheet} is complete and Row #{header_row_index} contains headers" unless(@header_row)
52
51
 
53
- @headers = []
54
-
55
- (0..JExcelFile::MAX_COLUMNS).each do |i|
56
- cell = @header_row.getCell(i)
57
- break unless cell
58
- header = "#{@excel.cell_value(cell).to_s}".strip
59
- break if header.empty?
60
- @headers << header
61
- end
52
+ @headers = []
62
53
 
63
- raise MissingHeadersError, "No headers found - Check Sheet #{@sheet} is complete and Row #{header_row_index} contains headers" if(@headers.empty?)
64
-
65
- # Create a method_mapper which maps list of headers into suitable calls on the Active Record class
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 )
68
-
69
- logger.info "Excel Loader processing #{@excel.num_rows} rows"
70
- load_object_class.transaction do
71
- @loaded_objects = []
54
+ # TODO - make more robust - currently end on first empty column
55
+ # There is no actual max columns in Excel .. you will run out of memory though at some point
56
+ (0..1024).each do |column|
57
+ cell = @header_row[column]
58
+ break unless cell
59
+ header = "#{cell.to_s}".strip
60
+ break if header.empty?
61
+ @headers << header
62
+ end
72
63
 
73
- (1..@excel.num_rows).collect do |row|
64
+ raise MissingHeadersError, "No headers found - Check Sheet #{@sheet} is complete and Row #{header_row_index} contains headers" if(@headers.empty?)
74
65
 
75
- # Excel num_rows seems to return all 'visible' rows, which appears to be greater than the actual data rows
76
- # (TODO - write spec to process .xls with a huge number of rows)
77
- #
78
- # This is rubbish but currently manually detect when actual data ends, this isn't very smart but
79
- # got no better idea than ending once we hit the first completely empty row
80
- break if @excel.sheet.getRow(row).nil?
66
+
67
+ # Create a method_mapper which maps list of headers into suitable calls on the Active Record class
68
+ # For example if model has an attribute 'price' will map columns called Price, price, PRICE etc to this attribute
69
+ map_headers_to_operators( @headers, options )
81
70
 
82
- contains_data = false
71
+ logger.info "Excel Loader processing #{@sheet.num_rows} rows"
72
+
73
+ loaded_objects.clear
83
74
 
84
- # First assign any default values for columns not included in parsed_file
85
- process_missing_columns_with_defaults
86
-
75
+ load_object_class.transaction do
76
+
77
+ @sheet.each_with_index do |row, i|
78
+
79
+ next if(i == header_row_index)
80
+
81
+ # Excel num_rows seems to return all 'visible' rows, which appears to be greater than the actual data rows
82
+ # (TODO - write spec to process .xls with a huge number of rows)
83
+ #
84
+ # This is rubbish but currently manually detect when actual data ends, this isn't very smart but
85
+ # got no better idea than ending once we hit the first completely empty row
86
+ break if row.nil?
87
+
88
+ contains_data = false
89
+
90
+ # First assign any default values for columns not included in parsed_file
91
+ process_missing_columns_with_defaults
87
92
 
88
- # TODO - Smart sorting of column processing order ....
89
- # Does not currently ensure mandatory columns (for valid?) processed first but model needs saving
90
- # before associations can be processed so user should ensure mandatory columns are prior to associations
93
+ # TODO - Smart sorting of column processing order ....
94
+ # Does not currently ensure mandatory columns (for valid?) processed first but model needs saving
95
+ # before associations can be processed so user should ensure mandatory columns are prior to associations
91
96
 
92
- # as part of this we also attempt to save early, for example before assigning to
93
- # has_and_belongs_to associations which require the load_object has an id for the join table
97
+ # as part of this we also attempt to save early, for example before assigning to
98
+ # has_and_belongs_to associations which require the load_object has an id for the join table
94
99
 
95
- # Iterate over the columns method_mapper found in Excel,
96
- # pulling data out of associated column
97
- @method_mapper.method_details.each_with_index do |method_detail, col|
100
+ # Iterate over the columns method_mapper found in Excel,
101
+ # pulling data out of associated column
102
+ @method_mapper.method_details.each_with_index do |method_detail, col|
98
103
 
99
- value = value_at(row, col)
104
+ value = row[col]
100
105
 
101
- contains_data = true unless(value.nil? || value.to_s.empty?)
106
+ contains_data = true unless(value.nil? || value.to_s.empty?)
102
107
 
103
- prepare_data(method_detail, value)
108
+ prepare_data(method_detail, value)
104
109
 
105
- process()
106
- end
110
+ process()
111
+ end
107
112
 
108
- break unless(contains_data == true)
109
-
110
- # TODO - requirements to handle not valid ?
111
- # all or nothing or carry on and dump out the exception list at end
112
- #puts "DEBUG: FINAL SAVE #{load_object.inspect}"
113
- unless(save)
114
- failure
115
- logger.error "Failed to save row [#{row}]"
116
- logger.error load_object.errors.inspect
117
- else
118
- logger.info "Row #{row} succesfully SAVED : ID #{load_object.id}"
119
- end
113
+ break unless(contains_data == true)
114
+
115
+ # TODO - requirements to handle not valid ?
116
+ # all or nothing or carry on and dump out the exception list at end
117
+ #puts "DEBUG: FINAL SAVE #{load_object.inspect}"
118
+ unless(save)
119
+ failure
120
+ logger.error "Failed to save row [#{row}]"
121
+ logger.error load_object.errors.inspect
122
+ else
123
+ logger.info "Row #{row} succesfully SAVED : ID #{load_object.id}"
124
+ end
120
125
 
121
- # don't forget to reset the object or we'll update rather than create
122
- new_load_object
126
+ # don't forget to reset the object or we'll update rather than create
127
+ new_load_object
123
128
 
124
- end
125
129
  end
126
- puts "Excel loading stage complete - #{loaded_objects.size} rows added."
127
- puts "There were NO failures." if failed_objects.empty?
128
-
129
- puts "WARNING : Check logs : #{failed_objects.size} rows contained errors and #{failed_objects.size} records NOT created." unless failed_objects.empty?
130
130
  end
131
+
132
+ loaded_objects.compact! if(loaded_objects)
133
+
134
+ puts "Excel loading stage complete - #{loaded_objects.size} rows added."
135
+ puts "There were NO failures." if failed_objects.empty?
136
+
137
+ puts "WARNING : Check logs : #{failed_objects.size} rows contained errors and #{failed_objects.size} records NOT created." unless failed_objects.empty?
138
+ end
131
139
 
132
- def value_at(row, column)
133
- @excel.get_cell_value( @excel.sheet.getRow(row), column)
134
- end
140
+ def value_at(row, column)
141
+ @excel[row, column]
135
142
  end
143
+ end
136
144
 
137
145
 
138
- class ExcelLoader < LoaderBase
146
+ class ExcelLoader < LoaderBase
139
147
 
140
- include ExcelLoading
148
+ include ExcelLoading
141
149
 
142
- def initialize(klass, object = nil, options = {})
143
- super( klass, object, options )
144
- raise "Cannot load - failed to create a #{klass}" unless @load_object
145
- end
146
-
150
+ def initialize(klass, object = nil, options = {})
151
+ super( klass, object, options )
152
+ raise "Cannot load - failed to create a #{klass}" unless @load_object
153
+ end
147
154
 
148
- def perform_load( file_name, options = {} )
149
- perform_excel_load( file_name, options )
150
155
 
151
- puts "Excel loading stage complete - #{loaded_objects.size} rows added."
152
- end
156
+ def perform_load( file_name, options = {} )
157
+ perform_excel_load( file_name, options )
153
158
 
159
+ puts "Excel loading stage complete - #{loaded_objects.size} rows added."
154
160
  end
155
-
156
- else
157
-
158
- module ExcelLoading
159
- end
160
-
161
+
161
162
  end
162
-
163
163
  end
@@ -28,8 +28,10 @@ module DataShift
28
28
 
29
29
  attr_accessor :loaded_objects, :failed_objects
30
30
 
31
- attr_accessor :options, :verbose
31
+ attr_accessor :config, :verbose
32
32
 
33
+ def options() return @config; end
34
+
33
35
  # Support multiple associations being added to a base object to be specified in a single column.
34
36
  #
35
37
  # Entry represents the association to find via supplied name, value to use in the lookup.
@@ -110,9 +112,9 @@ module DataShift
110
112
  end unless(options[:load] == false)
111
113
 
112
114
  @method_mapper = DataShift::MethodMapper.new
113
- @options = options.dup # clone can cause issues like 'can't modify frozen hash'
115
+ @config = options.dup # clone can cause issues like 'can't modify frozen hash'
114
116
 
115
- @verbose = @options[:verbose]
117
+ @verbose = @config[:verbose]
116
118
  @headers = []
117
119
 
118
120
  @default_data_objects ||= {}
@@ -123,6 +125,8 @@ module DataShift
123
125
  @prefixes = {}
124
126
  @postfixes = {}
125
127
 
128
+ @loaded_objects = []
129
+
126
130
  reset(object)
127
131
  end
128
132
 
@@ -149,7 +153,6 @@ module DataShift
149
153
  ext = File.extname(file_name)
150
154
 
151
155
  if(ext.casecmp('.xls') == 0)
152
- raise DataShift::BadRuby, "Please install and use JRuby for loading .xls files" unless(Guards::jruby?)
153
156
  perform_excel_load(file_name, options)
154
157
  elsif(ext.casecmp('.csv') == 0)
155
158
  perform_csv_load(file_name, options)
@@ -227,23 +230,42 @@ module DataShift
227
230
  end
228
231
 
229
232
 
230
- # Find a record for model klazz, looking up on field for x
233
+ # Find a record for model klazz, looking up on field containing search_terms
231
234
  # Responds to global Options :
232
- # :case_sensitive : Default is a case insensitive lookup.
233
- # :use_like : Attempts a lookup using ike and x% ratehr than equality
235
+ # :case_sensitive : Default is a case insensitive lookup.
236
+ # :use_like : Attempts a lookup using ike and x% ratehr than equality
234
237
 
235
- def get_record_by(klazz, field, x)
238
+ def get_record_by(klazz, field, search_terms, split_on = ' ', split_on_prefix = nil)
236
239
 
237
240
  begin
238
- if(@options[:case_sensitive])
239
- return klazz.send("find_by_#{field}", x)
240
- elsif(@options[:use_like])
241
- return klazz.where("#{field} like ?", "#{x}%").first
241
+ record = if(@config[:case_sensitive])
242
+ klazz.send("find_by_#{field}", search_terms)
243
+ elsif(@config[:use_like])
244
+ klazz.where("#{field} like ?", "#{search_terms}%").first
242
245
  else
243
- return klazz.where("lower(#{field}) = ?", x.downcase).first
246
+ klazz.where("lower(#{field}) = ?", search_terms.downcase).first
244
247
  end
248
+
249
+ # try the separate individual portions of the search_terms, front -> back
250
+ search_terms.split(split_on).each do |x|
251
+ z = "#{split_on_prefix}#{x}" if(split_on_prefix)
252
+
253
+ record = get_record_by(klazz, field, z, split_on, split_on_prefix)
254
+ break if record
255
+ end unless(record)
256
+
257
+ # this time try sequentially and incrementally scanning
258
+ search_terms.split(split_on).inject("") do |str, term|
259
+ z = (split_on_prefix) ? "#{split_on_prefix}#{str}#{term}": "#{str}#{term}"
260
+ record = get_record_by(klazz, field, z, split_on, split_on_prefix)
261
+ break if record
262
+ term
263
+ end unless(record)
264
+
265
+ return record
266
+
245
267
  rescue => e
246
- logger.error("Exception attempting to find a record for [#{x}] on #{klazz}.#{field}")
268
+ logger.error("Exception attempting to find a record for [#{search_terms}] on #{klazz}.#{field}")
247
269
  logger.error e.backtrace
248
270
  logger.error e.inspect
249
271
  return nil
@@ -311,14 +333,14 @@ module DataShift
311
333
  end
312
334
 
313
335
  if(data['LoaderBase'])
314
- @options.merge!(data['LoaderBase'])
336
+ @config.merge!(data['LoaderBase'])
315
337
  end
316
338
 
317
339
  if(data[self.class.name])
318
- @options.merge!(data[self.class.name])
340
+ @config.merge!(data[self.class.name])
319
341
  end
320
342
 
321
- logger.info("Loader Options : #{@options.inspect}")
343
+ logger.info("Loader Options : #{@config.inspect}")
322
344
  end
323
345
 
324
346
  # Set member variables to hold details and value.
@@ -380,7 +402,7 @@ module DataShift
380
402
  #
381
403
  def process()
382
404
 
383
- logger.info("Current value to assign : #{@current_value}") #if @options['verboose_logging']
405
+ logger.info("Current value to assign : #{@current_value}") #if @config['verboose_logging']
384
406
 
385
407
  if(@current_method_detail.operator_for(:has_many))
386
408
 
@@ -420,7 +442,7 @@ module DataShift
420
442
  unless(find_by_values.size == @current_value.size)
421
443
  found = @current_value.collect {|f| f.send(find_operator) }
422
444
  @load_object.errors.add( @current_method_detail.operator, "Association with key(s) #{(find_by_values - found).inspect} NOT found")
423
- puts "WARNING: Association with key(s) #{(lookups - found).inspect} NOT found - Not added."
445
+ puts "WARNING: Association #{@current_method_detail.operator} with key(s) #{(find_by_values - found).inspect} NOT found - Not added."
424
446
  next if(@current_value.empty?)
425
447
  end
426
448
 
@@ -464,7 +486,7 @@ module DataShift
464
486
  failure
465
487
  puts "Error saving #{@load_object.class} : #{e.inspect}"
466
488
  logger.error e.backtrace
467
- raise "Error in save whilst processing column #{@current_method_detail.name}" if(@options[:strict])
489
+ raise "Error in save whilst processing column #{@current_method_detail.name}" if(@config[:strict])
468
490
  end
469
491
  end
470
492
 
@@ -522,7 +544,7 @@ module DataShift
522
544
  end
523
545
 
524
546
  def abort_on_failure?
525
- @options[:abort_on_failure] == 'true'
547
+ @config[:abort_on_failure] == 'true'
526
548
  end
527
549
 
528
550
  def loaded_count