datashift 0.16.0 → 0.40.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 (422) hide show
  1. checksums.yaml +4 -4
  2. data/{LICENSE.txt → LICENSE} +0 -0
  3. data/Rakefile +1 -20
  4. data/datashift.thor +125 -0
  5. data/lib/applications/apache_poi_extensions.rb +21 -52
  6. data/lib/applications/excel.rb +64 -57
  7. data/lib/applications/hssf_row_extensions.rb +66 -0
  8. data/lib/applications/jexcel_file.rb +99 -95
  9. data/lib/applications/jexcel_file_extensions.rb +76 -83
  10. data/lib/applications/jruby/word.rb +36 -36
  11. data/lib/applications/ruby_poi_translations.rb +34 -32
  12. data/lib/applications/spreadsheet_extensions.rb +21 -19
  13. data/lib/datashift.rb +49 -59
  14. data/lib/datashift/binder.rb +217 -0
  15. data/lib/datashift/column_packer.rb +21 -72
  16. data/lib/datashift/configuration.rb +317 -0
  17. data/lib/datashift/context_factory.rb +88 -0
  18. data/lib/datashift/core_ext/array.rb +15 -0
  19. data/lib/datashift/core_ext/csv_ext.rb +46 -0
  20. data/lib/datashift/core_ext/string.rb +49 -0
  21. data/lib/datashift/core_ext/to_b.rb +11 -0
  22. data/lib/datashift/delimiters.rb +55 -61
  23. data/lib/datashift/doc_context.rb +137 -0
  24. data/lib/datashift/excel_base.rb +93 -81
  25. data/lib/datashift/exceptions.rb +30 -28
  26. data/lib/datashift/file_definitions.rb +44 -39
  27. data/lib/datashift/guards.rb +5 -5
  28. data/lib/datashift/header.rb +25 -0
  29. data/lib/datashift/headers.rb +94 -0
  30. data/lib/datashift/inbound_data/column.rb +44 -0
  31. data/lib/datashift/inbound_data/lookup_support.rb +33 -0
  32. data/lib/datashift/inbound_data/method_binding.rb +139 -0
  33. data/lib/datashift/load_object.rb +37 -12
  34. data/lib/datashift/logging.rb +54 -27
  35. data/lib/datashift/mandatory.rb +39 -0
  36. data/lib/datashift/mapping/data_flow_schema.rb +198 -0
  37. data/lib/datashift/{model_mapper.rb → mapping/mapper_utils.rb} +30 -10
  38. data/lib/datashift/model_methods/catalogue.rb +183 -0
  39. data/lib/datashift/model_methods/collection.rb +140 -0
  40. data/lib/datashift/model_methods/model_method.rb +162 -0
  41. data/lib/datashift/model_methods/model_methods_manager.rb +76 -0
  42. data/lib/datashift/model_methods/operator.rb +62 -0
  43. data/lib/datashift/node_collection.rb +26 -0
  44. data/lib/datashift/node_context.rb +68 -0
  45. data/lib/datashift/populator.rb +308 -282
  46. data/lib/datashift/progress_monitor.rb +91 -0
  47. data/lib/datashift/querying.rb +110 -52
  48. data/lib/datashift/templates/import_export_config.erb +55 -0
  49. data/lib/datashift/transformation/factory.rb +219 -0
  50. data/lib/datashift/transformation/remove.rb +44 -0
  51. data/lib/datashift/version.rb +3 -0
  52. data/lib/exporters/configuration.rb +84 -0
  53. data/lib/exporters/csv_exporter.rb +54 -52
  54. data/lib/exporters/excel_exporter.rb +80 -61
  55. data/lib/exporters/exporter_base.rb +8 -8
  56. data/lib/generators/config_generator.rb +218 -0
  57. data/lib/generators/csv_generator.rb +13 -70
  58. data/lib/generators/excel_generator.rb +23 -111
  59. data/lib/generators/generator_base.rb +15 -70
  60. data/lib/loaders/configuration.rb +90 -0
  61. data/lib/loaders/csv_loader.rb +63 -101
  62. data/lib/loaders/excel_loader.rb +71 -156
  63. data/lib/loaders/failure_data.rb +40 -0
  64. data/lib/loaders/file_loader.rb +16 -0
  65. data/lib/loaders/loader_base.rb +82 -410
  66. data/lib/loaders/loader_factory.rb +42 -0
  67. data/lib/loaders/paperclip/attachment_loader.rb +157 -140
  68. data/lib/loaders/paperclip/datashift_paperclip.rb +18 -35
  69. data/lib/loaders/paperclip/image_loading.rb +40 -35
  70. data/lib/loaders/reporters/basic_stdout_reporter.rb +40 -0
  71. data/lib/loaders/reporters/reporter.rb +26 -0
  72. data/lib/tasks/config.thor +65 -0
  73. data/{tasks → lib/tasks}/config/seed_fu_product_template.erb +0 -0
  74. data/{tasks → lib/tasks}/config/tidy_config.txt +0 -0
  75. data/lib/tasks/export.thor +192 -0
  76. data/lib/tasks/generate.thor +190 -0
  77. data/lib/tasks/import.thor +142 -0
  78. data/lib/{thor → tasks}/paperclip.thor +69 -69
  79. data/{tasks → lib/tasks/to_convert_to_thor}/db_tasks.rake +20 -20
  80. data/lib/tasks/tools.thor +109 -0
  81. data/spec/MissingAttachmentRecords/DEMO_001_ror_bag.jpeg +0 -0
  82. data/spec/MissingAttachmentRecords/DEMO_002_Powerstation.jpeg +0 -0
  83. data/spec/MissingAttachmentRecords/DEMO_003_ror_mug.jpeg +0 -0
  84. data/spec/MissingAttachmentRecords/DEMO_004_ror_ringer.jpeg +0 -0
  85. data/spec/datashift/binder_spec.rb +266 -0
  86. data/spec/datashift/config_generator_spec.rb +186 -0
  87. data/spec/datashift/configuration.rb +66 -0
  88. data/spec/datashift/context_factory_spec.rb +63 -0
  89. data/spec/datashift/data_flow_schema_spec.rb +150 -0
  90. data/spec/datashift/datashift_spec.rb +52 -0
  91. data/spec/datashift/excel_base_spec.rb +57 -0
  92. data/spec/datashift/excel_spec.rb +188 -0
  93. data/spec/datashift/failure_data_spec.rb +27 -0
  94. data/spec/{file_definitions.rb → datashift/file_definitions.rb} +9 -10
  95. data/spec/datashift/headers_spec.rb +56 -0
  96. data/spec/datashift/inbound_data_spec.rb +47 -0
  97. data/spec/datashift/mapper_utils_spec.rb +38 -0
  98. data/spec/datashift/method_binding_spec.rb +60 -0
  99. data/spec/datashift/model_method_spec.rb +109 -0
  100. data/spec/datashift/model_methods_catalogue.rb +111 -0
  101. data/spec/datashift/model_methods_collection_spec.rb +138 -0
  102. data/spec/datashift/model_methods_manager_spec.rb +329 -0
  103. data/spec/datashift/populator_spec.rb +117 -0
  104. data/spec/datashift/thor_spec.rb +314 -0
  105. data/spec/datashift/transformation/factory_spec.rb +195 -0
  106. data/spec/datashift/transformation/transformer_remove_spec.rb +43 -0
  107. data/spec/dummy/Gemfile +53 -0
  108. data/spec/dummy/Gemfile.lock +197 -0
  109. data/spec/dummy/README.rdoc +28 -0
  110. data/spec/dummy/Rakefile +6 -0
  111. data/spec/dummy/app/assets/javascripts/application.js +16 -0
  112. data/spec/dummy/app/assets/javascripts/categories.js +2 -0
  113. data/spec/dummy/app/assets/javascripts/digitals.js +2 -0
  114. data/spec/dummy/app/assets/javascripts/empties.js +2 -0
  115. data/spec/dummy/app/assets/javascripts/loader_releases.js +2 -0
  116. data/spec/dummy/app/assets/javascripts/long_and_complex_table_linked_to_versions.js +2 -0
  117. data/spec/dummy/app/assets/javascripts/milestones.js +2 -0
  118. data/spec/dummy/app/assets/javascripts/owners.js +2 -0
  119. data/spec/dummy/app/assets/javascripts/projects.js +2 -0
  120. data/spec/dummy/app/assets/javascripts/users.js +2 -0
  121. data/spec/dummy/app/assets/javascripts/versions.js +2 -0
  122. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  123. data/spec/dummy/app/assets/stylesheets/categories.css +4 -0
  124. data/spec/dummy/app/assets/stylesheets/digitals.css +4 -0
  125. data/spec/dummy/app/assets/stylesheets/empties.css +4 -0
  126. data/spec/dummy/app/assets/stylesheets/loader_releases.css +4 -0
  127. data/spec/dummy/app/assets/stylesheets/long_and_complex_table_linked_to_versions.css +4 -0
  128. data/spec/dummy/app/assets/stylesheets/milestones.css +4 -0
  129. data/spec/dummy/app/assets/stylesheets/owners.css +4 -0
  130. data/spec/dummy/app/assets/stylesheets/projects.css +4 -0
  131. data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
  132. data/spec/dummy/app/assets/stylesheets/users.css +4 -0
  133. data/spec/dummy/app/assets/stylesheets/versions.css +4 -0
  134. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  135. data/spec/dummy/app/controllers/categories_controller.rb +58 -0
  136. data/spec/dummy/app/controllers/digitals_controller.rb +58 -0
  137. data/spec/dummy/app/controllers/empties_controller.rb +58 -0
  138. data/spec/dummy/app/controllers/loader_releases_controller.rb +58 -0
  139. data/spec/dummy/app/controllers/long_and_complex_table_linked_to_versions_controller.rb +58 -0
  140. data/spec/dummy/app/controllers/milestones_controller.rb +58 -0
  141. data/spec/dummy/app/controllers/owners_controller.rb +58 -0
  142. data/spec/dummy/app/controllers/projects_controller.rb +58 -0
  143. data/spec/dummy/app/controllers/users_controller.rb +58 -0
  144. data/spec/dummy/app/controllers/versions_controller.rb +58 -0
  145. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  146. data/spec/dummy/app/helpers/categories_helper.rb +2 -0
  147. data/spec/dummy/app/helpers/digitals_helper.rb +2 -0
  148. data/spec/dummy/app/helpers/empties_helper.rb +2 -0
  149. data/spec/dummy/app/helpers/loader_releases_helper.rb +2 -0
  150. data/spec/dummy/app/helpers/long_and_complex_table_linked_to_versions_helper.rb +2 -0
  151. data/spec/dummy/app/helpers/milestones_helper.rb +2 -0
  152. data/spec/dummy/app/helpers/owners_helper.rb +2 -0
  153. data/spec/dummy/app/helpers/projects_helper.rb +2 -0
  154. data/spec/dummy/app/helpers/users_helper.rb +2 -0
  155. data/spec/dummy/app/helpers/versions_helper.rb +2 -0
  156. data/spec/dummy/app/models/category.rb +6 -0
  157. data/spec/dummy/app/models/digital.rb +22 -0
  158. data/spec/dummy/app/models/empty.rb +2 -0
  159. data/spec/dummy/app/models/loader_release.rb +10 -0
  160. data/spec/dummy/app/models/long_and_complex_table_linked_to_version.rb +6 -0
  161. data/spec/dummy/app/models/milestone.rb +15 -0
  162. data/spec/dummy/app/models/owner.rb +13 -0
  163. data/spec/dummy/app/models/project.rb +53 -0
  164. data/spec/dummy/app/models/user.rb +5 -0
  165. data/spec/dummy/app/models/version.rb +7 -0
  166. data/spec/dummy/app/views/categories/_form.html.erb +17 -0
  167. data/spec/dummy/app/views/categories/edit.html.erb +6 -0
  168. data/spec/dummy/app/views/categories/index.html.erb +25 -0
  169. data/spec/dummy/app/views/categories/new.html.erb +5 -0
  170. data/spec/dummy/app/views/categories/show.html.erb +4 -0
  171. data/spec/dummy/app/views/digitals/_form.html.erb +17 -0
  172. data/spec/dummy/app/views/digitals/edit.html.erb +6 -0
  173. data/spec/dummy/app/views/digitals/index.html.erb +25 -0
  174. data/spec/dummy/app/views/digitals/new.html.erb +5 -0
  175. data/spec/dummy/app/views/digitals/show.html.erb +4 -0
  176. data/spec/dummy/app/views/empties/_form.html.erb +17 -0
  177. data/spec/dummy/app/views/empties/edit.html.erb +6 -0
  178. data/spec/dummy/app/views/empties/index.html.erb +25 -0
  179. data/spec/dummy/app/views/empties/new.html.erb +5 -0
  180. data/spec/dummy/app/views/empties/show.html.erb +4 -0
  181. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  182. data/spec/dummy/app/views/loader_releases/_form.html.erb +17 -0
  183. data/spec/dummy/app/views/loader_releases/edit.html.erb +6 -0
  184. data/spec/dummy/app/views/loader_releases/index.html.erb +25 -0
  185. data/spec/dummy/app/views/loader_releases/new.html.erb +5 -0
  186. data/spec/dummy/app/views/loader_releases/show.html.erb +4 -0
  187. data/spec/dummy/app/views/long_and_complex_table_linked_to_versions/_form.html.erb +17 -0
  188. data/spec/dummy/app/views/long_and_complex_table_linked_to_versions/edit.html.erb +6 -0
  189. data/spec/dummy/app/views/long_and_complex_table_linked_to_versions/index.html.erb +25 -0
  190. data/spec/dummy/app/views/long_and_complex_table_linked_to_versions/new.html.erb +5 -0
  191. data/spec/dummy/app/views/long_and_complex_table_linked_to_versions/show.html.erb +4 -0
  192. data/spec/dummy/app/views/milestones/_form.html.erb +17 -0
  193. data/spec/dummy/app/views/milestones/edit.html.erb +6 -0
  194. data/spec/dummy/app/views/milestones/index.html.erb +25 -0
  195. data/spec/dummy/app/views/milestones/new.html.erb +5 -0
  196. data/spec/dummy/app/views/milestones/show.html.erb +4 -0
  197. data/spec/dummy/app/views/owners/_form.html.erb +17 -0
  198. data/spec/dummy/app/views/owners/edit.html.erb +6 -0
  199. data/spec/dummy/app/views/owners/index.html.erb +25 -0
  200. data/spec/dummy/app/views/owners/new.html.erb +5 -0
  201. data/spec/dummy/app/views/owners/show.html.erb +4 -0
  202. data/spec/dummy/app/views/projects/_form.html.erb +17 -0
  203. data/spec/dummy/app/views/projects/edit.html.erb +6 -0
  204. data/spec/dummy/app/views/projects/index.html.erb +25 -0
  205. data/spec/dummy/app/views/projects/new.html.erb +5 -0
  206. data/spec/dummy/app/views/projects/show.html.erb +4 -0
  207. data/spec/dummy/app/views/users/_form.html.erb +17 -0
  208. data/spec/dummy/app/views/users/edit.html.erb +6 -0
  209. data/spec/dummy/app/views/users/index.html.erb +25 -0
  210. data/spec/dummy/app/views/users/new.html.erb +5 -0
  211. data/spec/dummy/app/views/users/show.html.erb +4 -0
  212. data/spec/dummy/app/views/versions/_form.html.erb +17 -0
  213. data/spec/dummy/app/views/versions/edit.html.erb +6 -0
  214. data/spec/dummy/app/views/versions/index.html.erb +25 -0
  215. data/spec/dummy/app/views/versions/new.html.erb +5 -0
  216. data/spec/dummy/app/views/versions/show.html.erb +4 -0
  217. data/spec/dummy/bin/bundle +3 -0
  218. data/spec/dummy/bin/rails +9 -0
  219. data/spec/dummy/bin/rake +9 -0
  220. data/spec/dummy/bin/setup +29 -0
  221. data/spec/dummy/bin/spring +16 -0
  222. data/spec/dummy/config.ru +4 -0
  223. data/spec/dummy/config/application.rb +26 -0
  224. data/spec/dummy/config/boot.rb +3 -0
  225. data/spec/dummy/config/database.yml +25 -0
  226. data/spec/dummy/config/environment.rb +5 -0
  227. data/spec/dummy/config/environments/development.rb +41 -0
  228. data/spec/dummy/config/environments/production.rb +79 -0
  229. data/spec/dummy/config/environments/test.rb +42 -0
  230. data/spec/dummy/config/initializers/assets.rb +11 -0
  231. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  232. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  233. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  234. data/spec/dummy/config/initializers/inflections.rb +16 -0
  235. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  236. data/spec/dummy/config/initializers/session_store.rb +3 -0
  237. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  238. data/spec/dummy/config/locales/en.yml +23 -0
  239. data/spec/dummy/config/routes.rb +76 -0
  240. data/spec/dummy/config/secrets.yml +22 -0
  241. data/spec/dummy/db/development.sqlite3 +0 -0
  242. data/spec/dummy/db/migrate/20110803201325_create_test_bed.rb +98 -0
  243. data/spec/dummy/db/migrate/20121009161700_add_digitals.rb +24 -0
  244. data/spec/dummy/db/migrate/20161005123106_create_digitals.rb +8 -0
  245. data/spec/dummy/db/migrate/20161005123106_create_long_and_complex_table_linked_to_versions.rb +8 -0
  246. data/spec/dummy/db/migrate/20161005123107_create_loader_releases.rb +8 -0
  247. data/spec/dummy/db/migrate/20161005123108_create_owners.rb +8 -0
  248. data/spec/dummy/db/migrate/20161005123109_create_empties.rb +8 -0
  249. data/spec/dummy/db/migrate/20161005123110_create_projects.rb +8 -0
  250. data/spec/dummy/db/migrate/20161005123111_create_users.rb +8 -0
  251. data/spec/dummy/db/migrate/20161005123111_create_versions.rb +8 -0
  252. data/spec/dummy/db/migrate/20161005123112_create_milestones.rb +8 -0
  253. data/spec/dummy/db/migrate/20161005123113_create_categories.rb +8 -0
  254. data/spec/dummy/db/schema.rb +93 -0
  255. data/spec/dummy/db/seeds.rb +9 -0
  256. data/spec/dummy/db/test.sqlite3 +0 -0
  257. data/spec/dummy/log/test.log +69 -0
  258. data/spec/dummy/public/404.html +67 -0
  259. data/spec/dummy/public/422.html +67 -0
  260. data/spec/dummy/public/500.html +66 -0
  261. data/spec/dummy/public/favicon.ico +0 -0
  262. data/spec/dummy/public/robots.txt +5 -0
  263. data/spec/dummy/sandbox_example.thor +4 -0
  264. data/spec/dummy/test/controllers/categories_controller_test.rb +49 -0
  265. data/spec/dummy/test/controllers/digitals_controller_test.rb +49 -0
  266. data/spec/dummy/test/controllers/empties_controller_test.rb +49 -0
  267. data/spec/dummy/test/controllers/loader_releases_controller_test.rb +49 -0
  268. data/spec/dummy/test/controllers/long_and_complex_table_linked_to_versions_controller_test.rb +49 -0
  269. data/spec/dummy/test/controllers/milestones_controller_test.rb +49 -0
  270. data/spec/dummy/test/controllers/owners_controller_test.rb +49 -0
  271. data/spec/dummy/test/controllers/projects_controller_test.rb +49 -0
  272. data/spec/dummy/test/controllers/users_controller_test.rb +49 -0
  273. data/spec/dummy/test/controllers/versions_controller_test.rb +49 -0
  274. data/spec/dummy/test/factories/categories.rb +5 -0
  275. data/spec/dummy/test/factories/digitals.rb +5 -0
  276. data/spec/dummy/test/factories/empties.rb +5 -0
  277. data/spec/dummy/test/factories/loader_releases.rb +5 -0
  278. data/spec/dummy/test/factories/long_and_complex_table_linked_to_versions.rb +5 -0
  279. data/spec/dummy/test/factories/milestones.rb +5 -0
  280. data/spec/dummy/test/factories/owners.rb +5 -0
  281. data/spec/dummy/test/factories/projects.rb +5 -0
  282. data/spec/dummy/test/factories/users.rb +5 -0
  283. data/spec/dummy/test/factories/versions.rb +5 -0
  284. data/spec/dummy/test/models/category_test.rb +7 -0
  285. data/spec/dummy/test/models/digital_test.rb +7 -0
  286. data/spec/dummy/test/models/empty_test.rb +7 -0
  287. data/spec/dummy/test/models/loader_release_test.rb +7 -0
  288. data/spec/dummy/test/models/long_and_complex_table_linked_to_version_test.rb +7 -0
  289. data/spec/dummy/test/models/milestone_test.rb +7 -0
  290. data/spec/dummy/test/models/owner_test.rb +7 -0
  291. data/spec/dummy/test/models/project_test.rb +7 -0
  292. data/spec/dummy/test/models/user_test.rb +7 -0
  293. data/spec/dummy/test/models/version_test.rb +7 -0
  294. data/spec/dummy/test/test_helper.rb +10 -0
  295. data/spec/exporters/csv_exporter_spec.rb +240 -0
  296. data/spec/exporters/csv_generator_spec.rb +139 -0
  297. data/spec/exporters/excel_exporter_spec.rb +193 -0
  298. data/spec/exporters/excel_generator_spec.rb +181 -0
  299. data/spec/exporters/generator_base_spec.rb +45 -0
  300. data/spec/factories/categories.rb +7 -0
  301. data/spec/factories/factories.rb +18 -0
  302. data/spec/factories/milestone.rb +16 -0
  303. data/spec/factories/projects.rb +41 -0
  304. data/spec/fixtures/BadAssociationName.xls +0 -0
  305. data/spec/fixtures/DemoNegativeTesting.xls +0 -0
  306. data/spec/fixtures/ProjectConfiguration.yml +18 -0
  307. data/spec/fixtures/ProjectsMultiCategories.xls +0 -0
  308. data/spec/fixtures/ProjectsMultiCategoriesHeaderLookup.xls +0 -0
  309. data/spec/fixtures/ProjectsSingleCategories.xls +0 -0
  310. data/spec/fixtures/ProjectsSingleCategories.xlsx +0 -0
  311. data/spec/fixtures/SimpleProjects.xls +0 -0
  312. data/spec/fixtures/config/database.yml +28 -0
  313. data/spec/fixtures/csv/BadAssociationName.csv +6 -0
  314. data/spec/fixtures/csv/DemoNegativeTesting.csv +6 -0
  315. data/spec/fixtures/csv/ProjectsMultiCategories.csv +5 -0
  316. data/spec/fixtures/csv/ProjectsMultiCategoriesHeaderLookup.csv +5 -0
  317. data/spec/fixtures/csv/ProjectsSingleCategories.csv +5 -0
  318. data/spec/fixtures/csv/SimpleProjects.csv +4 -0
  319. data/spec/fixtures/db/migrate/20110803201325_create_test_bed.rb +98 -0
  320. data/spec/fixtures/db/migrate/20121009161700_add_digitals.rb +24 -0
  321. data/spec/fixtures/db/seeds.rb +9 -0
  322. data/spec/fixtures/images/DEMO_001_ror_bag.jpeg +0 -0
  323. data/spec/fixtures/images/DEMO_002_Powerstation.jpeg +0 -0
  324. data/spec/fixtures/images/DEMO_003_ror_mug.jpeg +0 -0
  325. data/spec/fixtures/images/DEMO_004_ror_ringer.jpeg +0 -0
  326. data/spec/fixtures/load_datashift.thor +3 -0
  327. data/spec/fixtures/models/category.rb +6 -0
  328. data/spec/fixtures/models/digital.rb +22 -0
  329. data/spec/fixtures/models/empty.rb +2 -0
  330. data/spec/fixtures/models/loader_release.rb +10 -0
  331. data/spec/fixtures/models/long_and_complex_table_linked_to_version.rb +6 -0
  332. data/spec/fixtures/models/milestone.rb +15 -0
  333. data/spec/fixtures/models/owner.rb +13 -0
  334. data/spec/fixtures/models/project.rb +53 -0
  335. data/spec/fixtures/models/user.rb +5 -0
  336. data/spec/fixtures/models/version.rb +7 -0
  337. data/spec/fixtures/results/exp_project_assoc_headers.xls +0 -0
  338. data/spec/fixtures/results/exp_project_collection_spec.csv +2 -0
  339. data/spec/fixtures/results/exp_project_export.xls +0 -0
  340. data/spec/fixtures/results/exp_project_first_export.xls +0 -0
  341. data/spec/fixtures/results/exp_project_plus_assoc.xls +0 -0
  342. data/spec/fixtures/results/exp_project_plus_assoc_export_spec.csv +9 -0
  343. data/spec/fixtures/results/gen_project_plus_assoc_template.xls +0 -0
  344. data/spec/fixtures/results/gen_project_plus_some_assoc_template.xls +0 -0
  345. data/spec/fixtures/results/gen_project_template.xls +0 -0
  346. data/spec/fixtures/results/project_and_assoc_in_hash_export.xls +0 -0
  347. data/spec/fixtures/results/project_and_assoc_in_json_export.csv +9 -0
  348. data/spec/fixtures/results/project_and_assoc_in_json_export.xls +0 -0
  349. data/spec/fixtures/results/project_export_spec_with_custom_delim_,.csv +2 -0
  350. data/spec/fixtures/results/project_export_spec_with_custom_delim_/302/243.csv +2 -0
  351. data/spec/fixtures/results/project_export_spec_with_custom_delim_/302/247.csv +2 -0
  352. data/spec/fixtures/results/project_plus_assoc_template.csv +1 -0
  353. data/spec/fixtures/results/project_plus_some_assoc_template.csv +1 -0
  354. data/spec/fixtures/results/project_remove_export_spec.csv +2 -0
  355. data/spec/fixtures/results/project_template.csv +1 -0
  356. data/spec/fixtures/results/project_with_methods_export_spec.csv +2 -0
  357. data/spec/fixtures/results/thor_spec_gen_project.csv +1 -0
  358. data/spec/fixtures/sandbox_example.thor +4 -0
  359. data/spec/fixtures/simple_export_spec.xls +0 -0
  360. data/spec/fixtures/simple_template_spec.xls +0 -0
  361. data/spec/fixtures/test_model_defs.rb +7 -0
  362. data/spec/loaders/csv_loader_spec.rb +206 -0
  363. data/spec/loaders/data_flow_excel_loader_spec.rb +290 -0
  364. data/spec/loaders/excel_loader_failures_spec.rb +67 -0
  365. data/spec/loaders/excel_loader_spec.rb +294 -0
  366. data/spec/loaders/loader_base_spec.rb +29 -0
  367. data/spec/loaders/paperclip_loader_spec.rb +106 -0
  368. data/spec/log/datashift.log +14930 -0
  369. data/spec/private/digitals/1/DEMO_003_ror_mug.jpeg +0 -0
  370. data/spec/private/digitals/2/DEMO_002_Powerstation.jpeg +0 -0
  371. data/spec/private/digitals/3/DEMO_004_ror_ringer.jpeg +0 -0
  372. data/spec/private/digitals/4/DEMO_001_ror_bag.jpeg +0 -0
  373. data/spec/spec_helper.rb +26 -230
  374. data/spec/support/clear_and_manage_contexts.rb +25 -0
  375. data/spec/support/database_cleaner.rb +32 -0
  376. data/spec/support/datashift_test_helpers.rb +153 -0
  377. data/spec/support/files_paths_helper.rb +13 -0
  378. data/spec/support/fixtures/results/mapping_template.yaml +15 -0
  379. data/spec/support/sandbox.rb +136 -0
  380. metadata +804 -85
  381. data/README.markdown +0 -274
  382. data/README.rdoc +0 -19
  383. data/VERSION +0 -1
  384. data/datashift.gemspec +0 -48
  385. data/lib/applications/jruby/old_pre_proxy_jexcel_file.rb +0 -437
  386. data/lib/datashift/data_transforms.rb +0 -83
  387. data/lib/datashift/mapping_file_definitions.rb +0 -88
  388. data/lib/datashift/mapping_service.rb +0 -91
  389. data/lib/datashift/method_detail.rb +0 -165
  390. data/lib/datashift/method_details_manager.rb +0 -95
  391. data/lib/datashift/method_dictionary.rb +0 -281
  392. data/lib/datashift/method_mapper.rb +0 -174
  393. data/lib/datashift/thor_base.rb +0 -38
  394. data/lib/generators/mapping_generator.rb +0 -112
  395. data/lib/helpers/core_ext/csv_file.rb +0 -33
  396. data/lib/helpers/core_ext/to_b.rb +0 -24
  397. data/lib/loaders/reporter.rb +0 -58
  398. data/lib/thor/export.thor +0 -175
  399. data/lib/thor/generate.thor +0 -191
  400. data/lib/thor/import.thor +0 -110
  401. data/lib/thor/mapping.thor +0 -65
  402. data/lib/thor/tools.thor +0 -84
  403. data/spec/Gemfile +0 -31
  404. data/spec/Gemfile.lock +0 -134
  405. data/spec/csv_exporter_spec.rb +0 -144
  406. data/spec/csv_generator_spec.rb +0 -159
  407. data/spec/csv_loader_spec.rb +0 -212
  408. data/spec/datashift_spec.rb +0 -55
  409. data/spec/excel_exporter_spec.rb +0 -199
  410. data/spec/excel_generator_spec.rb +0 -203
  411. data/spec/excel_loader_spec.rb +0 -237
  412. data/spec/excel_spec.rb +0 -203
  413. data/spec/loader_base_spec.rb +0 -166
  414. data/spec/mapping_spec.rb +0 -117
  415. data/spec/method_dictionary_spec.rb +0 -300
  416. data/spec/method_mapper_spec.rb +0 -100
  417. data/spec/model_mapper_spec.rb +0 -41
  418. data/spec/paperclip_loader_spec.rb +0 -92
  419. data/spec/populator_spec.rb +0 -128
  420. data/spec/thor_spec.rb +0 -90
  421. data/tasks/file_tasks.rake +0 -37
  422. data/tasks/word_to_seedfu.rake +0 -167
