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
@@ -0,0 +1,68 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2016
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2016
4
+ # License:: MIT
5
+ #
6
+ # Details:: Holds the current context - the node we are dealing with
7
+ # so requires the Inbound Column details, the associated ModelMethod
8
+ # and the row node containing the actual data to apply via the model method operator
9
+ #
10
+
11
+ module DataShift
12
+
13
+ class NodeContext
14
+
15
+ include DataShift::Logging
16
+
17
+ attr_accessor :doc_context, :method_binding, :row_index
18
+
19
+ attr_accessor :populator
20
+
21
+ attr_reader :data
22
+
23
+ def initialize(doc_context, method_binding, row_idx, data)
24
+ @doc_context = doc_context
25
+ @method_binding = method_binding
26
+ @row_index = row_idx
27
+ @data = data
28
+
29
+ @populator = ContextFactory.get_populator(method_binding)
30
+ end
31
+
32
+ delegate :model_method, :operator, to: :method_binding
33
+
34
+ def contains_data?
35
+ !(data.nil? || data.to_s.empty?)
36
+ end
37
+
38
+ def next_update?
39
+ false # for now create only
40
+ # TODO : Support UPDATES
41
+ # next = ProcessingRules.next_action(method_binding )
42
+ # next == :update
43
+ end
44
+
45
+ def process
46
+ populator.prepare_and_assign(self, doc_context.load_object, data)
47
+ rescue => x
48
+
49
+ failed = FailureData.new( doc_context.load_object, self, x.message)
50
+
51
+ failed.error_messages << "Failed to process node : #{method_binding.pp}"
52
+
53
+ doc_context.progress_monitor.failure(failed)
54
+
55
+ logger.error("#{x.backtrace.first} : #{x.message}")
56
+ raise x
57
+ end
58
+
59
+ end
60
+
61
+ class EmptyContext < NodeContext
62
+
63
+ def initialize
64
+ super(NilClass, DataShift::NoMethodBinding.new, -1, [])
65
+ end
66
+ end
67
+
68
+ end
@@ -4,24 +4,23 @@
4
4
  # License:: MIT
5
5
  #
6
6
  # Details:: The default Populator class for assigning data to models
7
- #
7
+ #
8
8
  # Provides individual population methods on an AR model.
9
9
  #
10
10
  # Enables users to assign values to AR object, without knowing much about that receiving object.
11
11
  #
12
- require 'to_b'
13
- require 'logging'
14
-
15
12
  module DataShift
16
13
 
17
- Struct.new("Substitution", :pattern, :replacement)
18
-
19
14
  class Populator
20
15
 
21
16
  include DataShift::Logging
17
+ extend DataShift::Logging
18
+
19
+ include DataShift::Delimiters
20
+ extend DataShift::Delimiters
22
21
 
23
22
  def self.insistent_method_list
24
- @insistent_method_list ||= [:to_s, :to_i, :to_f, :to_b]
23
+ @insistent_method_list ||= [:to_s, :downcase, :to_i, :to_f, :to_b]
25
24
  end
26
25
 
27
26
  # When looking up an association, when no field provided, try each of these in turn till a match
@@ -30,211 +29,215 @@ module DataShift
30
29
  @insistent_find_by_list ||= [:name, :title, :id]
31
30
  end
32
31
 
32
+ attr_reader :value, :attribute_hash
33
33
 
34
- # Default data embedded in column headings - so effectively apply globally
35
- # to teh whole column - hence class methods
36
- def self.set_header_default_data(operator, data )
37
- header_default_data[operator] = data
38
- end
39
-
40
- def self.header_default_data
41
- @header_default_data ||= {}
42
- end
34
+ attr_accessor :previous_value, :original_data
43
35
 
36
+ def initialize(transformer = nil)
37
+ # reset
38
+ @transformer = transformer || Transformation.factory
44
39
 
45
- attr_reader :current_value, :original_value_before_override
46
- attr_reader :current_col_type
40
+ @attribute_hash = {}
41
+ end
47
42
 
48
- attr_reader :current_attribute_hash
49
- attr_reader :current_method_detail
43
+ # Main client hooks :
50
44
 
