importable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (227) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +37 -0
  4. data/app/assets/javascripts/importable/application.js +7 -0
  5. data/app/assets/javascripts/importable/spreadsheets.js +2 -0
  6. data/app/assets/stylesheets/importable/application.css +7 -0
  7. data/app/assets/stylesheets/importable/spreadsheets.css +4 -0
  8. data/app/controllers/importable/application_controller.rb +4 -0
  9. data/app/controllers/importable/spreadsheets_controller.rb +74 -0
  10. data/app/models/importable/spreadsheet.rb +88 -0
  11. data/app/views/importable/spreadsheets/_actions.erb +6 -0
  12. data/app/views/importable/spreadsheets/_choose_worksheet_step.erb +23 -0
  13. data/app/views/importable/spreadsheets/_errors.erb +11 -0
  14. data/app/views/importable/spreadsheets/_form.html.erb +1 -0
  15. data/app/views/importable/spreadsheets/_upload_file_step.erb +12 -0
  16. data/app/views/importable/spreadsheets/new.html.erb +3 -0
  17. data/app/views/importable/spreadsheets/show.html.erb +3 -0
  18. data/config/routes.rb +16 -0
  19. data/db/development.sqlite3 +0 -0
  20. data/db/migrate/20110915001957_create_importable_spreadsheets.rb +10 -0
  21. data/lib/importable/engine.rb +5 -0
  22. data/lib/importable/exceptions.rb +3 -0
  23. data/lib/importable/mapper.rb +40 -0
  24. data/lib/importable/multi_step/import_helpers.rb +39 -0
  25. data/lib/importable/uploader.rb +13 -0
  26. data/lib/importable/validator.rb +16 -0
  27. data/lib/importable/version.rb +3 -0
  28. data/lib/importable.rb +12 -0
  29. data/lib/tasks/importable_tasks.rake +0 -0
  30. data/spec/acceptance/import_spreadsheet_spec.rb +33 -0
  31. data/spec/controllers/importable/spreadsheets_controller_spec.rb +152 -0
  32. data/spec/dummy/Rakefile +7 -0
  33. data/spec/dummy/app/assets/javascripts/application.js +7 -0
  34. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  35. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  36. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  37. data/spec/dummy/app/imports/foo_mapper.rb +5 -0
  38. data/spec/dummy/app/imports/foo_required_field_mapper.rb +5 -0
  39. data/spec/dummy/app/imports/plural_widgets_mapper.rb +5 -0
  40. data/spec/dummy/app/imports/singular_widget_mapper.rb +5 -0
  41. data/spec/dummy/app/models/foo.rb +2 -0
  42. data/spec/dummy/app/models/foo_required_field.rb +3 -0
  43. data/spec/dummy/app/views/layouts/importable/application.html.erb +18 -0
  44. data/spec/dummy/config/application.rb +45 -0
  45. data/spec/dummy/config/boot.rb +10 -0
  46. data/spec/dummy/config/database.yml +11 -0
  47. data/spec/dummy/config/environment.rb +5 -0
  48. data/spec/dummy/config/environments/development.rb +30 -0
  49. data/spec/dummy/config/environments/production.rb +60 -0
  50. data/spec/dummy/config/environments/test.rb +42 -0
  51. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/spec/dummy/config/initializers/inflections.rb +10 -0
  53. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  54. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  55. data/spec/dummy/config/initializers/session_store.rb +8 -0
  56. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/spec/dummy/config/locales/en.yml +5 -0
  58. data/spec/dummy/config/routes.rb +3 -0
  59. data/spec/dummy/config.ru +4 -0
  60. data/spec/dummy/db/development.sqlite3 +0 -0
  61. data/spec/dummy/db/migrate/20110913175340_create_foo_required_fields.rb +10 -0
  62. data/spec/dummy/db/migrate/20110913175348_create_foos.rb +20 -0
  63. data/spec/dummy/db/schema.rb +45 -0
  64. data/spec/dummy/db/test.sqlite3 +0 -0
  65. data/spec/dummy/log/development.log +1440 -0
  66. data/spec/dummy/log/test.log +12899 -0
  67. data/spec/dummy/public/404.html +26 -0
  68. data/spec/dummy/public/422.html +26 -0
  69. data/spec/dummy/public/500.html +26 -0
  70. data/spec/dummy/public/favicon.ico +0 -0
  71. data/spec/dummy/script/rails +6 -0
  72. data/spec/dummy/tmp/cache/assets/C84/C40/sprockets%2F7f0912160518f10045d9e488fc7436af +0 -0
  73. data/spec/dummy/tmp/cache/assets/C92/520/sprockets%2F22333678e07cb97428c92fe9a9f33283 +0 -0
  74. data/spec/dummy/tmp/cache/assets/CAD/170/sprockets%2F96fb203c710450777e6c21c337e5a9d3 +0 -0
  75. data/spec/dummy/tmp/cache/assets/CC8/800/sprockets%2F804f348fd27bfb7555b2e9977494612e +0 -0
  76. data/spec/dummy/tmp/cache/assets/CEC/F40/sprockets%2Feef825d9227d8044ea22a9ce75328574 +0 -0
  77. data/spec/dummy/tmp/cache/assets/D0F/F30/sprockets%2F1c0c77bdf759cb307e0d9a071367476b +0 -0
  78. data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  79. data/spec/dummy/tmp/cache/assets/D4D/A30/sprockets%2F256edbd7ad8e5d6e144e648e82150b89 +0 -0
  80. data/spec/dummy/tmp/cache/assets/D4F/9D0/sprockets%2Fd1cf18a7bc4837a9f12a55e76608f79e +0 -0
  81. data/spec/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +0 -0
  82. data/spec/dummy/tmp/cache/assets/D7B/2D0/sprockets%2F67457fb6359b5cc7faf0ad1e432996db +0 -0
  83. data/spec/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
  84. data/spec/dummy/tmp/cache/assets/DAC/950/sprockets%2Fbc53a3fdde9e5350798e72aa7f78ab28 +0 -0
  85. data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  86. data/spec/dummy/tmp/capybara/capybara-201109142003362645406331.html +14 -0
  87. data/spec/dummy/tmp/capybara/capybara-201109142004009667888038.html +14 -0
  88. data/spec/dummy/uploads/importable/spreadsheet/file/foo/503/foo_single_worksheet.xlsx +0 -0
  89. data/spec/dummy/uploads/importable/spreadsheet/file/foo/504/foo_multi_worksheet.xlsx +0 -0
  90. data/spec/dummy/uploads/importable/spreadsheet/file/foo/505/foo_multi_worksheet.xlsx +0 -0
  91. data/spec/dummy/uploads/importable/spreadsheet/file/foo/506/foo_multi_worksheet.xlsx +0 -0
  92. data/spec/dummy/uploads/importable/spreadsheet/file/foo/507/foo_multi_worksheet.xlsx +0 -0
  93. data/spec/dummy/uploads/importable/spreadsheet/file/foo/508/foo_multi_worksheet.xlsx +0 -0
  94. data/spec/dummy/uploads/importable/spreadsheet/file/foo/509/foo_multi_worksheet.xlsx +0 -0
  95. data/spec/dummy/uploads/importable/spreadsheet/file/foo/510/foo_multi_worksheet.xlsx +0 -0
  96. data/spec/dummy/uploads/importable/spreadsheet/file/foo/511/foo_multi_worksheet.xlsx +0 -0
  97. data/spec/dummy/uploads/importable/spreadsheet/file/foo/512/foo_multi_worksheet.xlsx +0 -0
  98. data/spec/dummy/uploads/importable/spreadsheet/file/foo/513/foo_multi_worksheet.xlsx +0 -0
  99. data/spec/dummy/uploads/importable/spreadsheet/file/foo/514/foo_multi_worksheet.xlsx +0 -0
  100. data/spec/dummy/uploads/importable/spreadsheet/file/foo/515/foo_multi_worksheet.xlsx +0 -0
  101. data/spec/dummy/uploads/importable/spreadsheet/file/foo/516/foo_multi_worksheet.xlsx +0 -0
  102. data/spec/dummy/uploads/importable/spreadsheet/file/foo/517/foo_multi_worksheet.xlsx +0 -0
  103. data/spec/dummy/uploads/importable/spreadsheet/file/foo/518/foo_multi_worksheet.xlsx +0 -0
  104. data/spec/dummy/uploads/importable/spreadsheet/file/foo/519/foo_multi_worksheet.xlsx +0 -0
  105. data/spec/dummy/uploads/importable/spreadsheet/file/foo/520/foo_multi_worksheet.xlsx +0 -0
  106. data/spec/dummy/uploads/importable/spreadsheet/file/foo/521/foo_single_worksheet.xlsx +0 -0
  107. data/spec/dummy/uploads/importable/spreadsheet/file/foo/522/foo_multi_worksheet.xlsx +0 -0
  108. data/spec/dummy/uploads/importable/spreadsheet/file/foo/523/foo_multi_worksheet.xlsx +0 -0
  109. data/spec/dummy/uploads/importable/spreadsheet/file/foo/524/foo_multi_worksheet.xlsx +0 -0
  110. data/spec/dummy/uploads/importable/spreadsheet/file/foo/525/foo_multi_worksheet.xlsx +0 -0
  111. data/spec/dummy/uploads/importable/spreadsheet/file/foo/526/foo_multi_worksheet.xlsx +0 -0
  112. data/spec/dummy/uploads/importable/spreadsheet/file/foo/527/foo_multi_worksheet.xlsx +0 -0
  113. data/spec/dummy/uploads/importable/spreadsheet/file/foo/528/foo_multi_worksheet.xlsx +0 -0
  114. data/spec/dummy/uploads/importable/spreadsheet/file/foo/529/foo_multi_worksheet.xlsx +0 -0
  115. data/spec/dummy/uploads/importable/spreadsheet/file/foo/530/foo_multi_worksheet.xlsx +0 -0
  116. data/spec/dummy/uploads/importable/spreadsheet/file/foo/531/foo_multi_worksheet.xlsx +0 -0
  117. data/spec/dummy/uploads/importable/spreadsheet/file/foo/532/foo_multi_worksheet.xlsx +0 -0
  118. data/spec/dummy/uploads/importable/spreadsheet/file/foo/533/foo_multi_worksheet.xlsx +0 -0
  119. data/spec/dummy/uploads/importable/spreadsheet/file/foo/534/foo_multi_worksheet.xlsx +0 -0
  120. data/spec/dummy/uploads/importable/spreadsheet/file/foo/535/foo_multi_worksheet.xlsx +0 -0
  121. data/spec/dummy/uploads/importable/spreadsheet/file/foo/536/foo_multi_worksheet.xlsx +0 -0
  122. data/spec/dummy/uploads/importable/spreadsheet/file/foo/537/foo_multi_worksheet.xlsx +0 -0
  123. data/spec/dummy/uploads/importable/spreadsheet/file/foo/538/foo_multi_worksheet.xlsx +0 -0
  124. data/spec/dummy/uploads/importable/spreadsheet/file/foo/539/foo_single_worksheet.xlsx +0 -0
  125. data/spec/dummy/uploads/importable/spreadsheet/file/foo/540/foo_multi_worksheet.xlsx +0 -0
  126. data/spec/dummy/uploads/importable/spreadsheet/file/foo/541/foo_multi_worksheet.xlsx +0 -0
  127. data/spec/dummy/uploads/importable/spreadsheet/file/foo/542/foo_multi_worksheet.xlsx +0 -0
  128. data/spec/dummy/uploads/importable/spreadsheet/file/foo/543/foo_multi_worksheet.xlsx +0 -0
  129. data/spec/dummy/uploads/importable/spreadsheet/file/foo/544/foo_multi_worksheet.xlsx +0 -0
  130. data/spec/dummy/uploads/importable/spreadsheet/file/foo/545/foo_multi_worksheet.xlsx +0 -0
  131. data/spec/dummy/uploads/importable/spreadsheet/file/foo/546/foo_multi_worksheet.xlsx +0 -0
  132. data/spec/dummy/uploads/importable/spreadsheet/file/foo/547/foo_multi_worksheet.xlsx +0 -0
  133. data/spec/dummy/uploads/importable/spreadsheet/file/foo/548/foo_multi_worksheet.xlsx +0 -0
  134. data/spec/dummy/uploads/importable/spreadsheet/file/foo/549/foo_multi_worksheet.xlsx +0 -0
  135. data/spec/dummy/uploads/importable/spreadsheet/file/foo/550/foo_multi_worksheet.xlsx +0 -0
  136. data/spec/dummy/uploads/importable/spreadsheet/file/foo/551/foo_multi_worksheet.xlsx +0 -0
  137. data/spec/dummy/uploads/importable/spreadsheet/file/foo/552/foo_multi_worksheet.xlsx +0 -0
  138. data/spec/dummy/uploads/importable/spreadsheet/file/foo/553/foo_multi_worksheet.xlsx +0 -0
  139. data/spec/dummy/uploads/importable/spreadsheet/file/foo/554/foo_multi_worksheet.xlsx +0 -0
  140. data/spec/dummy/uploads/importable/spreadsheet/file/foo/555/foo_multi_worksheet.xlsx +0 -0
  141. data/spec/dummy/uploads/importable/spreadsheet/file/foo/556/foo_multi_worksheet.xlsx +0 -0
  142. data/spec/dummy/uploads/importable/spreadsheet/file/foo/557/foo_single_worksheet.xlsx +0 -0
  143. data/spec/dummy/uploads/importable/spreadsheet/file/foo/558/foo_multi_worksheet.xlsx +0 -0
  144. data/spec/dummy/uploads/importable/spreadsheet/file/foo/559/foo_multi_worksheet.xlsx +0 -0
  145. data/spec/dummy/uploads/importable/spreadsheet/file/foo/560/foo_multi_worksheet.xlsx +0 -0
  146. data/spec/dummy/uploads/importable/spreadsheet/file/foo/561/foo_multi_worksheet.xlsx +0 -0
  147. data/spec/dummy/uploads/importable/spreadsheet/file/foo/562/foo_multi_worksheet.xlsx +0 -0
  148. data/spec/dummy/uploads/importable/spreadsheet/file/foo/563/foo_multi_worksheet.xlsx +0 -0
  149. data/spec/dummy/uploads/importable/spreadsheet/file/foo/564/foo_multi_worksheet.xlsx +0 -0
  150. data/spec/dummy/uploads/importable/spreadsheet/file/foo/565/foo_multi_worksheet.xlsx +0 -0
  151. data/spec/dummy/uploads/importable/spreadsheet/file/foo/566/foo_multi_worksheet.xlsx +0 -0
  152. data/spec/dummy/uploads/importable/spreadsheet/file/foo/567/foo_multi_worksheet.xlsx +0 -0
  153. data/spec/dummy/uploads/importable/spreadsheet/file/foo/568/foo_multi_worksheet.xlsx +0 -0
  154. data/spec/dummy/uploads/importable/spreadsheet/file/foo/569/foo_multi_worksheet.xlsx +0 -0
  155. data/spec/dummy/uploads/importable/spreadsheet/file/foo/570/foo_multi_worksheet.xlsx +0 -0
  156. data/spec/dummy/uploads/importable/spreadsheet/file/foo/571/foo_multi_worksheet.xlsx +0 -0
  157. data/spec/dummy/uploads/importable/spreadsheet/file/foo/572/foo_multi_worksheet.xlsx +0 -0
  158. data/spec/dummy/uploads/importable/spreadsheet/file/foo/573/foo_multi_worksheet.xlsx +0 -0
  159. data/spec/dummy/uploads/importable/spreadsheet/file/foo/574/foo_multi_worksheet.xlsx +0 -0
  160. data/spec/dummy/uploads/importable/spreadsheet/file/foo/575/foo_single_worksheet.xlsx +0 -0
  161. data/spec/dummy/uploads/importable/spreadsheet/file/foo/576/foo_multi_worksheet.xlsx +0 -0
  162. data/spec/dummy/uploads/importable/spreadsheet/file/foo/577/foo_multi_worksheet.xlsx +0 -0
  163. data/spec/dummy/uploads/importable/spreadsheet/file/foo/578/foo_multi_worksheet.xlsx +0 -0
  164. data/spec/dummy/uploads/importable/spreadsheet/file/foo/579/foo_multi_worksheet.xlsx +0 -0
  165. data/spec/dummy/uploads/importable/spreadsheet/file/foo/580/foo_multi_worksheet.xlsx +0 -0
  166. data/spec/dummy/uploads/importable/spreadsheet/file/foo/581/foo_multi_worksheet.xlsx +0 -0
  167. data/spec/dummy/uploads/importable/spreadsheet/file/foo/582/foo_multi_worksheet.xlsx +0 -0
  168. data/spec/dummy/uploads/importable/spreadsheet/file/foo/583/foo_multi_worksheet.xlsx +0 -0
  169. data/spec/dummy/uploads/importable/spreadsheet/file/foo/584/foo_multi_worksheet.xlsx +0 -0
  170. data/spec/dummy/uploads/importable/spreadsheet/file/foo/585/foo_multi_worksheet.xlsx +0 -0
  171. data/spec/dummy/uploads/importable/spreadsheet/file/foo/586/foo_multi_worksheet.xlsx +0 -0
  172. data/spec/dummy/uploads/importable/spreadsheet/file/foo/587/foo_multi_worksheet.xlsx +0 -0
  173. data/spec/dummy/uploads/importable/spreadsheet/file/foo/588/foo_multi_worksheet.xlsx +0 -0
  174. data/spec/dummy/uploads/importable/spreadsheet/file/foo/589/foo_multi_worksheet.xlsx +0 -0
  175. data/spec/dummy/uploads/importable/spreadsheet/file/foo/590/foo_multi_worksheet.xlsx +0 -0
  176. data/spec/dummy/uploads/importable/spreadsheet/file/foo/591/foo_multi_worksheet.xlsx +0 -0
  177. data/spec/dummy/uploads/importable/spreadsheet/file/foo/592/foo_multi_worksheet.xlsx +0 -0
  178. data/spec/dummy/uploads/importable/spreadsheet/file/foo/593/foo_single_worksheet.xlsx +0 -0
  179. data/spec/dummy/uploads/importable/spreadsheet/file/foo/594/foo_multi_worksheet.xlsx +0 -0
  180. data/spec/dummy/uploads/importable/spreadsheet/file/foo/595/foo_multi_worksheet.xlsx +0 -0
  181. data/spec/dummy/uploads/importable/spreadsheet/file/foo/596/foo_multi_worksheet.xlsx +0 -0
  182. data/spec/dummy/uploads/importable/spreadsheet/file/foo/597/foo_multi_worksheet.xlsx +0 -0
  183. data/spec/dummy/uploads/importable/spreadsheet/file/foo/598/foo_multi_worksheet.xlsx +0 -0
  184. data/spec/dummy/uploads/importable/spreadsheet/file/foo/599/foo_multi_worksheet.xlsx +0 -0
  185. data/spec/dummy/uploads/importable/spreadsheet/file/foo/600/foo_multi_worksheet.xlsx +0 -0
  186. data/spec/dummy/uploads/importable/spreadsheet/file/foo/601/foo_multi_worksheet.xlsx +0 -0
  187. data/spec/dummy/uploads/importable/spreadsheet/file/foo/602/foo_multi_worksheet.xlsx +0 -0
  188. data/spec/dummy/uploads/importable/spreadsheet/file/foo/603/foo_multi_worksheet.xlsx +0 -0
  189. data/spec/dummy/uploads/importable/spreadsheet/file/foo/604/foo_multi_worksheet.xlsx +0 -0
  190. data/spec/dummy/uploads/importable/spreadsheet/file/foo/605/foo_multi_worksheet.xlsx +0 -0
  191. data/spec/dummy/uploads/importable/spreadsheet/file/foo/606/foo_multi_worksheet.xlsx +0 -0
  192. data/spec/dummy/uploads/importable/spreadsheet/file/foo/607/foo_multi_worksheet.xlsx +0 -0
  193. data/spec/dummy/uploads/importable/spreadsheet/file/foo/608/foo_multi_worksheet.xlsx +0 -0
  194. data/spec/dummy/uploads/importable/spreadsheet/file/foo/609/foo_multi_worksheet.xlsx +0 -0
  195. data/spec/dummy/uploads/importable/spreadsheet/file/foo/610/foo_multi_worksheet.xlsx +0 -0
  196. data/spec/dummy/uploads/importable/spreadsheet/file/foo/611/foo_single_worksheet.xlsx +0 -0
  197. data/spec/dummy/uploads/importable/spreadsheet/file/foo/612/foo_multi_worksheet.xlsx +0 -0
  198. data/spec/dummy/uploads/importable/spreadsheet/file/foo/613/foo_multi_worksheet.xlsx +0 -0
  199. data/spec/dummy/uploads/importable/spreadsheet/file/foo/614/foo_multi_worksheet.xlsx +0 -0
  200. data/spec/dummy/uploads/importable/spreadsheet/file/foo/615/foo_multi_worksheet.xlsx +0 -0
  201. data/spec/dummy/uploads/importable/spreadsheet/file/foo/616/foo_multi_worksheet.xlsx +0 -0
  202. data/spec/dummy/uploads/importable/spreadsheet/file/foo/617/foo_multi_worksheet.xlsx +0 -0
  203. data/spec/dummy/uploads/importable/spreadsheet/file/foo/618/foo_multi_worksheet.xlsx +0 -0
  204. data/spec/dummy/uploads/importable/spreadsheet/file/foo/619/foo_multi_worksheet.xlsx +0 -0
  205. data/spec/dummy/uploads/importable/spreadsheet/file/foo/620/foo_multi_worksheet.xlsx +0 -0
  206. data/spec/dummy/uploads/importable/spreadsheet/file/foo/621/foo_multi_worksheet.xlsx +0 -0
  207. data/spec/dummy/uploads/importable/spreadsheet/file/foo/622/foo_multi_worksheet.xlsx +0 -0
  208. data/spec/dummy/uploads/importable/spreadsheet/file/foo/623/foo_multi_worksheet.xlsx +0 -0
  209. data/spec/dummy/uploads/importable/spreadsheet/file/foo/624/foo_multi_worksheet.xlsx +0 -0
  210. data/spec/dummy/uploads/importable/spreadsheet/file/foo/625/foo_multi_worksheet.xlsx +0 -0
  211. data/spec/dummy/uploads/importable/spreadsheet/file/foo/626/foo_multi_worksheet.xlsx +0 -0
  212. data/spec/dummy/uploads/importable/spreadsheet/file/foo/627/foo_multi_worksheet.xlsx +0 -0
  213. data/spec/dummy/uploads/importable/spreadsheet/file/foo/628/foo_multi_worksheet.xlsx +0 -0
  214. data/spec/lib/mapper_spec.rb +70 -0
  215. data/spec/lib/multi_step/import_helpers_spec.rb +94 -0
  216. data/spec/lib/uploader_spec.rb +28 -0
  217. data/spec/lib/validator_spec.rb +29 -0
  218. data/spec/models/spreadsheet_spec.rb +149 -0
  219. data/spec/spec_helper.rb +20 -0
  220. data/spec/support/foo_multi_worksheet.xls +0 -0
  221. data/spec/support/foo_multi_worksheet.xlsx +0 -0
  222. data/spec/support/foo_required_field_invalid.xlsx +0 -0
  223. data/spec/support/foo_required_field_valid.xlsx +0 -0
  224. data/spec/support/foo_single_worksheet.xls +0 -0
  225. data/spec/support/foo_single_worksheet.xlsx +0 -0
  226. data/spec/support/text.txt +1 -0
  227. metadata +584 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 Mike Bannister
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = Importable
2
+
3
+ Import spreadsheets and map data to rails models, easily.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Importable'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+ load 'rspec/rails/tasks/rspec.rake'
26
+
27
+ Bundler::GemHelper.install_tasks
28
+
29
+ namespace :db do
30
+ namespace :test do
31
+ task :prepare do
32
+ # Stub out for engine
33
+ end
34
+ end
35
+ end
36
+
37
+ task :default => 'spec'
@@ -0,0 +1,7 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,7 @@
1
+ /*
2
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
3
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
5
+ *= require_self
6
+ *= require_tree .
7
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,4 @@
1
+ module Importable
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,74 @@
1
+ module Importable
2
+ class SpreadsheetsController < ApplicationController
3
+ before_filter :require_type_param
4
+
5
+ def new
6
+ @spreadsheet = Spreadsheet.new
7
+ end
8
+
9
+ def create
10
+ init_spreadsheet!
11
+
12
+ if @spreadsheet.valid?
13
+ set_current_step!
14
+ prepare_next_step!
15
+
16
+ if @spreadsheet.last_step?
17
+ if import!
18
+ notice = "'#{@spreadsheet.default_sheet}' of #{@type} spreadsheet was successfully imported."
19
+ redirect_to spreadsheet_path(id: @spreadsheet.id, type: @type), notice: notice
20
+ return
21
+ end
22
+ @spreadsheet.previous_step
23
+ end
24
+ end
25
+
26
+ # if not redirected
27
+ render action: 'new'
28
+ end
29
+
30
+ def show
31
+ @spreadsheet = Spreadsheet.find(params[:id])
32
+ end
33
+
34
+ private
35
+
36
+ def init_spreadsheet!
37
+ @spreadsheet = begin
38
+ if params[:current_step] == 'upload_file'
39
+ Spreadsheet.new(file: params[:file], object_type: params[:type])
40
+ else
41
+ Spreadsheet.find(params[:spreadsheet_id])
42
+ end
43
+ end
44
+ end
45
+
46
+ def set_current_step!
47
+ @spreadsheet.current_step = params[:current_step]
48
+ end
49
+
50
+ def prepare_next_step!
51
+ if @spreadsheet.persisted? or @spreadsheet.save
52
+ if params[:back_button]
53
+ @spreadsheet.previous_step
54
+ else
55
+ @spreadsheet.next_step
56
+ end
57
+ params[:current_step] = @spreadsheet.current_step
58
+ end
59
+ end
60
+
61
+ def import!
62
+ default_sheet = @spreadsheet.sheets[params[:default_sheet].to_i]
63
+ @spreadsheet.default_sheet = default_sheet
64
+ @spreadsheet.import!
65
+ end
66
+
67
+ def require_type_param
68
+ unless Spreadsheet.mapper_type_exists?(params[:type])
69
+ raise Exceptions::ParamRequiredError.new("#{params[:type]} import mapper does not exist")
70
+ end
71
+ @type = params[:type]
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,88 @@
1
+ module Importable
2
+ class Spreadsheet < ActiveRecord::Base
3
+ include MultiStep::ImportHelpers
4
+
5
+ delegate :first_row, :last_row, :sheets, :row, :to => :spreadsheet
6
+ delegate :invalid_objects, :to => :mapper
7
+
8
+ mount_uploader :file, Importable::Uploader
9
+
10
+ validates_presence_of :file
11
+ validates_with Importable::Validator
12
+
13
+ def headers
14
+ @headers ||= spreadsheet.row(first_row)
15
+ end
16
+
17
+ def default_sheet=(sheet)
18
+ spreadsheet.default_sheet = sheet
19
+ end
20
+
21
+ def default_sheet
22
+ spreadsheet.default_sheet
23
+ end
24
+
25
+ def spreadsheet
26
+ @spreadsheet ||= spreadsheet_class.new(file.current_path)
27
+ end
28
+
29
+ def import!
30
+ mapper.valid?
31
+ end
32
+
33
+ def mapper
34
+ mapper_class.new(rows)
35
+ end
36
+
37
+ def mapper_class
38
+ singular_mapper_class || plural_mapper_class
39
+ end
40
+
41
+ def spreadsheet_class
42
+ extension = file.current_path.split('.').last
43
+ return Excel if extension == 'xls'
44
+ return Excelx if extension == 'xlsx'
45
+ end
46
+
47
+ def rows
48
+ @rows ||= begin
49
+ (first_row + 1).upto(last_row).map do |index|
50
+ data = {}
51
+ raw_data = row(index)
52
+ headers.zip(raw_data) do |key, val|
53
+ data[key] = val
54
+ end
55
+ data
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.mapper_files
61
+ Dir["#{Rails.root}/app/imports/**.rb"]
62
+ end
63
+
64
+ def self.mapper_types
65
+ mapper_files.map do |mapper_file|
66
+ file_name = File.basename(mapper_file, '.rb')
67
+ mapper_name = file_name.slice(0..-8) if file_name.ends_with?('_mapper')
68
+ mapper_name.try(:singularize)
69
+ end.compact
70
+ end
71
+
72
+ def self.mapper_type_exists?(type)
73
+ self.mapper_types.flat_map do |t|
74
+ [ t.pluralize, t.singularize ]
75
+ end.include?(type)
76
+ end
77
+
78
+ private
79
+
80
+ def singular_mapper_class
81
+ "#{object_type.singularize}_mapper".camelize.constantize rescue nil
82
+ end
83
+
84
+ def plural_mapper_class
85
+ "#{object_type.pluralize}_mapper".camelize.constantize rescue nil
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,6 @@
1
+ <div class="actions">
2
+ <p>
3
+ <%= submit_tag label %>
4
+ <%= submit_tag "Back", :name => "back_button" unless @spreadsheet.first_step? %>
5
+ </p>
6
+ </div>
@@ -0,0 +1,23 @@
1
+ <%= form_tag spreadsheets_path(type: @type) do %>
2
+ <%= render "errors" %>
3
+
4
+ <h2>Choose worksheet</h2>
5
+
6
+ <% @spreadsheet.sheets.each_with_index do |sheet, index| %>
7
+ <table>
8
+ <tr>
9
+ <td>
10
+ <%= radio_button_tag 'default_sheet', index, index.zero? %>
11
+ </td>
12
+ <td>
13
+ <%= label_tag "default_sheet_#{index}", sheet.humanize %>
14
+ </td>
15
+ </tr>
16
+ </table>
17
+ <% end %>
18
+
19
+ <%= hidden_field_tag :spreadsheet_id, @spreadsheet.id %>
20
+ <%= hidden_field_tag :current_step, 'choose_worksheet' %>
21
+
22
+ <%= render "actions", :label => 'Continue' %>
23
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <% if @spreadsheet.errors.any? %>
2
+ <div id="error_explanation">
3
+ <h2><%= pluralize(@spreadsheet.errors.count, "error") %> prohibited this importable_spreadsheet from being saved:</h2>
4
+
5
+ <ul>
6
+ <% @spreadsheet.errors.full_messages.each do |msg| %>
7
+ <li><%= msg %></li>
8
+ <% end %>
9
+ </ul>
10
+ </div>
11
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= render "#{@spreadsheet.current_step}_step" %>
@@ -0,0 +1,12 @@
1
+ <%= form_tag spreadsheets_path(type: @type), enctype: 'multipart/form-data' do %>
2
+ <%= render "errors" %>
3
+
4
+ <h2>File upload</h2>
5
+
6
+ <p><%= label_tag :file, "Choose #{@type.humanize.downcase} spreadsheet file" %></p>
7
+ <p><%= file_field_tag :file %></p>
8
+
9
+ <%= hidden_field_tag :current_step, 'upload_file' %>
10
+
11
+ <%= render "actions", :label => 'Upload' %>
12
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <h1><%= @type.humanize %> spreadsheet upload</h1>
2
+
3
+ <%= render 'form' %>
@@ -0,0 +1,3 @@
1
+ <p>
2
+ <%= link_to "Import more #{params[:type].pluralize}", new_spreadsheet_path(type: params[:type]) %>
3
+ </p>
data/config/routes.rb ADDED
@@ -0,0 +1,16 @@
1
+ Importable::Engine.routes.draw do
2
+ match "/:type",
3
+ to: 'spreadsheets#new',
4
+ as: 'new_spreadsheet',
5
+ via: 'get'
6
+
7
+ match "/:type",
8
+ to: 'spreadsheets#create',
9
+ as: 'spreadsheets',
10
+ via: 'post'
11
+
12
+ match "/:type/:id",
13
+ to: 'spreadsheets#show',
14
+ as: 'spreadsheet',
15
+ via: 'get'
16
+ end
File without changes
@@ -0,0 +1,10 @@
1
+ class CreateImportableSpreadsheets < ActiveRecord::Migration
2
+ def change
3
+ create_table :importable_spreadsheets do |t|
4
+ t.string :file
5
+ t.string :object_type
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ module Importable
2
+ class Engine < Rails::Engine
3
+ isolate_namespace Importable
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Importable::Exceptions
2
+ class ParamRequiredError < StandardError; end
3
+ end
@@ -0,0 +1,40 @@
1
+ module Importable
2
+ class Mapper
3
+ attr_accessor :data
4
+ attr_accessor :invalid_objects
5
+
6
+ def initialize(data)
7
+ @raw_data = data
8
+ @invalid_objects = []
9
+
10
+ map_to_objects!
11
+ validate_objects!
12
+ save_objects!
13
+ end
14
+
15
+ def map_row
16
+ raise NotImplementedError.new('map_row method must be overriden by mapper')
17
+ end
18
+
19
+ def valid?
20
+ @invalid_objects.empty?
21
+ end
22
+
23
+ def map_to_objects!
24
+ @data = @raw_data.flat_map { |row| map_row(row) }
25
+ end
26
+
27
+ def save_objects!
28
+ if valid?
29
+ @data.each { |object| object.save! if object.new_record? }
30
+ end
31
+ end
32
+
33
+ def validate_objects!
34
+ @data.each_with_index do |object, index|
35
+ line_number = (index + 2)
36
+ @invalid_objects << [object, line_number] unless object.valid?
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ module MultiStep
2
+ module ImportHelpers
3
+ attr_writer :current_step
4
+
5
+ def steps
6
+ %w[ upload_file choose_worksheet import_data ]
7
+ end
8
+
9
+ def current_step
10
+ @current_step || steps.first
11
+ end
12
+
13
+ def next_step
14
+ distance = 1
15
+ # skip choose worksheet step if there's only 1 worksheet
16
+ if self.current_step == 'upload_file' and self.sheets.length <= 1
17
+ distance = 2
18
+ end
19
+ self.current_step = steps[steps.index(current_step) + distance]
20
+ end
21
+
22
+ def previous_step
23
+ distance = 1
24
+ # skip choose worksheet step if there's only 1 worksheet
25
+ if self.current_step == 'import_data' and self.sheets.length <= 1
26
+ distance = 2
27
+ end
28
+ self.current_step = steps[steps.index(current_step) - distance]
29
+ end
30
+
31
+ def first_step?
32
+ current_step == steps.first
33
+ end
34
+
35
+ def last_step?
36
+ current_step == steps.last
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ module Importable
2
+ class Uploader < CarrierWave::Uploader::Base
3
+ storage :file
4
+
5
+ def extension_white_list
6
+ %w(xls xlsx)
7
+ end
8
+
9
+ def store_dir
10
+ "#{Rails.root}/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.object_type}/#{model.id}"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module Importable
2
+ class Validator < ActiveModel::Validator
3
+ def validate(spreadsheet)
4
+ if spreadsheet.file.try(:current_path)
5
+ spreadsheet.mapper.invalid_objects.each do |object, line_number|
6
+ object.errors.messages.each do |error|
7
+ field, errors = *error
8
+ errors.each do |message|
9
+ spreadsheet.errors[field] << "#{message} (line #{line_number})"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Importable
2
+ VERSION = "0.0.1"
3
+ end
data/lib/importable.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'carrierwave'
2
+ require 'roo'
3
+
4
+ require "importable/engine"
5
+ require "importable/exceptions"
6
+ require "importable/mapper"
7
+ require "importable/uploader"
8
+ require "importable/validator"
9
+ require "importable/multi_step/import_helpers"
10
+
11
+ module Importable
12
+ end
File without changes
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ feature "Import spreadsheet" do
4
+ scenario "Import spreadsheet with a single worksheet" do
5
+ spreadsheet_file = support_file('foo_single_worksheet.xlsx')
6
+
7
+ visit '/importable/foo'
8
+ attach_file("Choose foo spreadsheet file", spreadsheet_file)
9
+ click_button "Upload"
10
+ page.should have_content "'Sheet1' of foo spreadsheet was successfully imported."
11
+ end
12
+
13
+ scenario "Import spreadsheet with multiple worksheets" do
14
+ spreadsheet_file = support_file('foo_multi_worksheet.xlsx')
15
+
16
+ visit '/importable/foo'
17
+ attach_file("Choose foo spreadsheet file", spreadsheet_file)
18
+ click_button "Upload"
19
+ page.should have_content "Choose worksheet"
20
+ choose "Sheet2"
21
+ click_button "Continue"
22
+ page.should have_content "'Sheet2' of foo spreadsheet was successfully imported."
23
+ end
24
+
25
+ scenario "Import invalid spreadsheet" do
26
+ spreadsheet_file = support_file('foo_required_field_invalid.xlsx')
27
+
28
+ visit '/importable/foo_required_field'
29
+ attach_file("Choose foo required field spreadsheet file", spreadsheet_file)
30
+ click_button "Upload"
31
+ page.should have_content "Doof can't be blank (line 3)"
32
+ end
33
+ end