@@ -1,92 +1,37 @@
1
- # Copyright:: (c) Autotelik Media Ltd 2011
1
+ # Copyright:: (c) Autotelik Media Ltd 2015
2
2
  # Author :: Tom Statter
3
- # Date :: Aug 2010
4
3
  # License:: MIT
5
4
  #
6
- # Details:: Base class for generators, which provide serivrs to describe a Model in an external format
5
+ # Details:: Base class for generators, which provide services to describe a Model in an external format
7
6
  #
8
7
  module DataShift
9
8
 
10
9
  class GeneratorBase
11
10
 
12
- attr_accessor :filename, :headers, :remove_list
11
+ include DataShift::Logging
13
12
 
14
- def initialize(filename)
15
- @filename = filename
16
- @headers = []
17
- @remove_list =[]
18
- end
13
+ attr_accessor :configuration
19
14
 
20
- def self.rails_columns
21
- @rails_standard_columns ||= [:id, :created_at, :created_on, :updated_at, :updated_on]
15
+ def initialize
22
16
  end
23
17
 
24
-
25
- # Parse options and build collection of headers for a method_details_mgr wrapping a class
26
- # based on association requirements,
27
- #
28
- # Default is to include *everything*
29
- #
30
- # * <tt>:exclude</tt> - Association TYPE(s) to exclude completely.
31
- #
32
- # Possible association_type values are given by MethodDetail::supported_types_enum
33
- # ... [:assignment, :belongs_to, :has_one, :has_many]
18
+ # Prepare to generate with associations but then
19
+ # calls a **derived generate** method i.e abstract to this base class
34
20
  #