51
- def initialize
52
- @current_value = nil
53
- @current_method_detail = nil
54
- @original_value_before_override = nil
55
- @current_attribute_hash = {}
45
+ # Prepare the data to be populated, then assign to the Db record
56
46
 
47
+ def prepare_and_assign(context, record, data)
48
+ prepare_and_assign_method_binding(context.method_binding, record, data)
57
49
  end
58
50
 
59
- # Convert DSL string forms into a hash
60
- # e.g
61
- #
62
- # "{:name => 'autechre'}" => Hash['name'] = autechre'
63
- # "{:cost_price => '13.45', :price => 23, :sale_price => 4.23 }"
51
+ # This is the most pertinent hook for derived Processors, where you can provide custom
52
+ # population messages for specific Method bindings
64
53
 
65
- def self.string_to_hash( str )
66
- h = {}
67
- str.gsub(/[{}:]/,'').split(', ').map do |e|
68
- k,v = e.split('=>')
54
+ def prepare_and_assign_method_binding(method_binding, record, data)
55
+ prepare_data(method_binding, data)
69
56
 
70
- k.strip!
71
- v.strip!
57
+ assign(method_binding, record)
58
+ end
72
59
 
73
- if( v.match(/['"]/) )
74
- h[k] = v.gsub(/["']/, '')
75
- elsif( v.match(/^\d+$|^\d*\.\d+$|^\.\d+$/) )
76
- h[k] = v.to_f
77
- else
78
- h[k] = v
79
- end
80
- h
81
- end
60
+ def reset
61
+ @value = nil
62
+ @previous_value = nil
63
+ @original_data = nil
64
+ @attribute_hash = {}
65
+ end
66
+
67
+ def value?
68
+ !value.nil?
69
+ end
82
70
 
83
- h
71
+ def self.attribute_hash_const_regexp
72
+ @attribute_hash_const_regexp ||= Regexp.new( attribute_list_start + '.*' + attribute_list_end)
84
73
  end
85
74
 
86
- # Set member variables to hold details, value and optional attributes,
87
- # to be set on the 'value' once created
88
- #
89
75
  # Check supplied value, validate it, and if required :
90
76
  # set to provided default value
91
- # prepend any provided prefixes
77
+ # prepend any provided prefixes
92
78
  # add any provided postfixes
93
- def prepare_data(method_detail, value)
79
+ #
80
+ # Rtns : tuple of [:value, :attribute_hash]
81
+ #
82
+ def prepare_data(method_binding, data)
83
+
84
+ connection_adapter_column = method_binding.model_method.connection_adapter_column
85
+
94
86
 
95
- raise NilDataSuppliedError.new("No method detail supplied for prepare_data") unless(method_detail)
87
+ raise NilDataSuppliedError, 'No method_binding supplied for prepare_data' unless method_binding
88
+
89
+ @original_data = data
96
90
 
97
91
  begin
98
- @prepare_data_const_regexp ||= Regexp.new( Delimiters::attribute_list_start + ".*" + Delimiters::attribute_list_end)
99
-
100
- if( value.is_a? ActiveRecord::Relation ) # Rails 4 - query no longer returns an array
101
- @current_value = value.to_a
102
- elsif( !DataShift::Guards.jruby? && value.class.ancestors.include?(Spreadsheet::Formula))
103
- @current_value = value.value
104
- elsif( value.class.ancestors.include?(ActiveRecord::Base) || value.is_a?(Array))
105
- @current_value = value
106
- else
107
- @current_value = value.to_s
108
92
 
109
- attribute_hash = @current_value.slice!(@prepare_data_const_regexp)
93
+ if(data.is_a?(ActiveRecord::Relation)) # Rails 4 - query no longer returns an array
94
+ @value = data.to_a
110
95
 
111
- if(attribute_hash)
112
- @current_attribute_hash = Populator::string_to_hash( attribute_hash )
113
- logger.info "Populator for #{@current_value} has attributes #{@current_attribute_hash.inspect}"
114
- end
115
- end
96
+ elsif(data.class.ancestors.include?(ActiveRecord::Base) || data.is_a?(Array))
97
+ @value = data
116
98
 
117
- @current_attribute_hash ||= {}
99
+ elsif(!DataShift::Guards.jruby? &&
100
+ (data.is_a?(Spreadsheet::Formula) || data.class.ancestors.include?(Spreadsheet::Formula)) )
118
101
 
119
- @current_method_detail = method_detail
102
+ @value = data.value # TOFIX jruby/apache poi equivalent ?
120
103
 
121
- @current_col_type = @current_method_detail.col_type
104
+ elsif(connection_adapter_column && connection_adapter_column.cast_type.is_a?(ActiveRecord::Type::Boolean))
122
105
 
123
- operator = method_detail.operator
106
+ # DEPRECATION WARNING: You attempted to assign a value which is not explicitly `true` or `false` ("0.00")
107
+ # to a boolean column. Currently this value casts to `false`.
108
+ # This will change to match Ruby's semantics, and will cast to `true` in Rails 5.
109
+ # If you would like to maintain the current behavior, you should explicitly handle the values you would like cast to `false`.
124
110
 
125
- if(has_override?(operator))
126
- override_value(operator) # takes precedence over anything else
111
+ @value = if(data.in? [true, false])
112
+ data
113
+ else
114
+ (data.to_s.downcase == "true" || data.to_s.to_i == 1) ? true : false
115
+ end
127
116
  else
128
- # if no value check for a defaults from config, headers
129
- if(default_value(operator))
130
- @current_value = default_value(operator)
131
- elsif(Populator::header_default_data[operator])
132
- @current_value = Populator::header_default_data[operator].to_s
133
- elsif(Populator::header_default_data[operator])
134
- @current_value = Populator::header_default_data[operator].to_s
135
- elsif(method_detail.find_by_value)
136
- @current_value = method_detail.find_by_value
137
- end if(value.nil? || value.to_s.empty?)
138
- end
117
+ @value = data.to_s
139
118
 
140
- substitute( operator )
119
+ @attribute_hash = @value.slice!( Populator.attribute_hash_const_regexp )
141
120
 
142
- @current_value = "#{prefix(operator)}#{@current_value}" if(prefix(operator))
143
- @current_value = "#{@current_value}#{postfix(operator)}" if(postfix(operator))
121
+ if attribute_hash && !attribute_hash.empty?
122
+ @attribute_hash = Populator.string_to_hash( attribute_hash )
123
+ logger.info "Populator found attribute hash :[#{attribute_hash.inspect}]"
124
+ else
125
+ @attribute_hash = {}
126
+ end
127
+ end
128
+
129
+ run_transforms(method_binding)
144
130
 
145
131
  rescue => e
146
- logger.error("populator failed to prepare data supplied for operator #{method_detail.operator}")
147
- logger.error("populator error: #{e.inspect}")
148
- logger.error("populator stacktrace: #{e.backtrace.last}")
149
- raise DataProcessingError.new("opulator failed to prepare data #{value} for operator #{method_detail.operator}")
132
+ logger.error(e.message)
133
+ logger.error("Populator stacktrace: #{e.backtrace.first}")
134
+ raise DataProcessingError, "Populator failed to prepare data [#{value}] for #{method_binding.pp}"
150
135
  end
151
136
 
152
- return @current_value, @current_attribute_hash
137
+ [value, attribute_hash]
153
138
  end
154
139
 
155
- # Main client hook
140
+ def assign(method_binding, record)
156
141
 
157
- def prepare_and_assign(method_detail, record, value)
142
+ model_method = method_binding.model_method
158
143
 
159
- prepare_data(method_detail, value)
144
+ operator = model_method.operator
160
145
 
161
- assign(record)
162
- end
146
+ klass = model_method.klass
163
147
 
164
- def assign(record)
148
+ if model_method.operator_for(:belongs_to)
149
+ insistent_belongs_to(method_binding, record, value)
150
+ elsif model_method.operator_for(:has_many)
151
+ assign_has_many(method_binding, record)
152
+ elsif model_method.operator_for(:has_one)
165
153
 
166
- raise NilDataSuppliedError.new("No method detail - cannot assign data") unless(current_method_detail)
154
+ if value.is_a?(model_method.klass)
155
+ record.send(operator + '=', value)
156
+ else
157
+ logger.error("Cannot assign value [#{value.inspect}]")
158
+ logger.error("Value was Type (#{value.class}) - Required Type for has_one #{operator} is [#{klass}]")
159
+ end
167
160
 
168
- operator = current_method_detail.operator
161
+ elsif model_method.operator_for(:assignment)
169
162
 
170
- logger.debug("Populator assign - [#{current_value}] via #{current_method_detail.operator} (#{current_method_detail.operator_type})")
163
+ if model_method.connection_adapter_column
171
164
 
172
- if( current_method_detail.operator_for(:belongs_to) )
165
+ return if check_process_enum(record, model_method ) # TOFIX .. enum section probably belongs in prepare_data
173
166
 
174
- insistent_belongs_to(current_method_detail, record, current_value)
167
+ assignment(record, value, model_method)
175
168
 
176
- elsif( current_method_detail.operator_for(:has_many) )
169
+ else
170
+ logger.debug("Brute force assignment of value #{value} => [#{operator}]")
171
+ # brute force case for assignments without a column type (which enables us to do correct type_cast)
172
+ # so in this case, attempt straightforward assignment then if that fails, basic ops such as to_s, to_i, to_f etc
173
+ insistent_assignment(record, value, operator)
174
+ end
177
175
 
178
- # The include? check is best I can come up with right now .. to handle module/namespaces
179
- # TODO - can we determine the real class type of an association
180
- # e.g given a association taxons, which operator.classify gives us Taxon, but actually it's Spree::Taxon
181
- # so how do we get from 'taxons' to Spree::Taxons ? .. check if further info in reflect_on_all_associations
176
+ elsif model_method.operator_for(:method)
177
+ logger.debug("Method delegation assignment of value #{value} => [#{operator}]")
178
+ insistent_assignment(record, value, operator)
182
179
 
183
- begin #if(current_value.is_a?(Array) || current_value.class.name.include?(operator.classify))
184
- record.send(operator) << current_value
185
- rescue => e
186
- logger.error e.inspect
187
- logger.error "Cannot assign #{current_value.inspect} (#{current_value.class}) to has_many association [#{operator}] "
188
- end
180
+ else
181
+ logger.warn("Cannot assign via [#{operator}] to #{record.inspect} ")
182
+ end
189
183
 
190
- elsif( current_method_detail.operator_for(:has_one) )
184
+ end
191
185
 
192
- #puts "DEBUG : HAS_MANY : #{@name} : #{operator}(#{operator_class}) - Lookup #{@current_value} in DB"
193
- if(current_value.is_a?(current_method_detail.operator_class))
194
- record.send(operator + '=', current_value)
195
- else
196
- logger.error("ERROR #{current_value.class} - Not expected type for has_one #{operator} - cannot assign")
197
- # TODO - Not expected type - maybe try to look it up somehow ?"
198
- end
186
+ def assignment(record, value, model_method)
187
+
188
+ operator = model_method.operator
189
+ connection_adapter_column = model_method.connection_adapter_column
190
+
191
+ begin
192
+ if(connection_adapter_column.respond_to? :type_cast)
193
+ logger.debug("Assignment via [#{operator}] to [#{value}] (CAST TYPE [#{model_method.connection_adapter_column.type_cast(value).inspect}])")
194
+
195
+ record.send( operator + '=', model_method.connection_adapter_column.type_cast( value ) )
199
196
 
200
- elsif( current_method_detail.operator_for(:assignment) && current_col_type)
201
- # 'type_cast' was changed to 'type_cast_from_database'
202
- if Rails::VERSION::STRING < '4.2.0'
203
- logger.debug("Assign #{current_value} => [#{operator}] (CAST 2 TYPE #{current_col_type.type_cast( current_value ).inspect})")
204
- record.send( operator + '=' , current_method_detail.col_type.type_cast( current_value ) )
205
197
  else
206
- logger.debug("Assign #{current_value} => [#{operator}] (CAST 2 TYPE #{current_col_type.type_cast_from_database( current_value ).inspect})")
207
- record.send( operator + '=' , current_method_detail.col_type.type_cast_from_database( current_value ) )
208
- end
198
+ logger.debug("Assignment via [#{operator}] to [#{value}] (NO CAST)")
209
199
 
210
- elsif( current_method_detail.operator_for(:assignment) )
211
- logger.debug("Brute force assignment of value #{current_value} => [#{operator}]")
212
- # brute force case for assignments without a column type (which enables us to do correct type_cast)
213
- # so in this case, attempt straightforward assignment then if that fails, basic ops such as to_s, to_i, to_f etc
214
- insistent_assignment(record, current_value, operator)
215
- else
216
- puts "WARNING: No assignment possible on #{record.inspect} using [#{operator}]"
217
- logger.error("WARNING: No assignment possible on #{record.inspect} using [#{operator}]")
200
+ # Good guide on diff ways to set attributes
201
+ # http://www.davidverhasselt.com/set-attributes-in-activerecord/
202
+ if(DataShift::Configuration.call.update_and_validate)
203
+ record.update( operator => value)
204
+ else
205
+ record.send( operator + '=', value)
206
+ end
207
+ end
208
+ rescue => e
209
+ logger.error e.backtrace.first
210
+ logger.error("Assignment failed #{e.inspect}")
211
+ raise DataProcessingError, "Failed to set [#{value}] via [#{operator}] due to ERROR : #{e.message}"
218
212
  end
219
213
  end
220
214
 
221
215
  def insistent_assignment(record, value, operator)
222
216
 
223
- op = operator + '=' unless(operator.include?('='))
217
+ op = operator + '=' unless operator.include?('=')
224
218
 
219
+ # TODO: - fix this crap - perhaps recursion ??
225
220
  begin
226
221
  record.send(op, value)
227
- rescue => e
222
+ rescue
223
+ begin
224
+ op = operator.downcase
225
+ op += '=' unless operator.include?('=')
228
226
 
229
- Populator::insistent_method_list.each do |f|
230
- begin
231
- record.send(op, value.send( f) )
232
- break
233
- rescue => e
234
- if f == Populator::insistent_method_list.last
235
- logger.error(e.inspect)
236
- logger.error("Failed to assign [#{value}] via operator #{operator}")
237
- raise "Failed to assign [#{value}] to #{operator}" unless value.nil?
227
+ record.send(op, value)
228
+
229
+ rescue => e
230
+
231
+ Populator.insistent_method_list.each do |f|
232
+ begin
233
+ record.send(op, value.send(f) )
234
+ break
235
+ rescue => e
236
+ if f == Populator.insistent_method_list.last
237
+ logger.error(e.inspect)
238
+ logger.error("Failed to assign [#{value}] via operator #{operator}")
239
+ raise DataProcessingError, "Failed to assign [#{value}] to #{operator}" unless value.nil?
240
+ end
238
241
  end
239
242
  end
240
243
  end
@@ -242,48 +245,41 @@ module DataShift
242
245
  end
243
246
 
244
247
  # Attempt to find the associated object via id, name, title ....
245
- def insistent_belongs_to(method_detail, record, value )
248
+ def insistent_belongs_to(method_binding, record, value )
249
+
250
+ operator = method_binding.operator
246
251
 
247
- operator = method_detail.operator
252
+ klass = method_binding.model_method.operator_class
248
253
 
249
- if( value.class == method_detail.operator_class)
254
+ if value.class == klass
250
255
  logger.info("Populator assigning #{value} to belongs_to association #{operator}")
251
256
  record.send(operator) << value
252
257
  else
253
258
 
254
- # TODO - DRY all this
255
- if(method_detail.find_by_operator)
259
+ unless method_binding.klass.respond_to?('where')
260
+ raise CouldNotAssignAssociation, "Populator failed to assign [#{value}] to belongs_to [#{operator}]"
261
+ end
256
262
 
257
- item = method_detail.operator_class.where(method_detail.find_by_operator => value).first_or_create
263
+ # Try the default field names
258
264
 
259
- if(item)
260
- logger.info("Populator assigning #{item.inspect} to belongs_to association #{operator}")
261
- record.send(operator + '=', item)
262
- else
263
- logger.error("Could not find or create [#{value}] for belongs_to association [#{operator}]")
264
- raise CouldNotAssignAssociation.new "Populator failed to assign [#{value}] to belongs_to association [#{operator}]"
265
- end
265
+ # TODO: - add find by operators from headers or configuration to insistent_find_by_list
266
+ Populator.insistent_find_by_list.each do |find_by|
267
+ begin
266
268
 
267
- else
268
- #try the default field names
269
- Populator::insistent_find_by_list.each do |x|
270
- begin
269
+ item = klass.where(find_by => value).first_or_create
271
270
 
272
- next unless method_detail.operator_class.respond_to?("where")
271
+ next unless item
273
272
 
274
- item = method_detail.operator_class.where(x => value).first_or_create
273
+ logger.info("Populator assigning #{item.inspect} to belongs_to association #{operator}")
274
+ record.send(operator + '=', item)
275
+ break
275
276
 
276
- if(item)
277
- logger.info("Populator assigning #{item.inspect} to belongs_to association #{operator}")
278
- record.send(operator + '=', item)
279
- break
280
- end
281
- rescue => e
282
- logger.error(e.inspect)
283
- logger.error("Failed attempting to find belongs_to for #{method_detail.pp}")
284
- if(x == Populator::insistent_method_list.last)
285
- raise CouldNotAssignAssociation.new "Populator failed to assign [#{value}] to belongs_to association [#{operator}]" unless value.nil?
286
- end
277
+ rescue => e
278
+ logger.error(e.inspect)
279
+ logger.error("Failed attempting to find belongs_to for #{method_binding.pp}")
280
+ if find_by == Populator.insistent_method_list.last
281
+ raise CouldNotAssignAssociation,
282
+ "Populator failed to assign [#{value}] to belongs_to association [#{operator}]" unless value.nil?
287
283
  end
288
284
  end
289
285
  end
@@ -291,154 +287,184 @@ module DataShift
291
287
  end
292
288
  end
293
289
 
294
- def assignment( operator, record, value )
290
+ def check_process_enum(record, model_method)
295
291
 
296
- op = operator + '=' unless(operator.include?('='))
292
+ klass = model_method.klass
293
+ operator = model_method.operator
297
294
 
298
- begin
299
- record.send(op, value)
300
- rescue => e
301
- Populator::insistent_method_list.each do |f|
302
- begin
303
- record.send(op, value.send( f) )
304
- break
305
- rescue => e
306
- if f == Populator::insistent_method_list.last
307
- puts "I'm sorry I have failed to assign [#{value}] to #{operator}"
308
- raise "I'm sorry I have failed to assign [#{value}] to #{operator}" unless value.nil?
309
- end
310
- end
295
+ if klass.respond_to?(operator.pluralize)
296
+
297
+ enums = klass.send(operator.pluralize)
298
+
299
+ logger.debug("Checking for enum - #{enums.inspect} - #{value.parameterize.underscore}" )
300
+
301
+ if enums.is_a?(Hash) && enums.keys.include?(value.parameterize.underscore)
302
+ # ENUM
303
+ logger.debug("[#{operator}] Appears to be an ENUM - setting to [#{value}])")
304
+
305
+ # TODO: - now we know this column is an enum set operator type to :enum to save this check in future
306
+ # probably requires changes above to just assign enum directly without this check
307
+ model_method.operator_for(:assignment)
308
+
309
+ record.send( operator + '=', value.parameterize.underscore)
310
+ return true
311
311
  end
312
312
  end
313
313
  end
314
314
 
315
+ def self.string_to_hash( str )
316
+ str.to_hash_object
317
+ end
318
+
319
+ private
315
320
 
316
- # Default values and over rides can be provided in Ruby/YAML ???? config file.
317
- #
318
- # Format :
319
- #
320
- # Load Class: (e.g Spree:Product)
321
- # datashift_defaults:
322
- # value_as_string: "Default Project Value"
323
- # category: reference:category_002
324
- #
325
- # datashift_overrides:
326
- # value_as_double: 99.23546
327
- #
328
- def configure_from(load_object_class, yaml_file)
321
+ attr_writer :value, :attribute_hash
322
+
323
+ # TOFIX - Does not belong in this class
324
+ def run_transforms(method_binding)
325
+ default( method_binding ) if value.blank?
329
326
 
330
- data = YAML::load( ERB.new( IO.read(yaml_file) ).result )
327
+ override( method_binding )
331
328
 
332
- # TODO - MOVE DEFAULTS TO OWN MODULE
329
+ substitute( method_binding )
330
+
331
+ prefix( method_binding )
332
+
333
+ postfix( method_binding )
334
+
335
+ # TODO: - enable clients to register their own transformation methods and call them here
336
+ end
333
337
 
334
- logger.info("Setting Populator defaults: #{data.inspect}")
338
+ # A single column can contain multiple lookup key:value definitions.
339
+ # These are delimited by special char defined in Delimiters
340
+ #
341
+ # For example:
342
+ #
343
+ # size:large | colour:red,green,blue |
344
+ #
345
+ def split_multi_assoc_value
346
+ value.to_s.split( multi_assoc_delim )
347
+ end
335
348
 
336
- if(data[load_object_class.name])
349
+ def assign_has_many(method_binding, load_object)
337
350
 
338
- deflts = data[load_object_class.name]['datashift_defaults']
339
- default_values.merge!(deflts) if deflts
351
+ # there are times when we need to save early, for example before assigning to
352
+ # has_and_belongs_to associations which require the load_object has an id for the join table
340
353
 
341
- logger.info("Set Populator default_values: #{default_values.inspect}")
354
+ load_object.save_if_new
342
355
 
343
- ovrides = data[load_object_class.name]['datashift_overrides']
344
- override_values.merge!(ovrides) if ovrides
345
- logger.info("Set Populator overrides: #{override_values.inspect}")
356
+ collection = []
357
+ columns = []
346
358
 
347
- subs = data[load_object_class.name]['datashift_substitutions']
359
+ if value.is_a?(Array)
348
360
 
349
- subs.each do |o, sub|
350
- # TODO support single array as well as multiple [[..,..], [....]]
351
- sub.each { |tuple| set_substitution(o, tuple) }
352
- end if(subs)
361
+ value.each do |record|
362
+ if record.class.ancestors.include?(ActiveRecord::Base)
363
+ collection << record
364
+ else
365
+ columns << record
366
+ end
367
+ end
353
368
 
369
+ else
370
+ # A single column can contain multiple lookup key:value definitions, delimited by special char
371
+ # size:large | colour:red,green,blue => [where size: 'large'], [where colour: IN ['red,green,blue']
372
+ columns = split_multi_assoc_value
354
373
  end
355
374
 
356
- end
375
+ operator = method_binding.operator
357
376
 
358
- # Set a default value to be used to populate Model.operator
359
- # Generally defaults will be used when no value supplied.
360
- def set_substitution(operator, value )
361
- substitutions[operator] ||= []
377
+ columns.each do |col_str|
378
+ # split into usable parts ; size:large or colour:red,green,blue
379
+ field, find_by_values = Querying.where_field_and_values(method_binding, col_str )
362
380
 
363
- substitutions[operator] << Struct::Substitution.new(value[0], value[1])
364
- end
381
+ raise "Cannot perform DB find by #{field}. Expected format key:value" unless field && find_by_values
365
382
 
366
- def substitutions
367
- @substitutions ||= {}
368
- end
383
+ found_values = []
369
384
 
370
- def substitute( operator )
371
- subs = substitutions[operator] || {}
385
+ # we are looking up an association so need the Class of the Association
386
+ klass = method_binding.model_method.operator_class
372
387
 
373
- subs.each do |s|
374
- @original_value_before_override = @current_value
375
- @current_value = @original_value_before_override.gsub(s.pattern.to_s, s.replacement.to_s)
376
- end
377
- end
388
+ raise CouldNotDeriveAssociationClass,
389
+ "Failed to find class for has_many Association : #{method_binding.pp}" unless klass
378
390
 
391
+ logger.info("Running where clause on #{klass} : [#{field} IN #{find_by_values.inspect}]")
379
392
 
380
- # Set a value to be used to populate Model.operator
381
- # Generally over-rides will be used regardless of what value caller supplied.
382
- def set_override_value( operator, value )
383
- override_values[operator] = value
384
- end
393
+ find_by_values.each do |v|
394
+ begin
395
+ found_values << klass.where(field => v).first_or_create
396
+ rescue => e
397
+ logger.error(e.inspect)
398
+ logger.error("Failed to find or create #{klass} where #{field} => #{v}")
399
+ # TODO: some way to define if this is a fatal error or not ?
400
+ end
401
+ end
385
402
 
386
- def override_values
387
- @override_values ||= {}
388
- end
403
+ logger.info("Scan result #{found_values.inspect}")
389
404
 
390
- def override_value( operator )
391
- if(override_values[operator])
392
- @original_value_before_override = @current_value
405
+ unless find_by_values.size == found_values.size
406
+ found = found_values.collect { |f| f.send(field) }
407
+ load_object.errors.add( operator, "Association with key(s) #{(find_by_values - found).inspect} NOT found")
408
+ logger.error "Association [#{operator}] with key(s) #{(find_by_values - found).inspect} NOT found - Not added."
409
+ next if found_values.empty?
410
+ end
393
411
 
394
- @current_value = @override_values[operator]
395
- end
396
- end
412
+ logger.info("Assigning to has_many [#{operator}] : #{found_values.inspect} (#{found_values.class})")
397
413
 
414
+ begin
415
+ load_object.send(operator) << found_values
416
+ rescue => e
417
+ logger.error e.inspect
418
+ logger.error "Cannot assign #{found_values.inspect} to has_many [#{operator}] "
419
+ end
398
420
 
399
- def has_override?( operator )
400
- return override_values.has_key?(operator)
421
+ logger.info("Assignment to has_many [#{operator}] COMPLETE)")
422
+ end # END HAS_MANY
401
423
  end
402
424
 
403
- # Set a default value to be used to populate Model.operator
404
- # Generally defaults will be used when no value supplied.
405
- def set_default_value(operator, value )
406
- default_values[operator] = value
407
- end
425
+ # Transformations
408
426
 
409
- def default_values
410
- @default_values ||= {}
411
- end
427
+ def default( method_binding )
428
+ default = Transformation.factory.default(method_binding)
412
429
 
413
- # Return the default value for supplied operator
414
- def default_value(operator)
415
- default_values[operator]
416
- end
430
+ return unless default
417
431
 
418
- def set_prefix( operator, value )
419
- prefixes[operator] = value
432
+ @previous_value = value
433
+ @value = default
420
434
  end
421
435
 
422
- def prefix(operator)
423
- prefixes[operator]
424
- end
436
+ # Checks Transformation for a substitution for column defined in method_binding
437
+ def substitute( method_binding )
438
+ sub = Transformation.factory.substitution(method_binding)
425
439
 
426
- def prefixes
427
- @prefixes ||= {}
440
+ return unless sub
441
+ @previous_value = value
442
+ @value = previous_value.gsub(sub.pattern.to_s, sub.replacement.to_s)
428
443
  end
429
444
 
430
- def set_postfix(operator, value )
431
- postfixes[operator] = value
445
+ def override( method_binding )
446
+ override = Transformation.factory.override(method_binding)
447
+
448
+ return unless override
449
+ @previous_value = value
450
+ @value = override
432
451
  end
433
452
 
434
- def postfix(operator)
435
- postfixes[operator]
453
+ def prefix( method_binding )
454
+ prefix = Transformation.factory.prefix(method_binding)
455
+
456
+ return unless prefix
457
+ @previous_value = value
458
+ @value = prefix + @value
436
459
  end
437
460
 
438
- def postfixes
439
- @postfixes ||= {}
461
+ def postfix( method_binding )
462
+ postfix = Transformation.factory.postfix(method_binding)
463
+
464
+ return unless postfix
465
+ @previous_value = value
466
+ @value += postfix
440
467
  end
441
468
 
442
469
  end
443
-
444
470
  end