importable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +37 -0
- data/app/assets/javascripts/importable/application.js +7 -0
- data/app/assets/javascripts/importable/spreadsheets.js +2 -0
- data/app/assets/stylesheets/importable/application.css +7 -0
- data/app/assets/stylesheets/importable/spreadsheets.css +4 -0
- data/app/controllers/importable/application_controller.rb +4 -0
- data/app/controllers/importable/spreadsheets_controller.rb +74 -0
- data/app/models/importable/spreadsheet.rb +88 -0
- data/app/views/importable/spreadsheets/_actions.erb +6 -0
- data/app/views/importable/spreadsheets/_choose_worksheet_step.erb +23 -0
- data/app/views/importable/spreadsheets/_errors.erb +11 -0
- data/app/views/importable/spreadsheets/_form.html.erb +1 -0
- data/app/views/importable/spreadsheets/_upload_file_step.erb +12 -0
- data/app/views/importable/spreadsheets/new.html.erb +3 -0
- data/app/views/importable/spreadsheets/show.html.erb +3 -0
- data/config/routes.rb +16 -0
- data/db/development.sqlite3 +0 -0
- data/db/migrate/20110915001957_create_importable_spreadsheets.rb +10 -0
- data/lib/importable/engine.rb +5 -0
- data/lib/importable/exceptions.rb +3 -0
- data/lib/importable/mapper.rb +40 -0
- data/lib/importable/multi_step/import_helpers.rb +39 -0
- data/lib/importable/uploader.rb +13 -0
- data/lib/importable/validator.rb +16 -0
- data/lib/importable/version.rb +3 -0
- data/lib/importable.rb +12 -0
- data/lib/tasks/importable_tasks.rake +0 -0
- data/spec/acceptance/import_spreadsheet_spec.rb +33 -0
- data/spec/controllers/importable/spreadsheets_controller_spec.rb +152 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +7 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/imports/foo_mapper.rb +5 -0
- data/spec/dummy/app/imports/foo_required_field_mapper.rb +5 -0
- data/spec/dummy/app/imports/plural_widgets_mapper.rb +5 -0
- data/spec/dummy/app/imports/singular_widget_mapper.rb +5 -0
- data/spec/dummy/app/models/foo.rb +2 -0
- data/spec/dummy/app/models/foo_required_field.rb +3 -0
- data/spec/dummy/app/views/layouts/importable/application.html.erb +18 -0
- data/spec/dummy/config/application.rb +45 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +11 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20110913175340_create_foo_required_fields.rb +10 -0
- data/spec/dummy/db/migrate/20110913175348_create_foos.rb +20 -0
- data/spec/dummy/db/schema.rb +45 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +1440 -0
- data/spec/dummy/log/test.log +12899 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/tmp/cache/assets/C84/C40/sprockets%2F7f0912160518f10045d9e488fc7436af +0 -0
- data/spec/dummy/tmp/cache/assets/C92/520/sprockets%2F22333678e07cb97428c92fe9a9f33283 +0 -0
- data/spec/dummy/tmp/cache/assets/CAD/170/sprockets%2F96fb203c710450777e6c21c337e5a9d3 +0 -0
- data/spec/dummy/tmp/cache/assets/CC8/800/sprockets%2F804f348fd27bfb7555b2e9977494612e +0 -0
- data/spec/dummy/tmp/cache/assets/CEC/F40/sprockets%2Feef825d9227d8044ea22a9ce75328574 +0 -0
- data/spec/dummy/tmp/cache/assets/D0F/F30/sprockets%2F1c0c77bdf759cb307e0d9a071367476b +0 -0
- data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/D4D/A30/sprockets%2F256edbd7ad8e5d6e144e648e82150b89 +0 -0
- data/spec/dummy/tmp/cache/assets/D4F/9D0/sprockets%2Fd1cf18a7bc4837a9f12a55e76608f79e +0 -0
- data/spec/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +0 -0
- data/spec/dummy/tmp/cache/assets/D7B/2D0/sprockets%2F67457fb6359b5cc7faf0ad1e432996db +0 -0
- data/spec/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
- data/spec/dummy/tmp/cache/assets/DAC/950/sprockets%2Fbc53a3fdde9e5350798e72aa7f78ab28 +0 -0
- data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/capybara/capybara-201109142003362645406331.html +14 -0
- data/spec/dummy/tmp/capybara/capybara-201109142004009667888038.html +14 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/503/foo_single_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/504/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/505/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/506/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/507/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/508/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/509/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/510/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/511/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/512/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/513/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/514/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/515/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/516/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/517/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/518/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/519/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/520/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/521/foo_single_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/522/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/523/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/524/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/525/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/526/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/527/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/528/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/529/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/530/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/531/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/532/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/533/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/534/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/535/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/536/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/537/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/538/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/539/foo_single_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/540/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/541/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/542/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/543/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/544/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/545/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/546/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/547/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/548/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/549/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/550/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/551/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/552/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/553/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/554/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/555/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/556/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/557/foo_single_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/558/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/559/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/560/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/561/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/562/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/563/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/564/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/565/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/566/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/567/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/568/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/569/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/570/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/571/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/572/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/573/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/574/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/575/foo_single_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/576/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/577/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/578/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/579/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/580/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/581/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/582/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/583/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/584/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/585/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/586/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/587/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/588/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/589/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/590/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/591/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/592/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/593/foo_single_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/594/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/595/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/596/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/597/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/598/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/599/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/600/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/601/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/602/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/603/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/604/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/605/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/606/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/607/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/608/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/609/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/610/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/611/foo_single_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/612/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/613/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/614/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/615/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/616/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/617/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/618/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/619/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/620/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/621/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/622/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/623/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/624/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/625/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/626/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/627/foo_multi_worksheet.xlsx +0 -0
- data/spec/dummy/uploads/importable/spreadsheet/file/foo/628/foo_multi_worksheet.xlsx +0 -0
- data/spec/lib/mapper_spec.rb +70 -0
- data/spec/lib/multi_step/import_helpers_spec.rb +94 -0
- data/spec/lib/uploader_spec.rb +28 -0
- data/spec/lib/validator_spec.rb +29 -0
- data/spec/models/spreadsheet_spec.rb +149 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/foo_multi_worksheet.xls +0 -0
- data/spec/support/foo_multi_worksheet.xlsx +0 -0
- data/spec/support/foo_required_field_invalid.xlsx +0 -0
- data/spec/support/foo_required_field_valid.xlsx +0 -0
- data/spec/support/foo_single_worksheet.xls +0 -0
- data/spec/support/foo_single_worksheet.xlsx +0 -0
- data/spec/support/text.txt +1 -0
- 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
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,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,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,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 %>
|
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,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
|
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
|