35
- # * <tt>:remove</tt> - Array of header names to remove
21
+ # file_name => Filename for generated template
36
22
  #
37
- # Rails DB columns like id, created_at, updated_at are removed by default
38
- #
39
- # * <tt>:include_rails</tt> - Specify to keep Rails columns in mappings
40
- #
41
- def prepare_model_headers(method_details_mgr, options = {})
42
-
43
- work_list = MethodDetail::supported_types_enum.to_a - [ *options[:exclude] ]
44
-
45
- @headers = []
46
-
47
- work_list.each do |assoc_type|
48
- method_details_for_assoc_type = method_details_mgr.get_list_of_method_details(assoc_type)
49
-
50
- next if(method_details_for_assoc_type.nil? || method_details_for_assoc_type.empty?)
51
-
52
- method_details_for_assoc_type.each do |md|
53
- #comparable_association = md.operator.to_s.downcase.to_sym
54
- #i = remove_list.index { |r| r == comparable_association }
55
- #(i) ? remove_list.delete_at(i) : @headers << "#{md.operator}"
56
- @headers << md.operator
57
- end
58
- end
59
-
60
- remove_headers(options)
61
-
62
- end
63
-
64
- # Parse options and remove headers
65
- # Specify columns to remove with :
66
- # options[:remove]
67
- # Rails columns like id, created_at are removed by default,
68
- # to keep them in specify
69
- # options[:include_rails]
70
- #
71
- def remove_headers(options)
72
- remove_list = prep_remove_list( options )
73
-
74
- #TODO - more efficient way ?
75
- headers.delete_if { |h| remove_list.include?( h.to_sym ) } unless(remove_list.empty?)
76
- end
23
+ def generate_with_associations(file_name, klass)
77
24
 
