datashift 0.16.0 → 0.40.0

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