importable_attachments 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +24 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +38 -0
- data/MIT-LICENSE +20 -0
- data/README.md +8 -0
- data/README.rdoc +3 -0
- data/Rakefile +29 -0
- data/app/assets/images/importable_attachments/.gitkeep +0 -0
- data/app/assets/images/importable_attachments/buttons/.htaccess +5 -0
- data/app/assets/images/importable_attachments/buttons/download_32.png +0 -0
- data/app/assets/images/importable_attachments/buttons/upload_32.png +0 -0
- data/app/assets/javascripts/importable_attachments/application.js +14 -0
- data/app/assets/javascripts/importable_attachments/attachments.coffee +41 -0
- data/app/assets/stylesheets/importable_attachments/application.css +14 -0
- data/app/assets/stylesheets/importable_attachments/attachments.css +4 -0
- data/app/assets/stylesheets/scaffold.css +56 -0
- data/app/controllers/importable_attachments/application_controller.rb +4 -0
- data/app/controllers/importable_attachments/attachments_controller.rb +190 -0
- data/app/controllers/importable_attachments/versions_controller.rb +87 -0
- data/app/helpers/importable_attachments/application_helper.rb +4 -0
- data/app/models/attachment.rb +24 -0
- data/app/models/importable_attachments/attachment.rb +143 -0
- data/app/models/importable_attachments/version.rb +50 -0
- data/app/validators/existing_class_validator.rb +17 -0
- data/app/validators/importable_attachments/csv_validator.rb +36 -0
- data/app/validators/importable_attachments/excel.rb +18 -0
- data/app/validators/importable_attachments/excel_validator.rb +18 -0
- data/app/views/importable_attachments/attachments/_attachment.html.haml +9 -0
- data/app/views/importable_attachments/attachments/_form.html.haml +22 -0
- data/app/views/importable_attachments/attachments/_nested_form.html.haml +20 -0
- data/app/views/importable_attachments/attachments/edit.html.haml +39 -0
- data/app/views/importable_attachments/attachments/index.html.haml +23 -0
- data/app/views/importable_attachments/attachments/index.xml.builder +23 -0
- data/app/views/importable_attachments/attachments/new.html.haml +10 -0
- data/app/views/importable_attachments/attachments/show.html.haml +43 -0
- data/app/views/importable_attachments/versions/_form.html.haml +25 -0
- data/app/views/importable_attachments/versions/edit.html.haml +7 -0
- data/app/views/importable_attachments/versions/index.html.haml +27 -0
- data/app/views/importable_attachments/versions/new.html.haml +5 -0
- data/app/views/importable_attachments/versions/show.html.haml +21 -0
- data/app/views/layouts/_version.html.haml +33 -0
- data/app/views/layouts/importable_attachments/application.html.haml +48 -0
- data/bin/set_lc.sh +47 -0
- data/config/database.yml +25 -0
- data/config/features/attachments.rb +8 -0
- data/config/features/mark_requirements.rb +3 -0
- data/config/features/smarter_dates.rb +3 -0
- data/config/features/versioning.rb +7 -0
- data/config/initializers/0_configuration.rb +7 -0
- data/config/initializers/formtastic.rb +76 -0
- data/config/initializers/generators.rb +10 -0
- data/config/initializers/paperclip.rb +27 -0
- data/config/locales/responders.en.yml +10 -0
- data/config/routes.rb +11 -0
- data/db/migrate/001_create_importable_attachments_versions.rb +14 -0
- data/db/migrate/100_create_attachments.rb +19 -0
- data/importable_attachments.gemspec +81 -0
- data/lib/generators/importable_attachments/install_generator.rb +66 -0
- data/lib/generators/importable_attachments/templates/features/attachments.rb.erb +7 -0
- data/lib/generators/importable_attachments/templates/features/versioning.rb +7 -0
- data/lib/generators/importable_attachments/templates/initializers/paperclip.rb +27 -0
- data/lib/importable_attachments/base.rb +108 -0
- data/lib/importable_attachments/blueprints.rb +9 -0
- data/lib/importable_attachments/engine.rb +8 -0
- data/lib/importable_attachments/importers/csv.rb +208 -0
- data/lib/importable_attachments/importers/excel.rb +37 -0
- data/lib/importable_attachments/importers.rb +7 -0
- data/lib/importable_attachments/version.rb +3 -0
- data/lib/importable_attachments.rb +9 -0
- data/lib/paperclip_processors/save_upload.rb +33 -0
- data/lib/tasks/importable_attachments_tasks.rake +4 -0
- data/script/rails +8 -0
- data/spec/attachments/books.csv +6 -0
- data/spec/attachments/books.txt +6 -0
- data/spec/attachments/books2.csv +4 -0
- data/spec/attachments/empty.csv +0 -0
- data/spec/attachments/failed_instances.csv +3 -0
- data/spec/attachments/invalid_headers.csv +3 -0
- data/spec/attachments/just_headers.csv +1 -0
- data/spec/attachments/mostly_empty.csv +2 -0
- data/spec/attachments/mostly_empty_copy.xls +0 -0
- data/spec/controllers/importable_attachments/attachments_controller_spec.rb +236 -0
- data/spec/controllers/importable_attachments/versions_controller_spec.rb +158 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -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/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/book.rb +13 -0
- data/spec/dummy/app/models/library.rb +54 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +65 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/0_configuration.rb +7 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -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 +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/101_create_libraries.rb +10 -0
- data/spec/dummy/db/migrate/102_create_books.rb +12 -0
- data/spec/dummy/db/schema.rb +57 -0
- data/spec/dummy/features/person_uploads_generic_file.feature +11 -0
- data/spec/dummy/features/step_definitions/person_uploads_generic_file_steps.rb +11 -0
- data/spec/dummy/features/step_definitions/web_steps.rb +211 -0
- data/spec/dummy/features/support/capybara.rb +6 -0
- data/spec/dummy/features/support/database_cleaner.rb +26 -0
- data/spec/dummy/features/support/developer_helpers.rb +47 -0
- data/spec/dummy/features/support/env.rb +53 -0
- data/spec/dummy/features/support/paths.rb +33 -0
- data/spec/dummy/features/support/poltergeist.rb +1 -0
- data/spec/dummy/features/support/selectors.rb +39 -0
- data/spec/dummy/features/support/transactional_fixtures.rb +14 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +38 -0
- data/spec/dummy/public/422.html +38 -0
- data/spec/dummy/public/500.html +36 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec/support/.gitkeep +0 -0
- data/spec/dummy/spec/support/paperclip.rb +1 -0
- data/spec/models/importable_attachments/attachment_spec.rb +177 -0
- data/spec/models/importable_attachments/library_spec.rb +155 -0
- data/spec/models/importable_attachments/version_spec.rb +25 -0
- data/spec/routing/importable_attachments/versions_routing_spec.rb +43 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +30 -0
- metadata +737 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
|
2
|
+
# It is recommended to regenerate this file in the future when you upgrade to a
|
|
3
|
+
# newer version of cucumber-rails. Consider adding your own code to a new file
|
|
4
|
+
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
|
5
|
+
# files.
|
|
6
|
+
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
|
|
9
|
+
if defined?(IRB) && IRB.class.to_s == 'Class'
|
|
10
|
+
Object.send(:remove_const, :IRB)
|
|
11
|
+
require 'IRB'
|
|
12
|
+
require 'IRB/init'
|
|
13
|
+
require 'IRB/context'
|
|
14
|
+
require 'IRB/extend-command'
|
|
15
|
+
require 'IRB/locale'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
; require 'ruby-debug'
|
|
20
|
+
rescue LoadError => err;
|
|
21
|
+
puts err;
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
begin
|
|
25
|
+
; require 'pry'
|
|
26
|
+
if defined?(IRB) && IRB.class.to_s == 'Module'
|
|
27
|
+
Object.send(:remove_const, :IRB)
|
|
28
|
+
IRB = Pry
|
|
29
|
+
end
|
|
30
|
+
rescue LoadError => err;
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
require 'cucumber/rails'
|
|
34
|
+
|
|
35
|
+
# By default, any exception happening in your Rails application will bubble up
|
|
36
|
+
# to Cucumber so that your scenario will fail. This is a different from how
|
|
37
|
+
# your application behaves in the production environment, where an error page will
|
|
38
|
+
# be rendered instead.
|
|
39
|
+
#
|
|
40
|
+
# Sometimes we want to override this default behaviour and allow Rails to rescue
|
|
41
|
+
# exceptions and display an error page (just like when the app is running in production).
|
|
42
|
+
# Typical scenarios where you want to do this is when you test your error pages.
|
|
43
|
+
# There are two ways to allow Rails to rescue exceptions:
|
|
44
|
+
#
|
|
45
|
+
# 1) Tag your scenario (or feature) with @allow-rescue
|
|
46
|
+
#
|
|
47
|
+
# 2) Set the value below to true. Beware that doing this globally is not
|
|
48
|
+
# recommended as it will mask a lot of errors for you!
|
|
49
|
+
#
|
|
50
|
+
ActionController::Base.allow_rescue = false
|
|
51
|
+
|
|
52
|
+
require 'cucumber/rails/world'
|
|
53
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module NavigationHelpers
|
|
2
|
+
# Maps a name to a path. Used by the
|
|
3
|
+
#
|
|
4
|
+
# When /^I go to (.+)$/ do |page_name|
|
|
5
|
+
#
|
|
6
|
+
# step definition in web_steps.rb
|
|
7
|
+
#
|
|
8
|
+
def path_to(page_name)
|
|
9
|
+
case page_name
|
|
10
|
+
|
|
11
|
+
when /^the home\s?page$/
|
|
12
|
+
'/'
|
|
13
|
+
|
|
14
|
+
# Add more mappings here.
|
|
15
|
+
# Here is an example that pulls values out of the Regexp:
|
|
16
|
+
#
|
|
17
|
+
# when /^(.*)'s profile page$/i
|
|
18
|
+
# user_profile_path(User.find_by_login($1))
|
|
19
|
+
|
|
20
|
+
else
|
|
21
|
+
begin
|
|
22
|
+
page_name =~ /^the (.*) page$/
|
|
23
|
+
path_components = $1.split(/\s+/)
|
|
24
|
+
self.send(path_components.push('path').join('_').to_sym)
|
|
25
|
+
rescue NoMethodError, ArgumentError
|
|
26
|
+
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
|
27
|
+
"Now, go and add a mapping in #{__FILE__}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
World(NavigationHelpers)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Capybara.javascript_driver = :poltergeist
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module HtmlSelectorsHelpers
|
|
2
|
+
# Maps a name to a selector. Used primarily by the
|
|
3
|
+
#
|
|
4
|
+
# When /^(.+) within (.+)$/ do |step, scope|
|
|
5
|
+
#
|
|
6
|
+
# step definitions in web_steps.rb
|
|
7
|
+
#
|
|
8
|
+
def selector_for(locator)
|
|
9
|
+
case locator
|
|
10
|
+
|
|
11
|
+
when "the page"
|
|
12
|
+
"html > body"
|
|
13
|
+
|
|
14
|
+
# Add more mappings here.
|
|
15
|
+
# Here is an example that pulls values out of the Regexp:
|
|
16
|
+
#
|
|
17
|
+
# when /^the (notice|error|info) flash$/
|
|
18
|
+
# ".flash.#{$1}"
|
|
19
|
+
|
|
20
|
+
# You can also return an array to use a different selector
|
|
21
|
+
# type, like:
|
|
22
|
+
#
|
|
23
|
+
# when /the header/
|
|
24
|
+
# [:xpath, "//header"]
|
|
25
|
+
|
|
26
|
+
# This allows you to provide a quoted selector as the scope
|
|
27
|
+
# for "within" steps as was previously the default for the
|
|
28
|
+
# web steps:
|
|
29
|
+
when /^"(.+)"$/
|
|
30
|
+
$1
|
|
31
|
+
|
|
32
|
+
else
|
|
33
|
+
raise "Can't find mapping from \"#{locator}\" to a selector.\n" +
|
|
34
|
+
"Now, go and add a mapping in #{__FILE__}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
World(HtmlSelectorsHelpers)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# If you set this to true, each scenario will run in a database transaction.
|
|
2
|
+
# You can still turn off transactions on a per-scenario basis, simply tagging
|
|
3
|
+
# a feature or scenario with the @no-txn tag. If you are using Capybara,
|
|
4
|
+
# tagging with @culerity or @javascript will also turn transactions off.
|
|
5
|
+
#
|
|
6
|
+
# If you set this to false, transactions will be off for all scenarios,
|
|
7
|
+
# regardless of whether you use @no-txn or not.
|
|
8
|
+
#
|
|
9
|
+
# Beware that turning transactions off will leave data in your database
|
|
10
|
+
# after each scenario, which can lead to hard-to-debug failures in
|
|
11
|
+
# subsequent scenarios. If you do this, we recommend you create a Before
|
|
12
|
+
# block that will explicitly put your database in a known state.
|
|
13
|
+
Cucumber::Rails::World.use_transactional_fixtures = true
|
|
14
|
+
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>The page you were looking for doesn't exist (404)</title>
|
|
5
|
+
<style type="text/css">
|
|
6
|
+
body {
|
|
7
|
+
background-color: #fff;
|
|
8
|
+
color: #666;
|
|
9
|
+
text-align: center;
|
|
10
|
+
font-family: arial, sans-serif;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
div.dialog {
|
|
14
|
+
width: 25em;
|
|
15
|
+
padding: 0 4em;
|
|
16
|
+
margin: 4em auto 0 auto;
|
|
17
|
+
border: 1px solid #ccc;
|
|
18
|
+
border-right-color: #999;
|
|
19
|
+
border-bottom-color: #999;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
h1 {
|
|
23
|
+
font-size: 100%;
|
|
24
|
+
color: #f00;
|
|
25
|
+
line-height: 1.5em;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
28
|
+
</head>
|
|
29
|
+
|
|
30
|
+
<body>
|
|
31
|
+
<!-- This file lives in public/404.html -->
|
|
32
|
+
<div class="dialog">
|
|
33
|
+
<h1>The page you were looking for doesn't exist.</h1>
|
|
34
|
+
|
|
35
|
+
<p>You may have mistyped the address or the page may have moved.</p>
|
|
36
|
+
</div>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>The change you wanted was rejected (422)</title>
|
|
5
|
+
<style type="text/css">
|
|
6
|
+
body {
|
|
7
|
+
background-color: #fff;
|
|
8
|
+
color: #666;
|
|
9
|
+
text-align: center;
|
|
10
|
+
font-family: arial, sans-serif;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
div.dialog {
|
|
14
|
+
width: 25em;
|
|
15
|
+
padding: 0 4em;
|
|
16
|
+
margin: 4em auto 0 auto;
|
|
17
|
+
border: 1px solid #ccc;
|
|
18
|
+
border-right-color: #999;
|
|
19
|
+
border-bottom-color: #999;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
h1 {
|
|
23
|
+
font-size: 100%;
|
|
24
|
+
color: #f00;
|
|
25
|
+
line-height: 1.5em;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
28
|
+
</head>
|
|
29
|
+
|
|
30
|
+
<body>
|
|
31
|
+
<!-- This file lives in public/422.html -->
|
|
32
|
+
<div class="dialog">
|
|
33
|
+
<h1>The change you wanted was rejected.</h1>
|
|
34
|
+
|
|
35
|
+
<p>Maybe you tried to change something you didn't have access to.</p>
|
|
36
|
+
</div>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>We're sorry, but something went wrong (500)</title>
|
|
5
|
+
<style type="text/css">
|
|
6
|
+
body {
|
|
7
|
+
background-color: #fff;
|
|
8
|
+
color: #666;
|
|
9
|
+
text-align: center;
|
|
10
|
+
font-family: arial, sans-serif;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
div.dialog {
|
|
14
|
+
width: 25em;
|
|
15
|
+
padding: 0 4em;
|
|
16
|
+
margin: 4em auto 0 auto;
|
|
17
|
+
border: 1px solid #ccc;
|
|
18
|
+
border-right-color: #999;
|
|
19
|
+
border-bottom-color: #999;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
h1 {
|
|
23
|
+
font-size: 100%;
|
|
24
|
+
color: #f00;
|
|
25
|
+
line-height: 1.5em;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
28
|
+
</head>
|
|
29
|
+
|
|
30
|
+
<body>
|
|
31
|
+
<!-- This file lives in public/500.html -->
|
|
32
|
+
<div class="dialog">
|
|
33
|
+
<h1>We're sorry, but something went wrong.</h1>
|
|
34
|
+
</div>
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
File without changes
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
|
3
|
+
|
|
4
|
+
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
|
5
|
+
require File.expand_path('../../config/boot', __FILE__)
|
|
6
|
+
require 'rails/commands'
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'paperclip/matchers'
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
|
2
|
+
|
|
3
|
+
describe ImportableAttachments::Attachment do
|
|
4
|
+
# :call-seq:
|
|
5
|
+
# mock_io_stream [:opts]
|
|
6
|
+
#
|
|
7
|
+
# yields a mock Paperclip::Attachment object
|
|
8
|
+
|
|
9
|
+
def mock_io_stream(opts = {})
|
|
10
|
+
attached_to = opts[:attach_to]
|
|
11
|
+
@io_stream = Paperclip::Attachment.new(:io_stream, attached_to,
|
|
12
|
+
{path: ::Configuration.for('attachments').path,
|
|
13
|
+
preserve_files: true, processors: [:save_upload]})
|
|
14
|
+
spec_dir = File.dirname(@io_stream.path).sub(/(?:\/\.?)?$/, "")
|
|
15
|
+
FileUtils.mkdir_p spec_dir unless File.directory? spec_dir
|
|
16
|
+
@io_stream
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# :call-seq:
|
|
20
|
+
# mock_attachment [:opts]
|
|
21
|
+
#
|
|
22
|
+
# yields a mock Attachment object
|
|
23
|
+
|
|
24
|
+
def mock_attachment(opts = {})
|
|
25
|
+
lopts = {id: 27,
|
|
26
|
+
io_stream: nil,
|
|
27
|
+
io_stream_file_name: 'mostly_empty.csv',
|
|
28
|
+
io_stream_content_type: 'Test Content Type',
|
|
29
|
+
io_stream_file_size: 1,
|
|
30
|
+
io_stream_updated_at: DateTime.now,
|
|
31
|
+
revision_number: 1,
|
|
32
|
+
attachable_type: nil, attachable_id: nil, version: 1}
|
|
33
|
+
@attachment = mock_model(ImportableAttachments::Attachment, lopts.merge(opts))
|
|
34
|
+
@attachment.stubs(:io_stream).returns(mock_io_stream(:attach_to => @attachment))
|
|
35
|
+
FileUtils.cp @spec_file, @attachment.io_stream.path
|
|
36
|
+
@attachment
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
before :each do
|
|
40
|
+
#ActiveRecord::Base.send(:subclasses).each{|m|m.destroy_all}
|
|
41
|
+
@spec_file = Rails.root.join('spec', 'attachments', 'mostly_empty.csv')
|
|
42
|
+
|
|
43
|
+
mock_attachment
|
|
44
|
+
#Attachment.stubs(:find).with(@attachment.id.to_s).returns(@attachment)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context 'validations' do
|
|
48
|
+
it { should have_valid(:attachable_id).when(nil, 1, 2^64-1) }
|
|
49
|
+
it { should_not have_valid(:attachable_id).when(-1, 1.1) }
|
|
50
|
+
|
|
51
|
+
it { should have_valid(:attachable_type).when(nil, 'ImportableAttachments::Attachment') }
|
|
52
|
+
it { should_not have_valid(:attachable_type).when("Test \x0 data") }
|
|
53
|
+
|
|
54
|
+
it { should have_valid(:io_stream_file_name).when('Test data') }
|
|
55
|
+
it { should_not have_valid(:io_stream_file_name).when(nil, "Test \x0 data") }
|
|
56
|
+
|
|
57
|
+
it { should have_valid(:io_stream_content_type).when('Test data') }
|
|
58
|
+
it { should_not have_valid(:io_stream_content_type).when(nil, "Test \x0 data") }
|
|
59
|
+
|
|
60
|
+
it { should have_valid(:io_stream_updated_at).when(nil, Date.today) }
|
|
61
|
+
#it { should_not have_valid(:io_stream_updated_at).when("Test \x0 data") }
|
|
62
|
+
|
|
63
|
+
it 'should ensure the attachment exists' do
|
|
64
|
+
should validate_attachment_presence(:io_stream)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'should ensure the attachment is non-empty' do
|
|
68
|
+
should validate_attachment_size(:io_stream).greater_than(0)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'should require an :io_stream_file_name' do
|
|
72
|
+
attachment = ImportableAttachments::Attachment.new :io_stream => File.new(@spec_file, 'rb')
|
|
73
|
+
attachment.io_stream_file_name = nil
|
|
74
|
+
attachment.should_not be_valid
|
|
75
|
+
attachment.should have(1).error_on(:io_stream_file_name)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'should not allow odd characters in :io_stream_file_name' do
|
|
79
|
+
attachment = ImportableAttachments::Attachment.new :io_stream => File.new(@spec_file, 'rb')
|
|
80
|
+
attachment.io_stream_file_name = "file_path\b\b\b\b\bpath"
|
|
81
|
+
attachment.should_not be_valid
|
|
82
|
+
attachment.should have(1).error_on(:io_stream_file_name)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context 'expected behavior:' do
|
|
87
|
+
it 'should provide an :io_stream_content_type' do
|
|
88
|
+
attachment = ImportableAttachments::Attachment.new :io_stream => File.new(@spec_file, 'rb')
|
|
89
|
+
attachment.should be_valid
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'should save the file to the filesystem' do
|
|
93
|
+
attachment = ImportableAttachments::Attachment.new :io_stream => File.new(@spec_file, 'rb')
|
|
94
|
+
attachment.save!
|
|
95
|
+
File.exist?(attachment.io_stream.path).should be_true
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'should identify an unsaved file as nil' do
|
|
99
|
+
attachment = ImportableAttachments::Attachment.new :io_stream => File.new(@spec_file, 'rb')
|
|
100
|
+
attachment.magic_mime_type.should be_nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'should identify the file mime_type as CSV' do
|
|
104
|
+
attachment = ImportableAttachments::Attachment.create! :io_stream => File.new(@spec_file, 'rb')
|
|
105
|
+
attachment.magic_mime_type.should == 'text/plain'
|
|
106
|
+
attachment.io_stream.content_type.should == 'text/csv'
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'should provide an :io_stream_mime_type' do
|
|
110
|
+
attachment = ImportableAttachments::Attachment.new :io_stream => File.new(@spec_file, 'rb')
|
|
111
|
+
attachment.io_stream_mime_type.should be_a(MIME::Type)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'should save multiple versions of the file to the filesystem' do
|
|
115
|
+
with_versioning do
|
|
116
|
+
spec_file = Rails.root.join('spec', 'attachments', 'mostly_empty.csv')
|
|
117
|
+
attachment1 = ImportableAttachments::Attachment.new :io_stream => File.new(spec_file, 'rb')
|
|
118
|
+
attachment1.save!
|
|
119
|
+
File.exist?(attachment1.io_stream.path).should be_true
|
|
120
|
+
if ::Configuration.for('attachments').include_revision_in_filename
|
|
121
|
+
attachment1.io_stream.path.should match /mostly_empty\.1\.csv$/
|
|
122
|
+
else
|
|
123
|
+
attachment1.io_stream.path.should match /mostly_empty\.csv$/
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
#attachment1.io_stream = File.new(@spec_file,'rb')
|
|
127
|
+
ImportableAttachments::Attachment.stubs(:find).with(attachment1.id).returns(attachment1)
|
|
128
|
+
attachment2 = ImportableAttachments::Attachment.find(attachment1.id)
|
|
129
|
+
spec_file = Rails.root.join('spec', 'attachments', 'mostly_empty.csv')
|
|
130
|
+
attachment2.update_attributes :io_stream => File.new(spec_file, 'rb')
|
|
131
|
+
File.exist?(attachment2.io_stream.path).should be_true
|
|
132
|
+
if ::Configuration.for('attachments').include_revision_in_filename
|
|
133
|
+
attachment2.io_stream.path.should match /mostly_empty\.2\.csv$/
|
|
134
|
+
else
|
|
135
|
+
attachment1.io_stream.path.should match /mostly_empty\.csv$/
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'should yield a revision_number' do
|
|
141
|
+
with_versioning do
|
|
142
|
+
spec_file = Rails.root.join('spec', 'attachments', 'mostly_empty.csv')
|
|
143
|
+
attachment1 = ImportableAttachments::Attachment.new :io_stream => File.new(spec_file, 'rb')
|
|
144
|
+
attachment1.save!
|
|
145
|
+
attachment1.revision_number.should == 1
|
|
146
|
+
|
|
147
|
+
#attachment1.io_stream = File.new(@spec_file,'rb')
|
|
148
|
+
ImportableAttachments::Attachment.stubs(:find).with(attachment1.id).returns(attachment1)
|
|
149
|
+
attachment2 = ImportableAttachments::Attachment.find(attachment1.id)
|
|
150
|
+
spec_file = Rails.root.join('spec', 'attachments', 'mostly_empty_copy.xls')
|
|
151
|
+
attachment2.update_attributes :io_stream => File.new(spec_file, 'rb')
|
|
152
|
+
attachment2.revision_number.should == 2
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# == Schema Information
|
|
159
|
+
#
|
|
160
|
+
# Table name: attachments
|
|
161
|
+
#
|
|
162
|
+
# id :integer not null, primary key
|
|
163
|
+
# attachable_type :string(255)
|
|
164
|
+
# attachable_id :string(255)
|
|
165
|
+
# io_stream_file_name :string(255)
|
|
166
|
+
# io_stream_content_type :string(255)
|
|
167
|
+
# io_stream_file_size :integer
|
|
168
|
+
# io_stream_updated_at :datetime
|
|
169
|
+
# created_at :datetime not null
|
|
170
|
+
# updated_at :datetime not null
|
|
171
|
+
#
|
|
172
|
+
# Indexes
|
|
173
|
+
#
|
|
174
|
+
# idx_importable_attachments_on_attachable_type_and_id (attachable_type,attachable_id)
|
|
175
|
+
# index_attachments_on_io_stream_file_name (io_stream_file_name)
|
|
176
|
+
#
|
|
177
|
+
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
def spec_file(file_name)
|
|
4
|
+
Rails.root.join('spec', 'attachments', file_name)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
describe Library do
|
|
8
|
+
before :each do
|
|
9
|
+
@spec_file = spec_file('books.csv')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context 'attachment validations' do
|
|
13
|
+
subject { Library.create(name: 'XYZ Library', address: '123 Main St.') }
|
|
14
|
+
it { subject.should have_valid(:attachment).when(nil, Attachment.new(io_stream: File.new(@spec_file))) }
|
|
15
|
+
it {
|
|
16
|
+
subject.should_not have_valid(:attachment).when(Attachment.new io_stream: File.new(Rails.root.join('spec', 'attachments', 'books.txt'))) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context 'importing attachments' do
|
|
20
|
+
before :each do
|
|
21
|
+
@attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
subject { Library.create(name: 'XYZ Library', address: '123 Main St.') }
|
|
25
|
+
|
|
26
|
+
context 'in the right format' do
|
|
27
|
+
it 'should optionally allow an attachment' do
|
|
28
|
+
subject.attachment = @attachment
|
|
29
|
+
subject.attachment.should_not be_nil
|
|
30
|
+
subject.attachment.attachable_type.should == 'Library'
|
|
31
|
+
subject.attachment.io_stream_file_name.should == File.basename(@spec_file)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'should import a spreadsheet if it is in the right format' do
|
|
35
|
+
subject.books.should be_empty
|
|
36
|
+
lambda {
|
|
37
|
+
subject.attachment = @attachment
|
|
38
|
+
}.should change(subject.books, :count).by(5)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context 'wrong extension' do
|
|
43
|
+
before :each do
|
|
44
|
+
@spec_file = spec_file('books.txt')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'should not import a spreadsheet if it has the wrong extension' do
|
|
48
|
+
attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
49
|
+
subject.attachment = attachment
|
|
50
|
+
subject.should_not be_valid
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'should not delete existing data if new upload has the wrong extension' do
|
|
54
|
+
subject.attachment = @attachment
|
|
55
|
+
attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
56
|
+
lambda {
|
|
57
|
+
subject.attachment = attachment
|
|
58
|
+
}.should_not change(subject.books, :count)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'should not register / acknowledge the file is still in the system' do
|
|
62
|
+
subject.attachment = @attachment
|
|
63
|
+
attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
64
|
+
lambda {
|
|
65
|
+
subject.attachment = attachment
|
|
66
|
+
}.should_not change(subject.attachment, :io_stream_file_name)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'invalid headers' do
|
|
71
|
+
before :each do
|
|
72
|
+
@spec_file = spec_file('invalid_headers.csv')
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'should not import a spreadsheet if it has invalid headers' do
|
|
76
|
+
attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
77
|
+
subject.attachment = attachment
|
|
78
|
+
subject.should_not be_valid
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'should not delete existing data if new upload has invalid headers' do
|
|
82
|
+
subject.attachment = @attachment
|
|
83
|
+
attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
84
|
+
lambda {
|
|
85
|
+
subject.attachment = attachment
|
|
86
|
+
}.should_not change(subject.books, :count)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'should not register / acknowledge the file is still in the system' do
|
|
90
|
+
subject.attachment = @attachment
|
|
91
|
+
attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
92
|
+
lambda {
|
|
93
|
+
subject.attachment = attachment
|
|
94
|
+
}.should_not change(subject.attachment, :io_stream_file_name)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context 'failed instances' do
|
|
99
|
+
before :each do
|
|
100
|
+
@spec_file = spec_file('failed_instances.csv')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'should not import a spreadsheet if it has failed instances' do
|
|
104
|
+
attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
105
|
+
subject.attachment = attachment
|
|
106
|
+
subject.should_not be_valid
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'should not delete existing data if new upload has failed instances' do
|
|
110
|
+
subject.attachment = @attachment
|
|
111
|
+
attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
112
|
+
lambda {
|
|
113
|
+
subject.attachment = attachment
|
|
114
|
+
}.should_not change(subject.books, :count)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'should not register / acknowledge the file is still in the system' do
|
|
118
|
+
subject.attachment = @attachment
|
|
119
|
+
attachment = Attachment.new io_stream: File.new(@spec_file, 'rb')
|
|
120
|
+
lambda {
|
|
121
|
+
subject.attachment = attachment
|
|
122
|
+
}.should_not change(subject.attachment, :io_stream_file_name)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context 'updating when an attachment already exists with destructive import' do
|
|
127
|
+
it 'should delete the rows from old file and replace them with rows from new file' do
|
|
128
|
+
subject.attachment = @attachment
|
|
129
|
+
attachment = Attachment.new io_stream: File.new(spec_file('books2.csv'), 'rb')
|
|
130
|
+
lambda {
|
|
131
|
+
subject.attachment = attachment
|
|
132
|
+
}.should change(subject.books, :count).by(-2)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
context 'assigning attachment to new record' do
|
|
137
|
+
subject { Library.new(name: 'XYZ Library', address: '123 Main St.') }
|
|
138
|
+
|
|
139
|
+
it 'should not import when the record is not persisted' do
|
|
140
|
+
lambda {
|
|
141
|
+
subject.attachment = @attachment
|
|
142
|
+
}.should_not change(subject.books, :count)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'should import upon saving the record' do
|
|
146
|
+
lambda {
|
|
147
|
+
subject.attachment = @attachment
|
|
148
|
+
subject.save
|
|
149
|
+
}.should change(subject.books, :count).by(5)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
end
|
|
155
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ImportableAttachments::Version do
|
|
4
|
+
# :call-seq:
|
|
5
|
+
# mock_version [:opts]
|
|
6
|
+
#
|
|
7
|
+
# yields a mock Version object
|
|
8
|
+
|
|
9
|
+
def mock_version(opts = {})
|
|
10
|
+
lopts = {:id => 27, :item_type => nil, :item_id => nil}
|
|
11
|
+
@version = mock_model(Version, lopts.merge(opts))
|
|
12
|
+
@version
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
before :each do
|
|
16
|
+
mock_version
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context 'validations' do
|
|
20
|
+
it { should have_valid(:item_id).when(nil, 1, 'uniqueId', 'unique_id', 'unique.id') }
|
|
21
|
+
|
|
22
|
+
it { should have_valid(:item_type).when(nil, 'Attachment', 'ImportableAttachments::Attachment') }
|
|
23
|
+
it { should_not have_valid(:item_type).when("Test \x0 data") }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module ImportableAttachments
|
|
4
|
+
describe VersionsController do
|
|
5
|
+
context 'routing' do
|
|
6
|
+
|
|
7
|
+
before :each do
|
|
8
|
+
@routes = ::ImportableAttachments::Engine.routes
|
|
9
|
+
|
|
10
|
+
# https://github.com/rspec/rspec-rails/pull/539
|
|
11
|
+
assertion_instance.instance_variable_set(:@routes, ::ImportableAttachments::Engine.routes)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'routes to #index' do
|
|
15
|
+
get('/versions').should route_to('importable_attachments/versions#index')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'routes to #new' do
|
|
19
|
+
get('/versions/new').should route_to('importable_attachments/versions#new')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'routes to #show' do
|
|
23
|
+
get('/versions/1').should route_to('importable_attachments/versions#show', :id => '1')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'routes to #edit' do
|
|
27
|
+
get('/versions/1/edit').should route_to('importable_attachments/versions#edit', :id => '1')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'routes to #create' do
|
|
31
|
+
post('/versions').should route_to('importable_attachments/versions#create')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'routes to #update' do
|
|
35
|
+
put('/versions/1').should route_to('importable_attachments/versions#update', :id => '1')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'routes to #destroy' do
|
|
39
|
+
delete('/versions/1').should route_to('importable_attachments/versions#destroy', :id => '1')
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|