25
+ state = DataShift::Configuration.call.with
78
26
 
79
- # Take options and create a list of symbols to remove from headers
80
- # Rails columns like id, created_at etc are added to the remove list by default
81
- # Specify :include_rails to keep them in
82
- def prep_remove_list( options )
83
- remove_list = [ *options[:remove] ].compact.collect{|x| x.to_s.downcase.to_sym }
27
+ DataShift::Configuration.call.with = :all
84
28
 
85
- remove_list += GeneratorBase::rails_columns unless(options[:include_rails])
29
+ generate(file_name, klass)
30
+ ensure
31
+ DataShift::Configuration.call.with = state
86
32
 
87
- remove_list
88
33
  end
89
34
 
90
35
  end
91
36
 
92
- end
37
+ end
@@ -0,0 +1,90 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2015
2
+ # Author :: Tom Statter
3
+ # Date :: Feb 2015
4
+ # License:: MIT
5
+ #
6
+ # Details:: Read mappings and provide cache type services for source=>destination mappings
7
+ #
8
+ require 'erubis'
9
+
10
+ module DataShift
11
+
12
+ module Loaders
13
+
14
+ class Configuration < DataShift::Configuration
15
+
16
+ # Default is to stop processing once we hit a completely empty row. Over ride.
17
+ # WARNING maybe slow, as will process all rows as defined by Excel
18
+ # @param [Boolean]
19
+ #
20
+ attr_accessor :allow_empty_rows
21
+
22
+ # Destroy failed objects - if object.save fails at any point destroy the current object - all or nothing
23
+ # Default is true - database is cleaned up
24
+ # @param [Boolean]
25
+ #
26
+ attr_accessor :destroy_on_failure
27
+
28
+ # Stop processing and abort if any row fails to import
29
+ # Default is false - row reported as failure but loading continues
30
+ # @param [Boolean]
31
+ #
32
+ attr_accessor :abort_on_failure
33
+
34
+ # Row containing headers - default is 0
35
+ # @param [Integer]
36
+ #
37
+ attr_writer :header_row
38
+
39
+ def initialize
40
+ @mandatory = []
41
+ @allow_empty_rows = false
42
+ @abort_on_failure = false
43
+ @destroy_on_failure = true
44
+ @header_row = 0
45
+ end
46
+
47
+ # Custom Readers
48
+
49
+ def header_row
50
+ raise MissingHeadersError, "Minimum row for Headers is 0 - passed #{@header_row}" if @header_row.to_i < 0
51
+ @header_row
52
+ end
53
+
54
+ # @return [DataShift::Loaders::Configuration] DataShift's current configuration
55
+ def self.call
56
+ @configuration ||= Loaders::Configuration.new
57
+ end
58
+
59
+ def self.reset
60
+ @configuration = Loaders::Configuration.new
61
+ end
62
+
63
+ # Set DataShift's configure
64
+ # @param config [DataShift::Loaders::Configuration]
65
+ class << self
66
+ attr_writer :configuration
67
+ end
68
+
69
+ # Modify DataShift's current Import configuration
70
+ # ```
71
+ # DataShift::Loaders::Configuration.configure do |config|
72
+ # config.verbose = false
73
+ # end
74
+ # ```
75
+ def self.configure
76
+ yield call
77
+ end
78
+
79
+ # Modify DataShift's current Import configuration from an options hash
80
+
81
+ def self.from_hash( options )
82
+ DataShift::Loaders::Configuration.configure do |config|
83
+ config.mandatory = options[:mandatory] if(options[:mandatory])
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+
90
+ end
@@ -4,144 +4,106 @@
4
4
  # License:: MIT
5
5
  #
6
6
  # Details:: Specific loader to support CSV files.
7
- #
8
7
  #
9
- require 'loaders/loader_base'
10
- require 'datashift/exceptions'
11
- require 'datashift/method_mapper'
8
+ #
9
+ require_relative 'file_loader'
12
10
 
13
11
  module DataShift
14
12
 
15
- module CsvLoading
13
+ class CsvLoader < LoaderBase
16
14
 
17
15
  include DataShift::Logging
16
+ include DataShift::FileLoader
17
+
18
+ def initialize
19
+ super
20
+ end
18
21
 
19
- # Load data through active Record models into DB from a CSV file
20
- #
21
- # Assumes header_row is first row i.e row 0
22
- #
23
- #
24
- # OPTIONS :
25
- #
26
- # [:dummy] : Perform a dummy run - attempt to load everything but then roll back
22
+ # Options
23
+ #
24
+ # [:allow_empty_rows] : Default is to stop processing once we hit a completely empty row. Over ride.
25
+ # WARNING maybe slow, as will process all rows as defined by Excel
26
+ #
27
+ # [:dummy] : Perform a dummy run - attempt to load everything but then roll back
28
+ #
27
29
  #
28
- # Options passed through to : populate_method_mapper_from_headers
29
- #
30
- # [:mandatory] : Array of mandatory column names
31
- # [:force_inclusion] : Array of inbound column names to force into mapping
32
- # [:include_all] : Include all headers in processing - takes precedence of :force_inclusion
33
- # [:strict] : Raise exception when no mapping found for a column heading (non mandatory)
30
+ def perform_load( _options = {} )
31
+ require 'csv'
34
32
 
35
- def perform_csv_load(file_name, options = {})
33
+ raise "Cannot load - failed to create a #{klass}" unless load_object
36
34
 
37
- require "csv"
35
+ logger.info "Starting bulk load from CSV : #{file_name}"
38
36
 
39
- # TODO - can we abstract out what a 'parsed file' is - so a common object can represent excel,csv etc
37
+ # TODO: - can we abstract out what a 'parsed file' is - headers plus value of each node
38
+ # so a common object can represent excel,csv etc
40
39
  # then we can make load() more generic
41
40
 
42
- @parsed_file = CSV.read(file_name)
41
+ parsed_file = CSV.read(file_name)
43
42
 
44
- # Create a method_mapper which maps list of headers into suitable calls on the Active Record class
45
- # For example if model has an attribute 'price' will map columns called Price, price, PRICE etc to this attribute
46
- populate_method_mapper_from_headers( @parsed_file.shift, options)
43
+ # assume headers are row 0
44
+ header_idx = 0
45
+ header_row = parsed_file.shift
46
+
47
+ set_headers( DataShift::Headers.new(:csv, header_idx, header_row) )
48
+
49
+ # maps list of headers into suitable calls on the Active Record class
50
+ bind_headers(headers)
47
51
 
48
- puts "\n\n\nLoading from CSV file: #{file_name}"
49
- puts "Processing #{@parsed_file.size} rows"
50
52
  begin
53
+ puts 'Dummy Run - Changes will be rolled back' if(configuration.dummy_run)
51
54
 
52
55
  load_object_class.transaction do
53
- @reporter.reset
56
+ logger.info "Processing #{parsed_file.size} rows"
54
57
 
55
- @parsed_file.each_with_index do |row, i|
58
+ parsed_file.each_with_index do |row, i|
59
+ current_row_idx = i
56
60
 
57
- @current_row = row
61
+ logger.info "Processing Row #{i} : #{row}"
58
62
 
59
- @reporter.processed_object_count += 1
63
+ # Iterate over the bindings, creating a context from data in associated Excel column
60
64
 
61
- logger.info("Begin processing Row #{@reporter.processed_object_count} from CSV file")
62
-
63
- begin
64
- # First assign any default values for columns not included in parsed_file
65
- process_defaults
65
+ @binder.bindings.each_with_index do |method_binding, i|
66
+ unless method_binding.valid?
67
+ logger.warn("No binding was found for column (#{i})")
68
+ next
69
+ end
66
70
 
67
- # TODO - Smart sorting of column processing order ....
68
- # Does not currently ensure mandatory columns (for valid?) processed first but model needs saving
69
- # before associations can be processed so user should ensure mandatory columns are prior to associations
71
+ value = row[method_binding.inbound_index] # binding contains column number
70
72
 
71
- # as part of this we also attempt to save early, for example before assigning to
72
- # has_and_belongs_to associations which require the load_object has an id for the join table
73
+ context = doc_context.create_node_context(method_binding, i, value)
73
74
 
74
- # Iterate over the columns method_mapper found in Excel,
75
- # pulling data out of associated column
76
- @method_mapper.method_details.each_with_index do |method_detail, col|
75
+ logger.info "Processing Column #{method_binding.inbound_index} (#{method_binding.pp})"
77
76
 
78
- unless method_detail
79
- logger.warn("No method_detail found for col #{col + 1} #{method_detail}")
80
- next # TODO populate unmapped with a real MethodDetail that is 'null' and create is_nil
77
+ begin
78
+ context.process
79
+ rescue => x
80
+ if doc_context.all_or_nothing?
81
+ logger.error('All or nothing set and Current Columnfailed so complete Row aborted')
82
+ break
81
83
  end
82
-
83
- value = row[col]
84
-
85
- process(method_detail, value)
86
- end
87
-
88
- rescue => e
89
- failure(row, true)
90
- logger.error e.inspect
91
- logger.error e.backtrace.first.inspect
92
- logger.error "Failed to process row [#{@reporter.processed_object_count}] (#{@current_row})"
93
-
94
- if(verbose)
95
- puts "Failed to process row [#{@reporter.processed_object_count}] (#{@current_row})"
96
- puts e.inspect
97
84
  end
85
+ end # end of each column(node)
98
86
 
99
- # don't forget to reset the load object
100
- new_load_object
101
- next
102
- end
103
-
104
- # TODO - make optional - all or nothing or carry on and dump out the exception list at end
105
-
106
- logger.debug "Attempting Save on : #{load_object.inspect}"
87
+ doc_context.save_and_monitor_progress
107
88
 
108
- save_and_report
89
+ doc_context.reset unless doc_context.node_context.next_update?
90
+ end # all rows processed
109
91
 
110
- # don't forget to reset the object or we'll update rather than create
111
- new_load_object
92
+ if(configuration.dummy_run)
93
+ puts 'CSV loading stage done - Dummy run so Rolling Back.'
94
+ raise ActiveRecord::Rollback # Don't actually create/upload to DB if we are doing dummy run
112
95
  end
96
+ end # TRANSACTION N.B ActiveRecord::Rollback does not propagate outside of the containing transaction block
113
97
 
114
- raise ActiveRecord::Rollback if(options[:dummy]) # Don't actually create/upload to DB if we are doing dummy run
115
- end
116
98
  rescue => e
117
- logger.error "perform_csv_load failed - #{e.message}:\n#{e.backtrace}"
118
- if e.is_a?(ActiveRecord::Rollback) && options[:dummy]
119
- logger.info "CSV loading stage complete - Dummy run so Rolling Back."
120
- else
121
- raise e
122
- end
99
+ puts "ERROR: CSV loading failed : #{e.inspect}"
100
+ raise e
123
101
  ensure
124
102
  report
125
- end # transaction
126
-
127
- end
128
- end
103
+ end
129
104
 
130
- class CsvLoader < LoaderBase
131
-
132
- include DataShift::CsvLoading
133
-
134
- def initialize(klass, object = nil, options = {})
135
- super( klass, object, options )
136
- raise "Cannot load - failed to create a #{klass}" unless @load_object
137
- end
138
-
139
- def perform_load( file_name, options = {} )
140
- perform_csv_load( file_name, options )
141
-
142
- puts "CSV loading stage complete - #{loaded_count} rows added."
105
+ puts 'CSV loading stage Complete.'
143
106
  end
144
107
 
145
108
  end
146
-
147
- end
109
+ end
@@ -1,6 +1,5 @@
1
- # Copyright:: (c) Autotelik Media Ltd 2011
1
+ # Copyright:: (c) Autotelik Media Ltd 2015
2
2
  # Author :: Tom Statter
3
- # Date :: Aug 2011
4
3
  # License:: MIT
5
4
  #
6
5
  # Details:: Specific loader to support Excel files.
@@ -10,127 +9,100 @@
10
9
  # Iterates over all the rows using mapped operations to assign row data to a database object,
11
10
  # i.e pulls data from each column and sends to object.
12
11
  #
13
- require 'datashift/exceptions'
14
- require 'datashift/exceptions'
12
+ require_relative 'file_loader'
15
13
 
16
14
  module DataShift
17
15
 
18
- require 'loaders/loader_base'
19
-
20
- require 'excel'
21
-
22
- module ExcelLoading
23
-
24
- include ExcelBase
25
-
26
- attr_accessor :excel
27
-
28
- # Currently struggling to determine the 'end' of data in a spreadsheet
29
- # this reflects if current row had any data at all
30
- attr_reader :contains_data
31
-
32
- def start_excel( file_name, options = {} )
33
-
34
- @excel = Excel.new
35
-
36
- excel.open(file_name)
37
-
38
- puts "\n\n\nLoading from Excel file: #{file_name}"
39
- logger.info("\nStarting Load from Excel file: #{file_name}")
40
-
41
- sheet_number = options[:sheet_number] || 0
42
-
43
- @sheet = excel.worksheet( sheet_number )
44
-
45
- parse_headers(@sheet, options[:header_row])
46
-
47
- raise MissingHeadersError, "No headers found - Check Sheet #{@sheet} is complete and Row #{header_row_index} contains headers" if(excel_headers.empty?)
16
+ class ExcelLoader < LoaderBase
48
17
 
49
- # Create a method_mapper which maps list of headers into suitable calls on the Active Record class
50
- # For example if model has an attribute 'price' will map columns called Price or price or PRICE etc to this attribute
51
- populate_method_mapper_from_headers(excel_headers, options )
18
+ include DataShift::ExcelBase
19
+ include DataShift::FileLoader
52
20
 
53
- reporter.reset
21
+ def initialize
22
+ super
54
23
  end
55
24
 
56
- # Options:
57
- # [:dummy] : Perform a dummy run - attempt to load everything but then roll back
58
- #
25
+ # Options
26
+ #
27
+ # [:sheet_name] : Create a new worksheet assign to @sheet. Default is class.name
59
28
  # [:sheet_number] : Default is 0. The index of the Excel Worksheet to use.
60
- # [:header_row] : Default is 0. Use alternative row as header definition.
61
- #
62
- # Options passed through to : populate_method_mapper_from_headers
63
- #
64
- # [:mandatory] : Array of mandatory column names
65
- # [:force_inclusion] : Array of inbound column names to force into mapping
66
- # [:include_all] : Include all headers in processing - takes precedence of :force_inclusion
67
- # [:strict] : Raise exception when no mapping found for a column heading (non mandatory)
29
+ #
30
+ def perform_load( options = {} )
68
31
 
69
- def perform_excel_load( file_name, options = {} )
32
+ allow_empty_rows = DataShift::Loaders::Configuration.call.allow_empty_rows
33
+
34
+ logger.info "Starting bulk load from Excel : #{file_name}"
70
35
 
71
- raise MissingHeadersError, "Minimum row for Headers is 0 - passed #{options[:header_row]}" if(options[:header_row] && options[:header_row].to_i < 0)
36
+ start(file_name, options)
72
37
 
73
- start_excel(file_name, options)
38
+ # maps list of headers into suitable calls on the Active Record class
39
+ bind_headers(headers)
74
40
 
75
41
  begin
76
- puts "Dummy Run - Changes will be rolled back" if options[:dummy]
42
+ puts 'Dummy Run - Changes will be rolled back' if(configuration.dummy_run)
77
43
 
78
44
  load_object_class.transaction do
79
-
80
- @sheet.each_with_index do |row, i|
81
-
45
+ sheet.each_with_index do |row, i|
82
46
  current_row_idx = i
83
- @current_row = row
84
47
 
85
- next if(current_row_idx == header_row_index)
48
+ next if current_row_idx == headers.idx
86
49
 
87
50
  # Excel num_rows seems to return all 'visible' rows, which appears to be greater than the actual data rows
88
51
  # (TODO - write spec to process .xls with a huge number of rows)
89
52
  #
90
- # This is rubbish but currently manually detect when actual data ends, this isn't very smart but
53
+ # manually have to detect when actual data ends, this isn't very smart but
91
54
  # got no better idea than ending once we hit the first completely empty row
92
- break if(@current_row.nil? || @current_row.compact.empty?)
55
+ break if !allow_empty_rows && (row.nil? || row.empty?)
93
56
 
94
- logger.info "Processing Row #{current_row_idx} : #{@current_row}"
57
+ logger.info "Processing Row #{current_row_idx}"
95
58
 
96
- @contains_data = false
59
+ contains_data = false
97
60
 
98
- begin
61
+ doc_context.progress_monitor.start_monitoring
99
62
 
100
- process_excel_row(row)
63
+ # Iterate over the bindings,
64
+ # For each column bound to a model operator, create a context from data in associated Excel column
101
65
 
102
- # This is rubbish but currently have to manually detect when actual data ends,
103
- # no other way to detect when we hit the first completely empty row
104
- break unless(contains_data == true)
66
+ @binder.bindings.each_with_index do |method_binding, _i|
67
+ unless method_binding.valid?
68
+ logger.warn("No binding was found for column (#{current_row_idx})")
69
+ next
70
+ end
105
71
 
106
- rescue => e
107
- process_excel_failure(e, true)
72
+ # get the value from the cell, binding contains the column number
73
+ value = row[method_binding.inbound_index]
108
74
 
109
- # don't forget to reset the load object
110
- new_load_object
111
- next
112
- end
75
+ context = doc_context.create_node_context(method_binding, current_row_idx, value)
113
76
 
114
- break unless(contains_data == true)
77
+ contains_data ||= context.contains_data?
115
78
 
116
- # currently here as we can only identify the end of a speadsheet by first empty row
117
- @reporter.processed_object_count += 1
79
+ logger.info "Processing Column #{method_binding.inbound_index} (#{method_binding.pp})"
118
80
 
119
- # TODO - make optional - all or nothing or carry on and dump out the exception list at end
81
+ begin
82
+ context.process
83
+ rescue
84
+ if doc_context.all_or_nothing?
85
+ logger.error('All or nothing set and Current Column failed so complete Row aborted')
86
+ break
87
+ end
88
+ end
120
89
 
121
- save_and_report
90
+ end
91
+
92
+ # Excel data rows not accurate, seems to have to manually detect when actual Excel data rows end
93
+ break if !allow_empty_rows && contains_data == false
122
94
 
123
- # don't forget to reset the object or we'll update rather than create
124
- new_load_object
95
+ doc_context.save_and_monitor_progress
125
96
 
126
- end # all rows processed
97
+ # unless next operation is update, reset the loader object
98
+ doc_context.reset unless doc_context.node_context.next_update?
99
+ end # all rows processed
127
100
 
128
- if(options[:dummy])
129
- puts "Excel loading stage complete - Dummy run so Rolling Back."
101
+ if(configuration.dummy_run)
102
+ puts 'Excel loading stage done - Dummy run so Rolling Back.'
130
103
  raise ActiveRecord::Rollback # Don't actually create/upload to DB if we are doing dummy run
131
104
  end
132
-
133
- end # TRANSACTION N.B ActiveRecord::Rollback does not propagate outside of the containing transaction block
105
+ end # TRANSACTION N.B ActiveRecord::Rollback does not propagate outside of the containing transaction block
134
106
 
135
107
  rescue => e
136
108
  puts "ERROR: Excel loading failed : #{e.inspect}"
@@ -139,86 +111,29 @@ module DataShift
139
111
  report
140
112
  end
141
113
 
114
+ puts 'Excel loading stage Complete.'
142
115
  end
143
116
 
144
- def process_excel_failure( e, delete_object = true)
145
- failure(@current_row, delete_object)
146
-
147
- if(verbose)
148
- puts "perform_excel_load failed in row [#{current_row_idx}] #{@current_row} - #{e.message} :"
149
- puts e.backtrace
150
- end
151
-
152
- logger.error "perform_excel_load failed in row [#{current_row_idx}] #{@current_row} - #{e.message} :"
153
- logger.error e.backtrace.join("\n")
154
- end
155
-
156
-
157
- def value_at(row, column)
158
- @excel[row, column]
159
- end
160
-
161
- def process_excel_row(row)
162
-
163
- # First assign any default values for columns
164
- process_defaults
165
-
166
- # TODO - Smart sorting of column processing order ....
167
- # Does not currently ensure mandatory columns (for valid?) processed first but model needs saving
168
- # before associations can be processed so user should ensure mandatory columns are prior to associations
169
-
170
- # as part of this we also attempt to save early, for example before assigning to
171
- # has_and_belongs_to associations which require the load_object has an id for the join table
172
-
173
- # Iterate over method_details, working on data out of associated Excel column
174
- @method_mapper.method_details.each_with_index do |method_detail, i|
175
-
176
- unless method_detail
177
- logger.warn("No method_detail found for column (#{i})")
178
- next # TODO populate unmapped with a real MethodDetail that is 'null' and create is_nil
179
- end
180
-
181
- logger.info "Processing Column #{method_detail.column_index} (#{method_detail.operator})"
182
-
183
- value = row[method_detail.column_index]
184
-
185
- @contains_data = true unless(value.nil? || value.to_s.empty?)
186
-
187
- process(method_detail, value)
188
- end
189
-
190
- end
191
-
192
- end
193
-
194
-
195
- class ExcelLoader < LoaderBase
196
-
197
- include ExcelLoading
117
+ private
198
118
 
199
- # Setup loading
200
- #
201
- # Options to drive building the method dictionary for a class, enabling headers to be mapped to operators on that class.
119
+ # Options :
202
120
  #
203
- # Options
204
- # :reload : Force load of the method dictionary for object_class even if already loaded
205
- # :instance_methods : Include setter/delegate style instance methods for assignment, as well as AR columns
206
- # :verbose : Verbose logging and to STDOUT
121
+ # [:sheet_name] : Create a new worksheet assign to @sheet. Default is class.name
122
+ # [:sheet_number] : Default is 0. The index of the Excel Worksheet to use.
207
123
  #
208
- def initialize(klass, object = nil, options = {})
209
- super( klass, object, options )
210
- raise "Cannot load - failed to create a #{klass}" unless @load_object
211
- end
124
+ def start( file_name, options = {} )
125
+ open_excel(file_name, options)
212
126
 
127
+ header_row = DataShift::Loaders::Configuration.call.header_row
213
128
 
214
- def perform_load( file_name, options = {} )
129
+ set_headers( parse_headers(sheet, header_row) )
215
130
 
216
- logger.info "Starting bulk load from Excel : #{file_name}"
217
-
218
- perform_excel_load( file_name, options )
131
+ if headers.empty?
132
+ raise MissingHeadersError, "No headers found - Check Sheet #{sheet} is complete and Row #{headers.idx} contains headers"
133
+ end
219
134
 
220
- puts "Excel loading stage complete - #{loaded_count} rows added."
135
+ excel
221
136
  end
222
137
 
223
138
  end
224
- end
139
+ end