Commit 237597eb by Mike Mondragon

Merge remote branch 'upstream/master'

Conflicts:
	lib/paperclip/upfile.rb
	paperclip.gemspec
parents 3eb7b902 cbfaca48
......@@ -3,3 +3,9 @@
tmp
test/s3.yml
public
paperclip*.gem
capybara*.html
*.rbc
.bundle
*SPIKE*
.rvmrc
rvm:
- 1.8.7
- 1.9.2
- ree
- rbx-2.0
script: "bundle exec rake clean test"
gemfile:
- gemfiles/rails2.gemfile
- gemfiles/rails3.gemfile
- gemfiles/rails3_1.gemfile
appraise "rails2" do
gem "rails", "~> 2.3.12"
end
appraise "rails3" do
gem "rails", "~> 3.0.9"
end
appraise "rails3_1" do
gem "rails", "~> 3.1.0.rc5"
end
We love pull requests. Here's a quick guide:
1. Fork the repo.
2. Run the tests. We only take pull requests with passing tests, and it's great
to know that you have a clean slate: `bundle && rake`
3. Add a test for your change. Only refactoring and documentation changes
require no new tests. If you are adding functionality or fixing a bug, we need
a test!
4. Make the test pass.
5. Push to your fork and submit a pull request.
At this point you're waiting on us. We like to at least comment on, if not
accept, pull requests within three business days (and, typically, one business
day). We may suggest some changes or improvements or alternatives.
Some things that will increase the chance that your pull request is accepted,
taken straight from the Ruby on Rails guide:
* Use Rails idioms and helpers
* Include tests that fail without your code, and pass with it
* Update the documentation, the surrounding one, examples elsewhere, guides,
whatever is affected by your contribution
Syntax:
* Two spaces, no tabs.
* No trailing whitespace. Blank lines should not have any space.
* Prefer &&/|| over and/or.
* MyClass.my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
* a = b and not a=b.
* Follow the conventions you see used in the source already.
And in case we didn't emphasize it enough: we love tests!
source "http://rubygems.org"
gem "activerecord", :require => "active_record"
gem "appraisal"
gem "aws-s3", :require => "aws/s3"
gem "bundler"
gem "cocaine", "~>0.2"
gem "fog"
gem "jruby-openssl", :platform => :jruby
gem "mime-types"
gem "mocha"
gem "rake"
gem "rdoc", :require => false
gem "shoulda"
gem "sqlite3", "~>1.3.4"
# This is for Rails 3.1
gem "sprockets", "~> 2.0.0.beta.13", :require => false
# gem "ruby-debug", :platform => :ruby_18
# gem "ruby-debug19", :platform => :ruby_19
GEM
remote: http://rubygems.org/
specs:
activerecord (2.3.12)
activesupport (= 2.3.12)
activesupport (2.3.12)
appraisal (0.3.5)
aruba (~> 0.3.6)
bundler
rake
aruba (0.3.7)
childprocess (>= 0.1.9)
cucumber (>= 0.10.5)
rspec (>= 2.6.0)
aws-s3 (0.6.2)
builder
mime-types
xml-simple
bouncy-castle-java (1.5.0146.1)
builder (3.0.0)
childprocess (0.1.9)
ffi (~> 1.0.6)
cocaine (0.2.0)
cucumber (0.10.5)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (~> 2.4.0)
json (>= 1.4.6)
term-ansicolor (>= 1.0.5)
diff-lcs (1.1.2)
excon (0.6.3)
ffi (1.0.9)
ffi (1.0.9-java)
fog (0.8.2)
builder
excon (~> 0.6.1)
formatador (>= 0.1.3)
json
mime-types
net-ssh (>= 2.1.3)
nokogiri (>= 1.4.4)
ruby-hmac
formatador (0.1.4)
gherkin (2.4.5)
json (>= 1.4.6)
gherkin (2.4.5-java)
json (>= 1.4.6)
hike (1.2.0)
jruby-openssl (0.7.4)
bouncy-castle-java
json (1.5.3)
json (1.5.3-java)
mime-types (1.16)
mocha (0.9.12)
net-ssh (2.1.4)
nokogiri (1.4.4)
nokogiri (1.4.4-java)
weakling (>= 0.0.3)
rack (1.3.2)
rake (0.9.2)
rdoc (3.8)
rspec (2.6.0)
rspec-core (~> 2.6.0)
rspec-expectations (~> 2.6.0)
rspec-mocks (~> 2.6.0)
rspec-core (2.6.4)
rspec-expectations (2.6.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0)
ruby-hmac (0.4.0)
shoulda (2.11.3)
sprockets (2.0.0.beta.13)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.4)
term-ansicolor (1.0.5)
tilt (1.3.2)
weakling (0.0.4-java)
xml-simple (1.0.16)
PLATFORMS
java
ruby
DEPENDENCIES
activerecord
appraisal
aws-s3
bundler
cocaine (~> 0.2)
fog
jruby-openssl
mime-types
mocha
rake
rdoc
shoulda
sprockets (~> 2.0.0.beta.13)
sqlite3 (~> 1.3.4)
require 'rubygems'
require 'appraisal'
require 'bundler/setup'
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
require 'rdoc/task'
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
require 'paperclip'
desc 'Default: run unit tests.'
task :default => [:clean, :test]
task :default => [:clean, 'appraisal:install', :all]
desc 'Test the paperclip plugin under all supported Rails versions.'
task :all do |t|
exec('rake appraisal test')
end
desc 'Test the paperclip plugin.'
Rake::TestTask.new(:test) do |t|
......@@ -22,7 +31,7 @@ task :shell do |t|
end
desc 'Generate documentation for the paperclip plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'doc'
rdoc.title = 'Paperclip'
rdoc.options << '--line-numbers' << '--inline-source'
......@@ -40,47 +49,15 @@ task :clean do |t|
FileUtils.rm_rf "doc"
FileUtils.rm_rf "tmp"
FileUtils.rm_rf "pkg"
FileUtils.rm_rf "public"
FileUtils.rm "test/debug.log" rescue nil
FileUtils.rm "test/paperclip.db" rescue nil
Dir.glob("paperclip-*.gem").each{|f| FileUtils.rm f }
end
include_file_globs = ["README*",
"LICENSE",
"Rakefile",
"init.rb",
"{generators,lib,tasks,test,shoulda_macros}/**/*"]
exclude_file_globs = ["test/s3.yml",
"test/debug.log",
"test/paperclip.db",
"test/doc",
"test/doc/*",
"test/pkg",
"test/pkg/*",
"test/tmp",
"test/tmp/*"]
spec = Gem::Specification.new do |s|
s.name = "paperclip"
s.version = Paperclip::VERSION
s.author = "Jon Yurek"
s.email = "jyurek@thoughtbot.com"
s.homepage = "http://www.thoughtbot.com/projects/paperclip"
s.platform = Gem::Platform::RUBY
s.summary = "File attachments as attributes for ActiveRecord"
s.files = FileList[include_file_globs].to_a - FileList[exclude_file_globs].to_a
s.require_path = "lib"
s.test_files = FileList["test/**/test_*.rb"].to_a
s.rubyforge_project = "paperclip"
s.has_rdoc = true
s.extra_rdoc_files = FileList["README*"].to_a
s.rdoc_options << '--line-numbers' << '--inline-source'
s.requirements << "ImageMagick"
s.add_development_dependency 'shoulda'
s.add_development_dependency 'jferris-mocha', '>= 0.9.5.0.1241126838'
s.add_development_dependency 'aws-s3'
s.add_development_dependency 'sqlite3-ruby'
s.add_development_dependency 'activerecord'
s.add_development_dependency 'activesupport'
desc 'Build the gemspec.'
task :gemspec do |t|
exec 'gem build paperclip.gemspec'
end
desc "Print a list of the files to be put into the gem"
......@@ -89,13 +66,13 @@ task :manifest => :clean do
puts file
end
end
desc "Generate a gemspec file for GitHub"
task :gemspec => :clean do
File.open("#{spec.name}.gemspec", 'w') do |f|
f.write spec.to_ruby
end
end
end
desc "Build the gem into the current directory"
task :gem => :gemspec do
......
Feature: Running paperclip in a Rails app
Scenario: Basic utilization
Given I have a rails application
And I save the following as "app/models/user.rb"
"""
class User < ActiveRecord::Base
has_attached_file :avatar
end
"""
When I visit /users/new
And I fill in "user_name" with "something"
And I attach the file "test/fixtures/5k.png" to "user_avatar"
And I press "Submit"
Then I should see "Name: something"
And I should see an image with a path of "/system/avatars/1/original/5k.png"
And the file at "/system/avatars/1/original/5k.png" is the same as "test/fixtures/5k.png"
Feature: Running paperclip in a Rails app using basic S3 support
Scenario: Basic utilization
Given I have a rails application
And I save the following as "app/models/user.rb"
"""
class User < ActiveRecord::Base
has_attached_file :avatar,
:storage => :s3,
:path => "/:attachment/:id/:style/:filename",
:s3_credentials => Rails.root.join("config/s3.yml")
end
"""
And I validate my S3 credentials
And I save the following as "config/s3.yml"
"""
bucket: <%= ENV['PAPERCLIP_TEST_BUCKET'] || 'paperclip' %>
access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
"""
When I visit /users/new
And I fill in "user_name" with "something"
And I attach the file "test/fixtures/5k.png" to "user_avatar"
And I press "Submit"
Then I should see "Name: something"
And I should see an image with a path of "http://s3.amazonaws.com/paperclip/avatars/1/original/5k.png"
And the file at "http://s3.amazonaws.com/paperclip/avatars/1/original/5k.png" is the same as "test/fixtures/5k.png"
Then %r{I should see an image with a path of "([^"]*)"} do |path|
page.should have_css("img[src^='#{path}']")
end
Then %r{^the file at "([^"]*)" is the same as "([^"]*)"$} do |web_file, path|
expected = IO.read(path)
actual = if web_file.match %r{^https?://}
Net::HTTP.get(URI.parse(web_file))
else
visit(web_file)
page.body
end
actual.should == expected
end
Given "I have a rails application" do
steps %{
Given I generate a rails application
And this plugin is available
And I have a "users" resource with "name:string"
And I turn off class caching
Given I save the following as "app/models/user.rb"
"""
class User < ActiveRecord::Base
end
"""
And I save the following as "config/s3.yml"
"""
access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
bucket: paperclip
"""
And I save the following as "app/views/users/new.html.erb"
"""
<% form_for @user, :html => { :multipart => true } do |f| %>
<%= f.text_field :name %>
<%= f.file_field :avatar %>
<%= submit_tag "Submit" %>
<% end %>
"""
And I save the following as "app/views/users/show.html.erb"
"""
<p>Name: <%= @user.name %></p>
<p>Avatar: <%= image_tag @user.avatar.url %></p>
"""
And I run "script/generate paperclip user avatar"
And the rails application is prepped and running
}
end
Given %r{I generate a rails application} do
FileUtils.rm_rf TEMP_ROOT
FileUtils.mkdir_p TEMP_ROOT
Dir.chdir(TEMP_ROOT) do
`rails _2.3.8_ #{APP_NAME}`
end
end
When %r{I save the following as "([^"]*)"} do |path, string|
FileUtils.mkdir_p(File.join(CUC_RAILS_ROOT, File.dirname(path)))
File.open(File.join(CUC_RAILS_ROOT, path), 'w') { |file| file.write(string) }
end
When %r{I turn off class caching} do
Dir.chdir(CUC_RAILS_ROOT) do
file = "config/environments/test.rb"
config = IO.read(file)
config.gsub!(%r{^\s*config.cache_classes.*$},
"config.cache_classes = false")
File.open(file, "w"){|f| f.write(config) }
end
end
When %r{the rails application is prepped and running$} do
When "I reset the database"
When "the rails application is running"
end
When %r{I reset the database} do
When %{I run "rake db:drop db:create db:migrate"}
end
When %r{the rails application is running} do
Dir.chdir(CUC_RAILS_ROOT) do
require "config/environment"
require "capybara/rails"
end
end
When %r{this plugin is available} do
$LOAD_PATH << "#{PROJECT_ROOT}/lib"
require 'paperclip'
When %{I save the following as "vendor/plugins/paperclip/rails/init.rb"},
IO.read("#{PROJECT_ROOT}/rails/init.rb")
end
When %r{I run "([^"]*)"} do |command|
Dir.chdir(CUC_RAILS_ROOT) do
`#{command}`
end
end
When %r{I have a "([^"]*)" resource with "([^"]*)"} do |resource, fields|
When %{I run "script/generate scaffold #{resource} #{fields}"}
end
Given /I validate my S3 credentials/ do
key = ENV['AWS_ACCESS_KEY_ID']
secret = ENV['AWS_SECRET_ACCESS_KEY']
key.should_not be_nil
secret.should_not be_nil
assert_credentials(key, secret)
end
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
# It is recommended to regenerate this file in the future when you upgrade to a
# newer version of cucumber-rails. Consider adding your own code to a new file
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
# files.
require 'uri'
require 'cgi'
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
module WithinHelpers
def with_scope(locator)
locator ? within(locator) { yield } : yield
end
end
World(WithinHelpers)
Given /^(?:|I )am on (.+)$/ do |page_name|
visit path_to(page_name)
end
When /^(?:|I )go to (.+)$/ do |page_name|
visit path_to(page_name)
end
When /^(?:|I )visit (\/.+)$/ do |page_path|
visit page_path
end
When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector|
with_scope(selector) do
click_button(button)
end
end
When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector|
with_scope(selector) do
click_link(link)
end
end
When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field, value, selector|
with_scope(selector) do
fill_in(field, :with => value)
end
end
When /^(?:|I )fill in "([^"]*)" for "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector|
with_scope(selector) do
fill_in(field, :with => value)
end
end
# Use this to fill in an entire form with data from a table. Example:
#
# When I fill in the following:
# | Account Number | 5002 |
# | Expiry date | 2009-11-01 |
# | Note | Nice guy |
# | Wants Email? | |
#
# TODO: Add support for checkbox, select og option
# based on naming conventions.
#
When /^(?:|I )fill in the following(?: within "([^"]*)")?:$/ do |selector, fields|
with_scope(selector) do
fields.rows_hash.each do |name, value|
When %{I fill in "#{name}" with "#{value}"}
end
end
end
When /^(?:|I )select "([^"]*)" from "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector|
with_scope(selector) do
select(value, :from => field)
end
end
When /^(?:|I )check "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector|
with_scope(selector) do
check(field)
end
end
When /^(?:|I )uncheck "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector|
with_scope(selector) do
uncheck(field)
end
end
When /^(?:|I )choose "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector|
with_scope(selector) do
choose(field)
end
end
When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"(?: within "([^"]*)")?$/ do |path, field, selector|
with_scope(selector) do
attach_file(field, path)
end
end
Then /^(?:|I )should see JSON:$/ do |expected_json|
require 'json'
expected = JSON.pretty_generate(JSON.parse(expected_json))
actual = JSON.pretty_generate(JSON.parse(response.body))
expected.should == actual
end
Then /^(?:|I )should see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector|
with_scope(selector) do
if page.respond_to? :should
page.should have_content(text)
else
assert page.has_content?(text)
end
end
end
Then /^(?:|I )should see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector|
regexp = Regexp.new(regexp)
with_scope(selector) do
if page.respond_to? :should
page.should have_xpath('//*', :text => regexp)
else
assert page.has_xpath?('//*', :text => regexp)
end
end
end
Then /^(?:|I )should not see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector|
with_scope(selector) do
if page.respond_to? :should
page.should have_no_content(text)
else
assert page.has_no_content?(text)
end
end
end
Then /^(?:|I )should not see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector|
regexp = Regexp.new(regexp)
with_scope(selector) do
if page.respond_to? :should
page.should have_no_xpath('//*', :text => regexp)
else
assert page.has_no_xpath?('//*', :text => regexp)
end
end
end
Then /^the "([^"]*)" field(?: within "([^"]*)")? should contain "([^"]*)"$/ do |field, selector, value|
with_scope(selector) do
field = find_field(field)
field_value = (field.tag_name == 'textarea') ? field.text : field.value
if field_value.respond_to? :should
field_value.should =~ /#{value}/
else
assert_match(/#{value}/, field_value)
end
end
end
Then /^the "([^"]*)" field(?: within "([^"]*)")? should not contain "([^"]*)"$/ do |field, selector, value|
with_scope(selector) do
field = find_field(field)
field_value = (field.tag_name == 'textarea') ? field.text : field.value
if field_value.respond_to? :should_not
field_value.should_not =~ /#{value}/
else
assert_no_match(/#{value}/, field_value)
end
end
end
Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should be checked$/ do |label, selector|
with_scope(selector) do
field_checked = find_field(label)['checked']
if field_checked.respond_to? :should
field_checked.should be_true
else
assert field_checked
end
end
end
Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should not be checked$/ do |label, selector|
with_scope(selector) do
field_checked = find_field(label)['checked']
if field_checked.respond_to? :should
field_checked.should be_false
else
assert !field_checked
end
end
end
Then /^(?:|I )should be on (.+)$/ do |page_name|
current_path = URI.parse(current_url).path
if current_path.respond_to? :should
current_path.should == path_to(page_name)
else
assert_equal path_to(page_name), current_path
end
end
Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
query = URI.parse(current_url).query
actual_params = query ? CGI.parse(query) : {}
expected_params = {}
expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
if actual_params.respond_to? :should
actual_params.should == expected_params
else
assert_equal expected_params, actual_params
end
end
Then /^I save and open the page$/ do
save_and_open_page
end
Then /^show me the page$/ do
save_and_open_page
end
require 'capybara/cucumber'
require 'test/unit/assertions'
World(Test::Unit::Assertions)
module NavigationHelpers
# Maps a name to a path. Used by the
#
# When /^I go to (.+)$/ do |page_name|
#
# step definition in web_steps.rb
#
def path_to(page_name)
case page_name
when /the new user page/
'/users/new'
when /the home\s?page/
'/'
# Add more mappings here.
# Here is an example that pulls values out of the Regexp:
#
# when /^(.*)'s profile page$/i
# user_profile_path(User.find_by_login($1))
else
begin
page_name =~ /the (.*) page/
path_components = $1.split(/\s+/)
self.send(path_components.push('path').join('_').to_sym)
rescue Object => e
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
"Now, go and add a mapping in #{__FILE__}"
end
end
end
end
World(NavigationHelpers)
PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
TEMP_ROOT = File.join(PROJECT_ROOT, 'tmp').freeze
APP_NAME = 'testapp'.freeze
CUC_RAILS_ROOT = File.join(TEMP_ROOT, APP_NAME).freeze
ENV['RAILS_ENV'] = 'test'
module AWSS3Methods
def load_s3
begin
require 'aws/s3'
rescue LoadError => e
fail "You do not have aws-s3 installed."
end
end
def assert_credentials(key, secret)
load_s3
begin
AWS::S3::Base.establish_connection!(
:access_key_id => key,
:secret_access_key => secret
)
AWS::S3::Service.buckets
rescue AWS::S3::ResponseError => e
fail "Could not connect using AWS credentials in AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. " +
"Please make sure these are set in your environment."
end
end
end
World(AWSS3Methods)
# This file was generated by Appraisal
source "http://rubygems.org"
gem "activerecord", :require=>"active_record"
gem "appraisal"
gem "aws-s3", :require=>"aws/s3"
gem "bundler"
gem "cocaine", "~>0.2"
gem "fog"
gem "jruby-openssl", :platform=>:jruby
gem "mime-types"
gem "mocha"
gem "rake"
gem "rdoc", :require=>false
gem "shoulda"
gem "sqlite3", "~>1.3.4"
gem "sprockets", "~> 2.0.0.beta.13", :require=>false
gem "rails", "~> 2.3.12"
GEM
remote: http://rubygems.org/
specs:
actionmailer (2.3.12)
actionpack (= 2.3.12)
actionpack (2.3.12)
activesupport (= 2.3.12)
rack (~> 1.1.0)
activerecord (2.3.12)
activesupport (= 2.3.12)
activeresource (2.3.12)
activesupport (= 2.3.12)
activesupport (2.3.12)
appraisal (0.3.7)
aruba (~> 0.4.2)
bundler
rake
aruba (0.4.3)
bcat (>= 0.6.1)
childprocess (>= 0.1.9)
cucumber (>= 0.10.7)
rdiscount (>= 1.6.8)
rspec (>= 2.6.0)
aws-s3 (0.6.2)
builder
mime-types
xml-simple
bcat (0.6.1)
rack (~> 1.0)
bouncy-castle-java (1.5.0146.1)
builder (3.0.0)
childprocess (0.2.0)
ffi (~> 1.0.6)
cocaine (0.2.0)
cucumber (1.0.2)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (~> 2.4.5)
json (>= 1.4.6)
term-ansicolor (>= 1.0.5)
diff-lcs (1.1.2)
excon (0.6.5)
ffi (1.0.9)
ffi (1.0.9-java)
fog (0.9.0)
builder
excon (~> 0.6.1)
formatador (>= 0.1.3)
json
mime-types
net-scp (>= 1.0.4)
net-ssh (>= 2.1.4)
nokogiri (>= 1.4.4)
ruby-hmac
formatador (0.2.0)
gherkin (2.4.5)
json (>= 1.4.6)
gherkin (2.4.5-java)
json (>= 1.4.6)
hike (1.2.0)
jruby-openssl (0.7.4)
bouncy-castle-java
json (1.5.3)
json (1.5.3-java)
mime-types (1.16)
mocha (0.9.12)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
net-ssh (2.1.4)
nokogiri (1.5.0)
nokogiri (1.5.0-java)
rack (1.1.2)
rails (2.3.12)
actionmailer (= 2.3.12)
actionpack (= 2.3.12)
activerecord (= 2.3.12)
activeresource (= 2.3.12)
activesupport (= 2.3.12)
rake (>= 0.8.3)
rake (0.9.2)
rdiscount (1.6.8)
rdoc (3.8)
rspec (2.6.0)
rspec-core (~> 2.6.0)
rspec-expectations (~> 2.6.0)
rspec-mocks (~> 2.6.0)
rspec-core (2.6.4)
rspec-expectations (2.6.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0)
ruby-hmac (0.4.0)
shoulda (2.11.3)
sprockets (2.0.0.beta.13)
hike (~> 1.2)
rack (~> 1.0)
tilt (!= 1.3.0, ~> 1.1)
sqlite3 (1.3.4)
term-ansicolor (1.0.6)
tilt (1.3.2)
xml-simple (1.1.0)
PLATFORMS
java
ruby
DEPENDENCIES
activerecord
appraisal
aws-s3
bundler
cocaine (~> 0.2)
fog
jruby-openssl
mime-types
mocha
rails (~> 2.3.12)
rake
rdoc
shoulda
sprockets (~> 2.0.0.beta.13)
sqlite3 (~> 1.3.4)
# This file was generated by Appraisal
source "http://rubygems.org"
gem "activerecord", :require=>"active_record"
gem "appraisal"
gem "aws-s3", :require=>"aws/s3"
gem "bundler"
gem "cocaine", "~>0.2"
gem "fog"
gem "jruby-openssl", :platform=>:jruby
gem "mime-types"
gem "mocha"
gem "rake"
gem "rdoc", :require=>false
gem "shoulda"
gem "sqlite3", "~>1.3.4"
gem "sprockets", "~> 2.0.0.beta.13", :require=>false
gem "rails", "~> 3.0.9"
GEM
remote: http://rubygems.org/
specs:
abstract (1.0.0)
actionmailer (3.0.9)
actionpack (= 3.0.9)
mail (~> 2.2.19)
actionpack (3.0.9)
activemodel (= 3.0.9)
activesupport (= 3.0.9)
builder (~> 2.1.2)
erubis (~> 2.6.6)
i18n (~> 0.5.0)
rack (~> 1.2.1)
rack-mount (~> 0.6.14)
rack-test (~> 0.5.7)
tzinfo (~> 0.3.23)
activemodel (3.0.9)
activesupport (= 3.0.9)
builder (~> 2.1.2)
i18n (~> 0.5.0)
activerecord (3.0.9)
activemodel (= 3.0.9)
activesupport (= 3.0.9)
arel (~> 2.0.10)
tzinfo (~> 0.3.23)
activeresource (3.0.9)
activemodel (= 3.0.9)
activesupport (= 3.0.9)
activesupport (3.0.9)
appraisal (0.3.7)
aruba (~> 0.4.2)
bundler
rake
arel (2.0.10)
aruba (0.4.3)
bcat (>= 0.6.1)
childprocess (>= 0.1.9)
cucumber (>= 0.10.7)
rdiscount (>= 1.6.8)
rspec (>= 2.6.0)
aws-s3 (0.6.2)
builder
mime-types
xml-simple
bcat (0.6.1)
rack (~> 1.0)
bouncy-castle-java (1.5.0146.1)
builder (2.1.2)
childprocess (0.2.0)
ffi (~> 1.0.6)
cocaine (0.2.0)
cucumber (1.0.2)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (~> 2.4.5)
json (>= 1.4.6)
term-ansicolor (>= 1.0.5)
diff-lcs (1.1.2)
erubis (2.6.6)
abstract (>= 1.0.0)
excon (0.6.5)
ffi (1.0.9)
ffi (1.0.9-java)
fog (0.9.0)
builder
excon (~> 0.6.1)
formatador (>= 0.1.3)
json
mime-types
net-scp (>= 1.0.4)
net-ssh (>= 2.1.4)
nokogiri (>= 1.4.4)
ruby-hmac
formatador (0.2.0)
gherkin (2.4.5)
json (>= 1.4.6)
gherkin (2.4.5-java)
json (>= 1.4.6)
hike (1.2.0)
i18n (0.5.0)
jruby-openssl (0.7.4)
bouncy-castle-java
json (1.5.3)
json (1.5.3-java)
mail (2.2.19)
activesupport (>= 2.3.6)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.16)
mocha (0.9.12)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
net-ssh (2.1.4)
nokogiri (1.5.0)
nokogiri (1.5.0-java)
polyglot (0.3.1)
rack (1.2.3)
rack-mount (0.6.14)
rack (>= 1.0.0)
rack-test (0.5.7)
rack (>= 1.0)
rails (3.0.9)
actionmailer (= 3.0.9)
actionpack (= 3.0.9)
activerecord (= 3.0.9)
activeresource (= 3.0.9)
activesupport (= 3.0.9)
bundler (~> 1.0)
railties (= 3.0.9)
railties (3.0.9)
actionpack (= 3.0.9)
activesupport (= 3.0.9)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.4)
rake (0.9.2)
rdiscount (1.6.8)
rdoc (3.8)
rspec (2.6.0)
rspec-core (~> 2.6.0)
rspec-expectations (~> 2.6.0)
rspec-mocks (~> 2.6.0)
rspec-core (2.6.4)
rspec-expectations (2.6.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0)
ruby-hmac (0.4.0)
shoulda (2.11.3)
sprockets (2.0.0.beta.13)
hike (~> 1.2)
rack (~> 1.0)
tilt (!= 1.3.0, ~> 1.1)
sqlite3 (1.3.4)
term-ansicolor (1.0.6)
thor (0.14.6)
tilt (1.3.2)
treetop (1.4.9)
polyglot (>= 0.3.1)
tzinfo (0.3.29)
xml-simple (1.1.0)
PLATFORMS
java
ruby
DEPENDENCIES
activerecord
appraisal
aws-s3
bundler
cocaine (~> 0.2)
fog
jruby-openssl
mime-types
mocha
rails (~> 3.0.9)
rake
rdoc
shoulda
sprockets (~> 2.0.0.beta.13)
sqlite3 (~> 1.3.4)
# This file was generated by Appraisal
source "http://rubygems.org"
gem "activerecord", :require=>"active_record"
gem "appraisal"
gem "aws-s3", :require=>"aws/s3"
gem "bundler"
gem "cocaine", "~>0.2"
gem "fog"
gem "jruby-openssl", :platform=>:jruby
gem "mime-types"
gem "mocha"
gem "rake"
gem "rdoc", :require=>false
gem "shoulda"
gem "sqlite3", "~>1.3.4"
gem "sprockets", "~> 2.0.0.beta.13", :require=>false
gem "rails", "~> 3.1.0.rc5"
GEM
remote: http://rubygems.org/
specs:
actionmailer (3.1.0.rc6)
actionpack (= 3.1.0.rc6)
mail (~> 2.3.0)
actionpack (3.1.0.rc6)
activemodel (= 3.1.0.rc6)
activesupport (= 3.1.0.rc6)
builder (~> 3.0.0)
erubis (~> 2.7.0)
i18n (~> 0.6)
rack (~> 1.3.2)
rack-cache (~> 1.0.2)
rack-mount (~> 0.8.1)
rack-test (~> 0.6.0)
sprockets (~> 2.0.0.beta.12)
activemodel (3.1.0.rc6)
activesupport (= 3.1.0.rc6)
bcrypt-ruby (~> 2.1.4)
builder (~> 3.0.0)
i18n (~> 0.6)
activerecord (3.1.0.rc6)
activemodel (= 3.1.0.rc6)
activesupport (= 3.1.0.rc6)
arel (~> 2.2.1)
tzinfo (~> 0.3.29)
activeresource (3.1.0.rc6)
activemodel (= 3.1.0.rc6)
activesupport (= 3.1.0.rc6)
activesupport (3.1.0.rc6)
multi_json (~> 1.0)
appraisal (0.3.7)
aruba (~> 0.4.2)
bundler
rake
arel (2.2.1)
aruba (0.4.5)
bcat (>= 0.6.1)
childprocess (>= 0.1.9)
cucumber (>= 0.10.7)
rdiscount (>= 1.6.8)
rspec (>= 2.6.0)
aws-s3 (0.6.2)
builder
mime-types
xml-simple
bcat (0.6.1)
rack (~> 1.0)
bcrypt-ruby (2.1.4)
bcrypt-ruby (2.1.4-java)
bouncy-castle-java (1.5.0146.1)
builder (3.0.0)
childprocess (0.2.0)
ffi (~> 1.0.6)
cocaine (0.2.0)
cucumber (1.0.2)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (~> 2.4.5)
json (>= 1.4.6)
term-ansicolor (>= 1.0.5)
diff-lcs (1.1.2)
erubis (2.7.0)
excon (0.6.5)
ffi (1.0.9)
ffi (1.0.9-java)
fog (0.10.0)
builder
excon (~> 0.6.5)
formatador (~> 0.2.0)
mime-types
multi_json (~> 1.0.3)
net-scp (~> 1.0.4)
net-ssh (~> 2.1.4)
nokogiri (~> 1.5.0)
ruby-hmac
formatador (0.2.0)
gherkin (2.4.5)
json (>= 1.4.6)
gherkin (2.4.5-java)
json (>= 1.4.6)
hike (1.2.1)
i18n (0.6.0)
jruby-openssl (0.7.4)
bouncy-castle-java
json (1.5.3)
json (1.5.3-java)
mail (2.3.0)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.16)
mocha (0.9.12)
multi_json (1.0.3)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
net-ssh (2.1.4)
nokogiri (1.5.0)
nokogiri (1.5.0-java)
polyglot (0.3.2)
rack (1.3.2)
rack-cache (1.0.2)
rack (>= 0.4)
rack-mount (0.8.2)
rack (>= 1.0.0)
rack-ssl (1.3.2)
rack
rack-test (0.6.1)
rack (>= 1.0)
rails (3.1.0.rc6)
actionmailer (= 3.1.0.rc6)
actionpack (= 3.1.0.rc6)
activerecord (= 3.1.0.rc6)
activeresource (= 3.1.0.rc6)
activesupport (= 3.1.0.rc6)
bundler (~> 1.0)
railties (= 3.1.0.rc6)
railties (3.1.0.rc6)
actionpack (= 3.1.0.rc6)
activesupport (= 3.1.0.rc6)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
rake (0.9.2)
rdiscount (1.6.8)
rdoc (3.9.2)
rspec (2.6.0)
rspec-core (~> 2.6.0)
rspec-expectations (~> 2.6.0)
rspec-mocks (~> 2.6.0)
rspec-core (2.6.4)
rspec-expectations (2.6.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0)
ruby-hmac (0.4.0)
shoulda (2.11.3)
sprockets (2.0.0.beta.13)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.4)
term-ansicolor (1.0.6)
thor (0.14.6)
tilt (1.3.2)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.29)
xml-simple (1.1.0)
PLATFORMS
java
ruby
DEPENDENCIES
activerecord
appraisal
aws-s3
bundler
cocaine (~> 0.2)
fog
jruby-openssl
mime-types
mocha
rails (~> 3.1.0.rc5)
rake
rdoc
shoulda
sprockets (~> 2.0.0.beta.13)
sqlite3 (~> 1.3.4)
Usage:
script/generate paperclip Class attachment1 (attachment2 ...)
This will create a migration that will add the proper columns to your class's table.
\ No newline at end of file
This will create a migration that will add the proper columns to your class's table.
class PaperclipGenerator < Rails::Generator::NamedBase
attr_accessor :attachments, :migration_name
def initialize(args, options = {})
super
@class_name, @attachments = args[0], args[1..-1]
end
def manifest
def manifest
file_name = generate_file_name
@migration_name = file_name.camelize
record do |m|
......@@ -14,14 +14,14 @@ class PaperclipGenerator < Rails::Generator::NamedBase
File.join('db', 'migrate'),
:migration_file_name => file_name
end
end
private
end
private
def generate_file_name
names = attachments.map{|a| a.underscore }
names = names[0..-2] + ["and", names[-1]] if names.length > 1
"add_attachments_#{names.join("_")}_to_#{@class_name.underscore}"
end
end
require File.join(File.dirname(__FILE__), "lib", "paperclip")
require 'paperclip/railtie'
Paperclip::Railtie.insert
Description:
Explain the generator
Example:
rails generate paperclip Thing
This will create:
what/will/it/create
require 'rails/generators/active_record'
class PaperclipGenerator < ActiveRecord::Generators::Base
desc "Create a migration to add paperclip-specific fields to your model. " +
"The NAME argument is the name of your model, and the following " +
"arguments are the name of the attachments"
argument :attachment_names, :required => true, :type => :array, :desc => "The names of the attachment(s) to add.",
:banner => "attachment_one attachment_two attachment_three ..."
def self.source_root
@source_root ||= File.expand_path('../templates', __FILE__)
end
def generate_migration
migration_template "paperclip_migration.rb.erb", "db/migrate/#{migration_file_name}"
end
protected
def migration_name
"add_attachment_#{attachment_names.join("_")}_to_#{name.underscore}"
end
def migration_file_name
"#{migration_name}.rb"
end
def migration_class_name
migration_name.camelize
end
end
class <%= migration_class_name %> < ActiveRecord::Migration
def self.up
<% attachment_names.each do |attachment| -%>
add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_name, :string
add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_content_type, :string
add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_size, :integer
add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at, :datetime
<% end -%>
end
def self.down
<% attachment_names.each do |attachment| -%>
remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_name
remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_content_type
remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_size
remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at
<% end -%>
end
end
module Paperclip
# This module is intended as a compatability shim for the differences in
# callbacks between Rails 2.0 and Rails 2.1.
module CallbackCompatability
def self.included(base)
base.extend(ClassMethods)
base.send(:include, InstanceMethods)
end
module ClassMethods
# The implementation of this method is taken from the Rails 1.2.6 source,
# from rails/activerecord/lib/active_record/callbacks.rb, line 192.
def define_callbacks(*args)
args.each do |method|
self.class_eval <<-"end_eval"
def self.#{method}(*callbacks, &block)
callbacks << block if block_given?
write_inheritable_array(#{method.to_sym.inspect}, callbacks)
end
end_eval
end
end
end
module InstanceMethods
# The callbacks in < 2.1 don't worry about the extra options or the
# block, so just run what we have available.
def run_callbacks(meth, opts = nil, &blk)
callback(meth)
end
end
end
end
module Paperclip
module CallbackCompatability
module Rails21
def self.included(base)
base.extend(Defining)
base.send(:include, Running)
end
module Defining
def define_paperclip_callbacks(*args)
args.each do |callback|
define_callbacks("before_#{callback}")
define_callbacks("after_#{callback}")
end
end
end
module Running
def run_paperclip_callbacks(callback, opts = nil, &blk)
# The overall structure of this isn't ideal since after callbacks run even if
# befores return false. But this is how rails 3's callbacks work, unfortunately.
if run_callbacks(:"before_#{callback}"){ |result, object| result == false } != false
blk.call
end
run_callbacks(:"after_#{callback}"){ |result, object| result == false }
end
end
end
module Rails3
def self.included(base)
base.extend(Defining)
base.send(:include, Running)
end
module Defining
def define_paperclip_callbacks(*callbacks)
define_callbacks *[callbacks, {:terminator => "result == false"}].flatten
callbacks.each do |callback|
eval <<-end_callbacks
def before_#{callback}(*args, &blk)
set_callback(:#{callback}, :before, *args, &blk)
end
def after_#{callback}(*args, &blk)
set_callback(:#{callback}, :after, *args, &blk)
end
end_callbacks
end
end
end
module Running
def run_paperclip_callbacks(callback, opts = nil, &block)
run_callbacks(callback, opts, &block)
end
end
end
end
end
......@@ -15,10 +15,13 @@ module Paperclip
# File or path.
def self.from_file file
file = file.path if file.respond_to? "path"
raise(Paperclip::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name")) if file.blank?
geometry = begin
Paperclip.run("identify", %Q[-format "%wx%h" "#{file}"[0]])
rescue PaperclipCommandLineError
Paperclip.run("identify", "-format %wx%h :file", :file => "#{file}[0]")
rescue Cocaine::ExitStatusError
""
rescue Cocaine::CommandNotFoundError => e
raise Paperclip::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
end
parse(geometry) ||
raise(NotIdentifiedByImageMagickError.new("#{file} is not recognized by the 'identify' command."))
......@@ -75,12 +78,12 @@ module Paperclip
to_s
end
# Returns the scaling and cropping geometries (in string-based ImageMagick format)
# neccessary to transform this Geometry into the Geometry given. If crop is true,
# then it is assumed the destination Geometry will be the exact final resolution.
# In this case, the source Geometry is scaled so that an image containing the
# destination Geometry would be completely filled by the source image, and any
# overhanging image would be cropped. Useful for square thumbnail images. The cropping
# Returns the scaling and cropping geometries (in string-based ImageMagick format)
# neccessary to transform this Geometry into the Geometry given. If crop is true,
# then it is assumed the destination Geometry will be the exact final resolution.
# In this case, the source Geometry is scaled so that an image containing the
# destination Geometry would be completely filled by the source image, and any
# overhanging image would be cropped. Useful for square thumbnail images. The cropping
# is weighted at the center of the Geometry.
def transformation_to dst, crop = false
if crop
......
......@@ -6,13 +6,13 @@ module Paperclip
module Interpolations
extend self
# Hash assignment of interpolations. Included only for compatability,
# Hash assignment of interpolations. Included only for compatibility,
# and is not intended for normal use.
def self.[]= name, block
define_method(name, &block)
end
# Hash access of interpolations. Included only for compatability,
# Hash access of interpolations. Included only for compatibility,
# and is not intended for normal use.
def self.[] name
method(name)
......@@ -35,30 +35,41 @@ module Paperclip
# Returns the filename, the same way as ":basename.:extension" would.
def filename attachment, style_name
"#{basename(attachment, style_name)}.#{extension(attachment, style_name)}"
[ basename(attachment, style_name), extension(attachment, style_name) ].reject(&:blank?).join(".")
end
# Returns the interpolated URL. Will raise an error if the url itself
# contains ":url" to prevent infinite recursion. This interpolation
# is used in the default :path to ease default specifications.
RIGHT_HERE = "#{__FILE__.gsub(%r{^\./}, "")}:#{__LINE__ + 3}"
def url attachment, style_name
raise InfiniteInterpolationError if attachment.options[:url].include?(":url")
raise InfiniteInterpolationError if caller.any?{|b| b.index(RIGHT_HERE) }
attachment.url(style_name, false)
end
# Returns the timestamp as defined by the <attachment>_updated_at field
# in the server default time zone unless :use_global_time_zone is set
# to false. Note that a Rails.config.time_zone change will still
# invalidate any path or URL that uses :timestamp. For a
# time_zone-agnostic timestamp, use #updated_at.
def timestamp attachment, style_name
attachment.instance_read(:updated_at).to_s
attachment.instance_read(:updated_at).in_time_zone(attachment.time_zone).to_s
end
# Returns the RAILS_ROOT constant.
# Returns an integer timestamp that is time zone-neutral, so that paths
# remain valid even if a server's time zone changes.
def updated_at attachment, style_name
attachment.updated_at
end
# Returns the Rails.root constant.
def rails_root attachment, style_name
RAILS_ROOT
Rails.root
end
# Returns the RAILS_ENV constant.
# Returns the Rails.env constant.
def rails_env attachment, style_name
RAILS_ENV
Rails.env
end
# Returns the underscored, pluralized version of the class name.
......@@ -78,20 +89,65 @@ module Paperclip
# Returns the extension of the file. e.g. "jpg" for "file.jpg"
# If the style has a format defined, it will return the format instead
# of the actual extension.
def extension attachment, style_name
def extension attachment, style_name
((style = attachment.styles[style_name]) && style[:format]) ||
File.extname(attachment.original_filename).gsub(/^\.+/, "")
end
# Returns an extension based on the content type. e.g. "jpeg" for "image/jpeg".
# Each mime type generally has multiple extensions associated with it, so
# if the extension from teh original filename is one of these extensions,
# that extension is used, otherwise, the first in the list is used.
def content_type_extension attachment, style_name
mime_type = MIME::Types[attachment.content_type]
extensions_for_mime_type = unless mime_type.empty?
mime_type.first.extensions
else
[]
end
original_extension = extension(attachment, style_name)
if extensions_for_mime_type.include? original_extension
original_extension
elsif !extensions_for_mime_type.empty?
extensions_for_mime_type.first
else
# It's possible, though unlikely, that the mime type is not in the
# database, so just use the part after the '/' in the mime type as the
# extension.
%r{/([^/]*)$}.match(attachment.content_type)[1]
end
end
# Returns the id of the instance.
def id attachment, style_name
attachment.instance.id
end
# Returns the #to_param of the instance.
def param attachment, style_name
attachment.instance.to_param
end
# Returns the fingerprint of the instance.
def fingerprint attachment, style_name
attachment.fingerprint
end
# Returns a the attachment hash. See Paperclip::Attachment#hash for
# more details.
def hash attachment, style_name
attachment.hash(style_name)
end
# Returns the id of the instance in a split path form. e.g. returns
# 000/001/234 for an id of 1234.
def id_partition attachment, style_name
("%09d" % attachment.instance.id).scan(/\d{3}/).join("/")
if (id = attachment.instance.id).is_a?(Integer)
("%09d" % id).scan(/\d{3}/).join("/")
else
id.scan(/.{3}/).first(3).join("/")
end
end
# Returns the pluralized form of the attachment name. e.g.
......
# Provides method that can be included on File-type objects (IO, StringIO, Tempfile, etc) to allow stream copying
# and Tempfile conversion.
module IOStream
# Returns a Tempfile containing the contents of the readable object.
def to_tempfile
name = respond_to?(:original_filename) ? original_filename : (respond_to?(:path) ? path : "stream")
tempfile = Paperclip::Tempfile.new(File.basename(name))
def to_tempfile(object)
return object.to_tempfile if object.respond_to?(:to_tempfile)
name = object.respond_to?(:original_filename) ? object.original_filename : (object.respond_to?(:path) ? object.path : "stream")
tempfile = Paperclip::Tempfile.new(["stream", File.extname(name)])
tempfile.binmode
self.stream_to(tempfile)
stream_to(object, tempfile)
end
# Copies one read-able object from one place to another in blocks, obviating the need to load
# the whole thing into memory. Defaults to 8k blocks. If this module is included in both
# StringIO and Tempfile, then either can have its data copied anywhere else without typing
# worries or memory overhead worries. Returns a File if a String is passed in as the destination
# and returns the IO or Tempfile as passed in if one is sent as the destination.
def stream_to path_or_file, in_blocks_of = 8192
# the whole thing into memory. Defaults to 8k blocks. Returns a File if a String is passed
# in as the destination and returns the IO or Tempfile as passed in if one is sent as the destination.
def stream_to object, path_or_file, in_blocks_of = 8192
dstio = case path_or_file
when String then File.new(path_or_file, "wb+")
when IO then path_or_file
when Tempfile then path_or_file
end
buffer = ""
self.rewind
while self.read(in_blocks_of, buffer) do
object.rewind
while object.read(in_blocks_of, buffer) do
dstio.write(buffer)
end
dstio.rewind
dstio.rewind
dstio
end
end
class IO #:nodoc:
include IOStream
end
%w( Tempfile StringIO ).each do |klass|
if Object.const_defined? klass
Object.const_get(klass).class_eval do
include IOStream
end
end
end
# Corrects a bug in Windows when asking for Tempfile size.
if defined? Tempfile
if defined?(Tempfile) && RUBY_PLATFORM !~ /java/
class Tempfile
def size
if @tmpfile
......
......@@ -2,3 +2,32 @@ require 'paperclip/matchers/have_attached_file_matcher'
require 'paperclip/matchers/validate_attachment_presence_matcher'
require 'paperclip/matchers/validate_attachment_content_type_matcher'
require 'paperclip/matchers/validate_attachment_size_matcher'
module Paperclip
module Shoulda
# Provides rspec-compatible matchers for testing Paperclip attachments.
#
# In spec_helper.rb, you'll need to require the matchers:
#
# require "paperclip/matchers"
#
# And include the module:
#
# Spec::Runner.configure do |config|
# config.include Paperclip::Shoulda::Matchers
# end
#
# Example:
# describe User do
# it { should have_attached_file(:avatar) }
# it { should validate_attachment_presence(:avatar) }
# it { should validate_attachment_content_type(:avatar).
# allowing('image/png', 'image/gif').
# rejecting('text/plain', 'text/xml') }
# it { should validate_attachment_size(:avatar).
# less_than(2.megabytes) }
# end
module Matchers
end
end
end
module Paperclip
module Shoulda
module Matchers
# Ensures that the given instance or class has an attachment with the
# given name.
#
# Example:
# describe User do
# it { should have_attached_file(:avatar) }
# end
def have_attached_file name
HaveAttachedFileMatcher.new(name)
end
......@@ -12,6 +19,7 @@ module Paperclip
def matches? subject
@subject = subject
@subject = @subject.class unless Class === @subject
responds? && has_column? && included?
end
......
module Paperclip
module Shoulda
module Matchers
# Ensures that the given instance or class validates the content type of
# the given attachment as specified.
#
# Example:
# describe User do
# it { should validate_attachment_content_type(:icon).
# allowing('image/png', 'image/gif').
# rejecting('text/plain', 'text/xml') }
# end
def validate_attachment_content_type name
ValidateAttachmentContentTypeMatcher.new(name)
end
......@@ -8,6 +17,8 @@ module Paperclip
class ValidateAttachmentContentTypeMatcher
def initialize attachment_name
@attachment_name = attachment_name
@allowed_types = []
@rejected_types = []
end
def allowing *types
......@@ -22,18 +33,25 @@ module Paperclip
def matches? subject
@subject = subject
@subject = @subject.class unless Class === @subject
@allowed_types && @rejected_types &&
allowed_types_allowed? && rejected_types_rejected?
end
def failure_message
"Content types #{@allowed_types.join(", ")} should be accepted" +
" and #{@rejected_types.join(", ")} rejected by #{@attachment_name}"
"".tap do |str|
str << "Content types #{@allowed_types.join(", ")} should be accepted" if @allowed_types.present?
str << "\n" if @allowed_types.present? && @rejected_types.present?
str << "Content types #{@rejected_types.join(", ")} should be rejected by #{@attachment_name}" if @rejected_types.present?
end
end
def negative_failure_message
"Content types #{@allowed_types.join(", ")} should be rejected" +
" and #{@rejected_types.join(", ")} accepted by #{@attachment_name}"
"".tap do |str|
str << "Content types #{@allowed_types.join(", ")} should be rejected" if @allowed_types.present?
str << "\n" if @allowed_types.present? && @rejected_types.present?
str << "Content types #{@rejected_types.join(", ")} should be accepted by #{@attachment_name}" if @rejected_types.present?
end
end
def description
......@@ -42,24 +60,22 @@ module Paperclip
protected
def allow_types?(types)
types.all? do |type|
file = StringIO.new(".")
file.content_type = type
(subject = @subject.new).attachment_for(@attachment_name).assign(file)
subject.valid? && subject.errors.on(:"#{@attachment_name}_content_type").blank?
end
def type_allowed?(type)
file = StringIO.new(".")
file.content_type = type
(subject = @subject.new).attachment_for(@attachment_name).assign(file)
subject.valid?
subject.errors[:"#{@attachment_name}_content_type"].blank?
end
def allowed_types_allowed?
allow_types?(@allowed_types)
@allowed_types.all? { |type| type_allowed?(type) }
end
def rejected_types_rejected?
not allow_types?(@rejected_types)
!@rejected_types.any? { |type| type_allowed?(type) }
end
end
end
end
end
module Paperclip
module Shoulda
module Matchers
# Ensures that the given instance or class validates the presence of the
# given attachment.
#
# describe User do
# it { should validate_attachment_presence(:avatar) }
# end
def validate_attachment_presence name
ValidateAttachmentPresenceMatcher.new(name)
end
......@@ -12,6 +18,7 @@ module Paperclip
def matches? subject
@subject = subject
@subject = @subject.class unless Class === @subject
error_when_not_valid? && no_error_when_valid?
end
......@@ -32,17 +39,16 @@ module Paperclip
def error_when_not_valid?
(subject = @subject.new).send(@attachment_name).assign(nil)
subject.valid?
not subject.errors.on(:"#{@attachment_name}_file_name").blank?
not subject.errors[:"#{@attachment_name}_file_name"].blank?
end
def no_error_when_valid?
@file = StringIO.new(".")
(subject = @subject.new).send(@attachment_name).assign(@file)
subject.valid?
subject.errors.on(:"#{@attachment_name}_file_name").blank?
subject.errors[:"#{@attachment_name}_file_name"].blank?
end
end
end
end
end
module Paperclip
module Shoulda
module Matchers
# Ensures that the given instance or class validates the size of the
# given attachment as specified.
#
# Examples:
# it { should validate_attachment_size(:avatar).
# less_than(2.megabytes) }
# it { should validate_attachment_size(:icon).
# greater_than(1024) }
# it { should validate_attachment_size(:icon).
# in(0..100) }
def validate_attachment_size name
ValidateAttachmentSizeMatcher.new(name)
end
......@@ -28,6 +38,7 @@ module Paperclip
def matches? subject
@subject = subject
@subject = @subject.class unless Class === @subject
lower_than_low? && higher_than_low? && lower_than_high? && higher_than_high?
end
......@@ -58,7 +69,7 @@ module Paperclip
(subject = @subject.new).send(@attachment_name).assign(file)
subject.valid?
subject.errors.on(:"#{@attachment_name}_file_size").blank?
subject.errors[:"#{@attachment_name}_file_size"].blank?
end
def lower_than_low?
......@@ -82,4 +93,3 @@ module Paperclip
end
end
end
......@@ -6,7 +6,7 @@ module Paperclip
#
# Processors are required to be defined inside the Paperclip module and
# are also required to be a subclass of Paperclip::Processor. There is
# only one method you *must* implement to properly be a subclass:
# only one method you *must* implement to properly be a subclass:
# #make, but #initialize may also be of use. Both methods accept 3
# arguments: the file that will be operated on (which is an instance of
# File), a hash of options that were defined in has_attached_file's
......@@ -33,17 +33,26 @@ module Paperclip
new(file, options, attachment).make
end
end
# Due to how ImageMagick handles its image format conversion and how Tempfile
# handles its naming scheme, it is necessary to override how Tempfile makes
# its names so as to allow for file extensions. Idea taken from the comments
# on this blog post:
# http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
class Tempfile < ::Tempfile
# Replaces Tempfile's +make_tmpname+ with one that honors file extensions.
def make_tmpname(basename, n)
extension = File.extname(basename)
sprintf("%s,%d,%d%s", File.basename(basename, extension), $$, n, extension)
# This is Ruby 1.8.7's implementation.
if RUBY_VERSION <= "1.8.6" || RUBY_PLATFORM =~ /java/
def make_tmpname(basename, n)
case basename
when Array
prefix, suffix = *basename
else
prefix, suffix = basename, ''
end
t = Time.now.strftime("%y%m%d")
path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
end
end
end
end
require 'paperclip'
module Paperclip
if defined? Rails::Railtie
require 'rails'
class Railtie < Rails::Railtie
initializer 'paperclip.insert_into_active_record' do
ActiveSupport.on_load :active_record do
Paperclip::Railtie.insert
end
end
rake_tasks do
load "tasks/paperclip.rake"
end
end
end
class Railtie
def self.insert
ActiveRecord::Base.send(:include, Paperclip::Glue)
File.send(:include, Paperclip::Upfile)
end
end
end
module Paperclip
module Storage
# The default place to store attachments is in the filesystem. Files on the local
# filesystem can be very easily served by Apache without requiring a hit to your app.
# They also can be processed more easily after they've been saved, as they're just
# normal files. There is one Filesystem-specific option for has_attached_file.
# * +path+: The location of the repository of attachments on disk. This can (and, in
# almost all cases, should) be coordinated with the value of the +url+ option to
# allow files to be saved into a place where Apache can serve them without
# hitting your app. Defaults to
# ":rails_root/public/:attachment/:id/:style/:basename.:extension"
# By default this places the files in the app's public directory which can be served
# directly. If you are using capistrano for deployment, a good idea would be to
# make a symlink to the capistrano-created system directory from inside your app's
# public directory.
# See Paperclip::Attachment#interpolate for more information on variable interpolaton.
# :path => "/var/app/attachments/:class/:id/:style/:basename.:extension"
module Filesystem
def self.extended base
end
def exists?(style_name = default_style)
if original_filename
File.exist?(path(style_name))
else
false
end
end
# Returns representation of the data of the file assigned to the given
# style, in the format most representative of the current storage.
def to_file style_name = default_style
@queued_for_write[style_name] || (File.new(path(style_name), 'rb') if exists?(style_name))
end
def flush_writes #:nodoc:
@queued_for_write.each do |style_name, file|
file.close
FileUtils.mkdir_p(File.dirname(path(style_name)))
log("saving #{path(style_name)}")
begin
FileUtils.mv(file.path, path(style_name))
rescue SystemCallError
FileUtils.cp(file.path, path(style_name))
FileUtils.rm(file.path)
end
FileUtils.chmod(0666&~File.umask, path(style_name))
end
@queued_for_write = {}
end
def flush_deletes #:nodoc:
@queued_for_delete.each do |path|
begin
log("deleting #{path}")
FileUtils.rm(path) if File.exist?(path)
rescue Errno::ENOENT => e
# ignore file-not-found, let everything else pass
end
begin
while(true)
path = File.dirname(path)
FileUtils.rmdir(path)
break if File.exists?(path) # Ruby 1.9.2 does not raise if the removal failed.
end
rescue Errno::EEXIST, Errno::ENOTEMPTY, Errno::ENOENT, Errno::EINVAL, Errno::ENOTDIR, Errno::EACCES
# Stop trying to remove parent directories
rescue SystemCallError => e
log("There was an unexpected error while deleting directories: #{e.class}")
# Ignore it
end
end
@queued_for_delete = []
end
end
end
end
module Paperclip
module Storage
# fog is a modern and versatile cloud computing library for Ruby.
# Among others, it supports Amazon S3 to store your files. In
# contrast to the outdated AWS-S3 gem it is actively maintained and
# supports multiple locations.
# Amazon's S3 file hosting service is a scalable, easy place to
# store files for distribution. You can find out more about it at
# http://aws.amazon.com/s3 There are a few fog-specific options for
# has_attached_file, which will be explained using S3 as an example:
# * +fog_credentials+: Takes a Hash with your credentials. For S3,
# you can use the following format:
# aws_access_key_id: '<your aws_access_key_id>'
# aws_secret_access_key: '<your aws_secret_access_key>'
# provider: 'AWS'
# region: 'eu-west-1'
# * +fog_directory+: This is the name of the S3 bucket that will
# store your files. Remember that the bucket must be unique across
# all of Amazon S3. If the bucket does not exist, Paperclip will
# attempt to create it.
# * +path+: This is the key under the bucket in which the file will
# be stored. The URL will be constructed from the bucket and the
# path. This is what you will want to interpolate. Keys should be
# unique, like filenames, and despite the fact that S3 (strictly
# speaking) does not support directories, you can still use a / to
# separate parts of your file name.
# * +fog_public+: (optional, defaults to true) Should the uploaded
# files be public or not? (true/false)
# * +fog_host+: (optional) The fully-qualified domain name (FQDN)
# that is the alias to the S3 domain of your bucket, e.g.
# 'http://images.example.com'. This can also be used in
# conjunction with Cloudfront (http://aws.amazon.com/cloudfront)
module Fog
def self.extended base
begin
require 'fog'
rescue LoadError => e
e.message << " (You may need to install the fog gem)"
raise e
end unless defined?(Fog)
base.instance_eval do
@fog_directory = @options[:fog_directory]
@fog_credentials = @options[:fog_credentials]
@fog_host = @options[:fog_host]
@fog_public = @options.key?(:fog_public) ? @options[:fog_public] : true
@fog_file = @options[:fog_file] || {}
@url = ':fog_public_url'
Paperclip.interpolates(:fog_public_url) do |attachment, style|
attachment.public_url(style)
end unless Paperclip::Interpolations.respond_to? :fog_public_url
end
end
def exists?(style = default_style)
if original_filename
!!directory.files.head(path(style))
else
false
end
end
def flush_writes
for style, file in @queued_for_write do
log("saving #{path(style)}")
retried = false
begin
directory.files.create(@fog_file.merge(
:body => file,
:key => path(style),
:public => @fog_public
))
rescue Excon::Errors::NotFound
raise if retried
retried = true
directory.save
retry
end
end
@queued_for_write = {}
end
def flush_deletes
for path in @queued_for_delete do
log("deleting #{path}")
directory.files.new(:key => path).destroy
end
@queued_for_delete = []
end
# Returns representation of the data of the file assigned to the given
# style, in the format most representative of the current storage.
def to_file(style = default_style)
if @queued_for_write[style]
@queued_for_write[style]
else
body = directory.files.get(path(style)).body
filename = path(style)
extname = File.extname(filename)
basename = File.basename(filename, extname)
file = Tempfile.new([basename, extname])
file.binmode
file.write(body)
file.rewind
file
end
end
def public_url(style = default_style)
if @fog_host
host = (@fog_host =~ /%d/) ? @fog_host % (path(style).hash % 4) : @fog_host
"#{host}/#{path(style)}"
else
directory.files.new(:key => path(style)).public_url
end
end
private
def connection
@connection ||= ::Fog::Storage.new(@fog_credentials)
end
def directory
@directory ||= connection.directories.new(:key => @fog_directory)
end
end
end
end
......@@ -3,7 +3,7 @@ module Paperclip
# The Style class holds the definition of a thumbnail style, applying
# whatever processing is required to normalize the definition and delaying
# the evaluation of block parameters until useful context is available.
class Style
attr_reader :name, :attachment, :format
......@@ -25,29 +25,34 @@ module Paperclip
end
@format = nil if @format.blank?
end
# retrieves from the attachment the processors defined in the has_attached_file call
# (which method (in the attachment) will call any supplied procs)
# There is an important change of interface here: a style rule can set its own processors
# by default we behave as before, though.
# if a proc has been supplied, we call it here
def processors
@processors || attachment.processors
@processors.respond_to?(:call) ? @processors.call(attachment.instance) : (@processors || attachment.processors)
end
# retrieves from the attachment the whiny setting
def whiny
attachment.whiny
end
# returns true if we're inclined to grumble
def whiny?
!!whiny
end
def convert_options
attachment.send(:extra_options_for, name)
end
def source_file_options
attachment.send(:extra_source_file_options_for, name)
end
# returns the geometry string for this style
# if a proc has been supplied, we call it here
def geometry
......@@ -55,15 +60,15 @@ module Paperclip
end
# Supplies the hash of options that processors expect to receive as their second argument
# Arguments other than the standard geometry, format etc are just passed through from
# Arguments other than the standard geometry, format etc are just passed through from
# initialization and any procs are called here, just before post-processing.
def processor_options
args = {}
@other_args.each do |k,v|
args[k] = v.respond_to?(:call) ? v.call(attachment) : v
end
[:processors, :geometry, :format, :whiny, :convert_options].each do |k|
(arg = send(k)) && args[k] = arg
[:processors, :geometry, :format, :whiny, :convert_options, :source_file_options].each do |k|
(arg = send(k)) && args[k] = arg
end
args
end
......@@ -71,20 +76,20 @@ module Paperclip
# Supports getting and setting style properties with hash notation to ensure backwards-compatibility
# eg. @attachment.styles[:large][:geometry]@ will still work
def [](key)
if [:name, :convert_options, :whiny, :processors, :geometry, :format].include?(key)
if [:name, :convert_options, :whiny, :processors, :geometry, :format, :animated, :source_file_options].include?(key)
send(key)
elsif defined? @other_args[key]
@other_args[key]
end
end
def []=(key, value)
if [:name, :convert_options, :whiny, :processors, :geometry, :format].include?(key)
if [:name, :convert_options, :whiny, :processors, :geometry, :format, :animated, :source_file_options].include?(key)
send("#{key}=".intern, value)
else
@other_args[key] = value
end
end
end
end
\ No newline at end of file
end
......@@ -2,17 +2,21 @@ module Paperclip
# Handles thumbnailing images that are uploaded.
class Thumbnail < Processor
attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options, :source_file_options
attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options,
:source_file_options, :animated
# List of formats that we need to preserve animation
ANIMATED_FORMATS = %w(gif)
# Creates a Thumbnail object set to work on the +file+ given. It
# will attempt to transform the image into one defined by +target_geometry+
# which is a "WxH"-style string. +format+ will be inferred from the +file+
# unless specified. Thumbnail creation will raise no errors unless
# +whiny+ is true (which it is, by default. If +convert_options+ is
# set, the options will be appended to the convert command upon image conversion
# set, the options will be appended to the convert command upon image conversion
def initialize file, options = {}, attachment = nil
super
geometry = options[:geometry]
@file = file
@crop = geometry[-1,1] == '#'
......@@ -22,17 +26,21 @@ module Paperclip
@convert_options = options[:convert_options]
@whiny = options[:whiny].nil? ? true : options[:whiny]
@format = options[:format]
@animated = options[:animated].nil? ? true : options[:animated]
@source_file_options = @source_file_options.split(/\s+/) if @source_file_options.respond_to?(:split)
@convert_options = @convert_options.split(/\s+/) if @convert_options.respond_to?(:split)
@current_format = File.extname(@file.path)
@basename = File.basename(@file.path, @current_format)
end
# Returns true if the +target_geometry+ is meant to crop.
def crop?
@crop
end
# Returns true if the image is meant to make use of additional convert options.
def convert_options?
!@convert_options.nil? && !@convert_options.empty?
......@@ -42,20 +50,24 @@ module Paperclip
# that contains the new image.
def make
src = @file
dst = Tempfile.new([@basename, @format].compact.join("."))
dst = Tempfile.new([@basename, @format ? ".#{@format}" : ''])
dst.binmode
command = <<-end_command
#{ source_file_options }
"#{ File.expand_path(src.path) }[0]"
#{ transformation_command }
"#{ File.expand_path(dst.path) }"
end_command
begin
success = Paperclip.run("convert", command.gsub(/\s+/, " "))
rescue PaperclipCommandLineError
parameters = []
parameters << source_file_options
parameters << ":source"
parameters << transformation_command
parameters << convert_options
parameters << ":dest"
parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
success = Paperclip.run("convert", parameters, :source => "#{File.expand_path(src.path)}#{'[0]' unless animated?}", :dest => File.expand_path(dst.path))
rescue Cocaine::ExitStatusError => e
raise PaperclipError, "There was an error processing the thumbnail for #{@basename}" if @whiny
rescue Cocaine::CommandNotFoundError => e
raise Paperclip::CommandNotFoundError.new("Could not run the `convert` command. Please install ImageMagick.")
end
dst
......@@ -65,11 +77,18 @@ module Paperclip
# into the thumbnail.
def transformation_command
scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
trans = ""
trans << " -resize \"#{scale}\"" unless scale.nil? || scale.empty?
trans << " -crop \"#{crop}\" +repage" if crop
trans << " #{convert_options}" if convert_options?
trans = []
trans << "-coalesce" if animated?
trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
trans << "-crop" << %["#{crop}"] << "+repage" if crop
trans
end
protected
# Return true if the format is animated
def animated?
@animated && ANIMATED_FORMATS.include?(@current_format[1..-1]) && (ANIMATED_FORMATS.include?(@format.to_s) || @format.blank?)
end
end
end
require 'mime/types'
module Paperclip
# The Upfile module is a convenience module for adding uploaded-file-type methods
# to the +File+ class. Useful for testing.
......@@ -6,20 +8,28 @@ module Paperclip
# Infer the MIME-type of the file from the extension.
def content_type
type = (self.path.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
case type
when %r"jp(e|g|eg)" then "image/jpeg"
when %r"tiff?" then "image/tiff"
when %r"png", "gif", "bmp" then "image/#{type}"
when "txt" then "text/plain"
when %r"html?" then "text/html"
when "js" then "application/js"
when "csv", "xml", "css" then "text/#{type}"
types = MIME::Types.type_for(self.original_filename)
if types.length == 0
type_from_file_command
elsif types.length == 1
types.first.content_type
else
Paperclip.run("file", "--mime-type #{self.path}").split(':').last.strip rescue "application/x-#{type}"
iterate_over_array_to_find_best_option(types)
end
end
def iterate_over_array_to_find_best_option(types)
types.reject {|type| type.content_type.match(/\/x-/) }.first
end
def type_from_file_command
# On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
type = (self.original_filename.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
mime_type = (Paperclip.run("file", "-b --mime-type :file", :file => self.path).split(':').last.strip rescue "application/x-#{type}")
mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
mime_type
end
# Returns the file's normal name.
def original_filename
File.basename(self.path)
......@@ -34,17 +44,19 @@ end
if defined? StringIO
class StringIO
attr_accessor :original_filename, :content_type
attr_accessor :original_filename, :content_type, :fingerprint
def original_filename
@original_filename ||= "stringio.txt"
end
def content_type
@content_type ||= "text/plain"
end
def fingerprint
@fingerprint ||= Digest::MD5.hexdigest(self.string)
end
end
end
class File #:nodoc:
include Paperclip::Upfile
end
module Paperclip
VERSION = "2.3.16" unless defined? Paperclip::VERSION
end
def obtain_class
class_name = ENV['CLASS'] || ENV['class']
raise "Must specify CLASS" unless class_name
@klass = Object.const_get(class_name)
end
def obtain_attachments
name = ENV['ATTACHMENT'] || ENV['attachment']
raise "Class #{@klass.name} has no attachments specified" unless @klass.respond_to?(:attachment_definitions)
if !name.blank? && @klass.attachment_definitions.keys.include?(name)
[ name ]
else
@klass.attachment_definitions.keys
end
end
def for_all_attachments
klass = obtain_class
names = obtain_attachments
ids = klass.connection.select_values(klass.send(:construct_finder_sql, :select => 'id'))
module Paperclip
module Task
def self.obtain_class
class_name = ENV['CLASS'] || ENV['class']
raise "Must specify CLASS" unless class_name
class_name
end
ids.each do |id|
instance = klass.find(id)
names.each do |name|
result = if instance.send("#{ name }?")
yield(instance, name)
else
true
end
print result ? "." : "x"; $stdout.flush
def self.obtain_attachments(klass)
klass = Paperclip.class_for(klass.to_s)
name = ENV['ATTACHMENT'] || ENV['attachment']
raise "Class #{klass.name} has no attachments specified" unless klass.respond_to?(:attachment_definitions)
if !name.blank? && klass.attachment_definitions.keys.include?(name)
[ name ]
else
klass.attachment_definitions.keys
end
end
end
puts " Done."
end
namespace :paperclip do
......@@ -38,27 +24,39 @@ namespace :paperclip do
task :refresh => ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]
namespace :refresh do
desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT)."
desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT and STYLES splitted by comma)."
task :thumbnails => :environment do
errors = []
for_all_attachments do |instance, name|
result = instance.send(name).reprocess!
errors << [instance.id, instance.errors] unless instance.errors.blank?
result
klass = Paperclip::Task.obtain_class
names = Paperclip::Task.obtain_attachments(klass)
styles = (ENV['STYLES'] || ENV['styles'] || '').split(',').map(&:to_sym)
names.each do |name|
Paperclip.each_instance_with_attachment(klass, name) do |instance|
instance.send(name).reprocess!(*styles)
errors << [instance.id, instance.errors] unless instance.errors.blank?
end
end
errors.each{|e| puts "#{e.first}: #{e.last.full_messages.inspect}" }
end
desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
task :metadata => :environment do
for_all_attachments do |instance, name|
if file = instance.send(name).to_file
instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
instance.send("#{name}_content_type=", file.content_type.strip)
instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
instance.save(false)
else
true
klass = Paperclip::Task.obtain_class
names = Paperclip::Task.obtain_attachments(klass)
names.each do |name|
Paperclip.each_instance_with_attachment(klass, name) do |instance|
if file = instance.send(name).to_file(:original)
instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
instance.send("#{name}_content_type=", file.content_type.strip)
instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
if Rails.version >= "3.0.0"
instance.save(:validate => false)
else
instance.save(false)
end
else
true
end
end
end
end
......@@ -66,13 +64,17 @@ namespace :paperclip do
desc "Cleans out invalid attachments. Useful after you've added new validations."
task :clean => :environment do
for_all_attachments do |instance, name|
instance.send(name).send(:validate)
if instance.send(name).valid?
true
else
instance.send("#{name}=", nil)
instance.save
klass = Paperclip::Task.obtain_class
names = Paperclip::Task.obtain_attachments(klass)
names.each do |name|
Paperclip.each_instance_with_attachment(klass, name) do |instance|
instance.send(name).send(:validate)
if instance.send(name).valid?
true
else
instance.send("#{name}=", nil)
instance.save
end
end
end
end
......
# -*- encoding: utf-8 -*-
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
require 'paperclip/version'
Gem::Specification.new do |s|
s.name = %q{monde-paperclip}
s.version = "2.3.1.1"
include_files = ["README*", "LICENSE", "Rakefile", "init.rb", "{lib,tasks,test,rails,generators,shoulda_macros}/**/*"].map do |glob|
Dir[glob]
end.flatten
exclude_files = ["**/*.rbc", "test/s3.yml", "test/debug.log", "test/paperclip.db", "test/doc", "test/doc/*", "test/pkg", "test/pkg/*", "test/tmp", "test/tmp/*"].map do |glob|
Dir[glob]
end.flatten
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Jon Yurek"]
s.date = %q{2009-10-09}
s.email = %q{jyurek@thoughtbot.com}
s.extra_rdoc_files = ["README.rdoc"]
s.files = ["README.rdoc", "LICENSE", "Rakefile", "init.rb", "generators/paperclip", "generators/paperclip/paperclip_generator.rb", "generators/paperclip/templates", "generators/paperclip/templates/paperclip_migration.rb.erb", "generators/paperclip/USAGE", "lib/paperclip", "lib/paperclip/attachment.rb", "lib/paperclip/callback_compatability.rb", "lib/paperclip/geometry.rb", "lib/paperclip/interpolations.rb", "lib/paperclip/iostream.rb", "lib/paperclip/matchers", "lib/paperclip/matchers/have_attached_file_matcher.rb", "lib/paperclip/matchers/validate_attachment_content_type_matcher.rb", "lib/paperclip/matchers/validate_attachment_presence_matcher.rb", "lib/paperclip/matchers/validate_attachment_size_matcher.rb", "lib/paperclip/matchers.rb", "lib/paperclip/processor.rb", "lib/paperclip/storage.rb", "lib/paperclip/thumbnail.rb", "lib/paperclip/upfile.rb", "lib/paperclip.rb", "tasks/paperclip_tasks.rake", "test/attachment_test.rb", "test/database.yml", "test/fixtures", "test/fixtures/12k.png", "test/fixtures/50x50.png", "test/fixtures/5k.png", "test/fixtures/bad.png", "test/fixtures/s3.yml", "test/fixtures/text.txt", "test/fixtures/twopage.pdf", "test/geometry_test.rb", "test/helper.rb", "test/integration_test.rb", "test/interpolations_test.rb", "test/iostream_test.rb", "test/matchers", "test/matchers/have_attached_file_matcher_test.rb", "test/matchers/validate_attachment_content_type_matcher_test.rb", "test/matchers/validate_attachment_presence_matcher_test.rb", "test/matchers/validate_attachment_size_matcher_test.rb", "test/paperclip_test.rb", "test/processor_test.rb", "test/storage_test.rb", "test/thumbnail_test.rb", "test/upfile_test.rb", "shoulda_macros/paperclip.rb"]
s.homepage = %q{http://www.thoughtbot.com/projects/paperclip}
s.rdoc_options = ["--line-numbers", "--inline-source"]
s.require_paths = ["lib"]
s.requirements = ["ImageMagick"]
s.rubyforge_project = %q{paperclip}
s.rubygems_version = %q{1.3.5}
s.summary = %q{File attachments as attributes for ActiveRecord}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
s.add_development_dependency(%q<jferris-mocha>, ["= 0.9.5.0.1241126838"])
s.add_development_dependency(%q<aws-s3>, [">= 0"])
s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])
s.add_development_dependency(%q<activerecord>, [">= 0"])
s.add_development_dependency(%q<activesupport>, [">= 0"])
else
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
s.add_dependency(%q<jferris-mocha>, ["= 0.9.5.0.1241126838"])
s.add_dependency(%q<aws-s3>, [">= 0"])
s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
s.add_dependency(%q<activerecord>, [">= 0"])
s.add_dependency(%q<activesupport>, [">= 0"])
end
else
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
s.add_dependency(%q<jferris-mocha>, ["= 0.9.5.0.1241126838"])
s.add_dependency(%q<aws-s3>, [">= 0"])
s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
s.add_dependency(%q<activerecord>, [">= 0"])
s.add_dependency(%q<activesupport>, [">= 0"])
end
spec = Gem::Specification.new do |s|
s.name = "paperclip"
s.version = Paperclip::VERSION
s.author = "Jon Yurek"
s.email = "jyurek@thoughtbot.com"
s.homepage = "https://github.com/thoughtbot/paperclip"
s.description = "Easy upload management for ActiveRecord"
s.platform = Gem::Platform::RUBY
s.summary = "File attachments as attributes for ActiveRecord"
s.files = include_files - exclude_files
s.require_path = "lib"
s.test_files = Dir["test/**/test_*.rb"]
s.rubyforge_project = "paperclip"
s.extra_rdoc_files = Dir["README*"]
s.rdoc_options << '--line-numbers' << '--inline-source'
s.requirements << "ImageMagick"
s.add_dependency 'activerecord', '>=2.3.0'
s.add_dependency 'activesupport', '>=2.3.2'
s.add_dependency 'cocaine', '>=0.0.2'
s.add_dependency 'mime-types'
s.add_development_dependency 'shoulda'
s.add_development_dependency 'appraisal'
s.add_development_dependency 'mocha'
s.add_development_dependency 'aws-s3'
s.add_development_dependency 'sqlite3'
end
require 'paperclip/railtie'
Paperclip::Railtie.insert
require 'paperclip/matchers'
require 'action_controller'
module Paperclip
# =Paperclip Shoulda Macros
......@@ -46,7 +45,7 @@ module Paperclip
end
# Tests to ensure that you have file size validations turned on. You
# can pass the same options to this that you can to
# can pass the same options to this that you can to
# validate_attachment_file_size - :less_than, :greater_than, and :in.
# :less_than checks that a file is less than a certain size, :greater_than
# checks that a file is more than a certain size, and :in takes a Range or
......@@ -104,8 +103,10 @@ module Paperclip
end
end
class ActionController::Integration::Session #:nodoc:
include Paperclip::Shoulda
if defined?(ActionController::Integration::Session)
class ActionController::Integration::Session #:nodoc:
include Paperclip::Shoulda
end
end
class Factory
......
require './test/helper'
require 'fog'
Fog.mock!
class FogTest < Test::Unit::TestCase
context "" do
setup do
@fog_directory = 'papercliptests'
@credentials = {
:provider => 'AWS',
:aws_access_key_id => 'ID',
:aws_secret_access_key => 'SECRET'
}
@connection = Fog::Storage.new(@credentials)
@connection.directories.create(
:key => @fog_directory
)
@options = {
:fog_directory => @fog_directory,
:fog_credentials => @credentials,
:fog_host => nil,
:fog_file => {:cache_control => 1234},
:path => ":attachment/:basename.:extension",
:storage => :fog
}
rebuild_model(@options)
end
should "be extended by the Fog module" do
assert Dummy.new.avatar.is_a?(Paperclip::Storage::Fog)
end
context "when assigned" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
@dummy = Dummy.new
@dummy.avatar = @file
end
teardown do
@file.close
directory = @connection.directories.new(:key => @fog_directory)
directory.files.each {|file| file.destroy}
directory.destroy
end
context "without a bucket" do
setup do
@connection.directories.get(@fog_directory).destroy
end
should "create the bucket" do
assert @dummy.save
assert @connection.directories.get(@fog_directory)
end
end
context "with a bucket" do
should "succeed" do
assert @dummy.save
end
end
context "without a fog_host" do
setup do
rebuild_model(@options.merge(:fog_host => nil))
@dummy = Dummy.new
@dummy.avatar = StringIO.new('.')
@dummy.save
end
should "provide a public url" do
assert !@dummy.avatar.url.nil?
end
end
context "with a fog_host" do
setup do
rebuild_model(@options.merge(:fog_host => 'http://example.com'))
@dummy = Dummy.new
@dummy.avatar = StringIO.new('.')
@dummy.save
end
should "provide a public url" do
assert @dummy.avatar.url =~ /^http:\/\/example\.com\/avatars\/stringio\.txt\?\d*$/
end
end
context "with a fog_host that includes a wildcard placeholder" do
setup do
rebuild_model(
:fog_directory => @fog_directory,
:fog_credentials => @credentials,
:fog_host => 'http://img%d.example.com',
:path => ":attachment/:basename.:extension",
:storage => :fog
)
@dummy = Dummy.new
@dummy.avatar = StringIO.new('.')
@dummy.save
end
should "provide a public url" do
assert @dummy.avatar.url =~ /^http:\/\/img[0123]\.example\.com\/avatars\/stringio\.txt\?\d*$/
end
end
context "with fog_public set to false" do
setup do
rebuild_model(@options.merge(:fog_public => false))
@dummy = Dummy.new
@dummy.avatar = StringIO.new('.')
@dummy.save
end
should 'set the @fog_public instance variable to false' do
assert_equal false, @dummy.avatar.instance_variable_get('@fog_public')
end
end
end
end
end
require 'test/helper'
require './test/helper'
class GeometryTest < Test::Unit::TestCase
context "Paperclip::Geometry" do
......@@ -65,7 +65,7 @@ class GeometryTest < Test::Unit::TestCase
assert_equal "123x456#{mod}", @geo.to_s
end
end
['>', '<', '#', '@', '%', '^', '!', nil].each do |mod|
should "ensure the modifier #{mod.inspect} is preserved with no height" do
assert @geo = Paperclip::Geometry.parse("123x#{mod}")
......@@ -120,6 +120,35 @@ class GeometryTest < Test::Unit::TestCase
assert_raise(Paperclip::NotIdentifiedByImageMagickError){ @geo = Paperclip::Geometry.from_file(file) }
end
should "not generate from a blank filename" do
file = ""
assert_raise(Paperclip::NotIdentifiedByImageMagickError){ @geo = Paperclip::Geometry.from_file(file) }
end
should "not generate from a nil file" do
file = nil
assert_raise(Paperclip::NotIdentifiedByImageMagickError){ @geo = Paperclip::Geometry.from_file(file) }
end
should "not generate from a file with no path" do
file = mock("file", :path => "")
file.stubs(:respond_to?).with("path").returns(true)
assert_raise(Paperclip::NotIdentifiedByImageMagickError){ @geo = Paperclip::Geometry.from_file(file) }
end
should "let us know when a command isn't found versus a processing error" do
old_path = ENV['PATH']
begin
ENV['PATH'] = ''
assert_raises(Paperclip::CommandNotFoundError) do
file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
@geo = Paperclip::Geometry.from_file(file)
end
ensure
ENV['PATH'] = old_path
end
end
[['vertical', 900, 1440, true, false, false, 1440, 900, 0.625],
['horizontal', 1024, 768, false, true, false, 1024, 768, 1.3333],
['square', 100, 100, false, false, true, 100, 100, 1]].each do |args|
......
require 'rubygems'
require 'test/unit'
require 'shoulda'
require 'tempfile'
require 'test/unit'
gem 'jferris-mocha'
require 'shoulda'
require 'mocha'
gem 'sqlite3-ruby'
require 'active_record'
require 'active_record/version'
require 'active_support'
require 'mime/types'
puts "Testing against version #{ActiveRecord::VERSION::STRING}"
`ruby -e 'exit 0'` # Prime $? with a value.
begin
require 'ruby-debug'
rescue LoadError
puts "ruby-debug not loaded"
rescue LoadError => e
puts "debugger disabled"
end
ROOT = File.join(File.dirname(__FILE__), '..')
RAILS_ROOT = ROOT
RAILS_ENV = "test"
ROOT = File.join(File.dirname(__FILE__), '..')
def silence_warnings
old_verbose, $VERBOSE = $VERBOSE, nil
yield
ensure
$VERBOSE = old_verbose
end
class Test::Unit::TestCase
def setup
silence_warnings do
Object.const_set(:Rails, stub('Rails', :root => ROOT, :env => 'test'))
end
end
end
$LOAD_PATH << File.join(ROOT, 'lib')
$LOAD_PATH << File.join(ROOT, 'lib', 'paperclip')
require File.join(ROOT, 'lib', 'paperclip.rb')
require 'shoulda_macros/paperclip'
require './shoulda_macros/paperclip'
FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(__FILE__) + "/debug.log")
ActiveRecord::Base.establish_connection(config['test'])
def reset_class class_name
ActiveRecord::Base.send(:include, Paperclip)
ActiveRecord::Base.send(:include, Paperclip::Glue)
Object.send(:remove_const, class_name) rescue nil
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
klass.class_eval{ include Paperclip }
klass.class_eval{ include Paperclip::Glue }
klass
end
......@@ -51,49 +68,43 @@ end
def rebuild_model options = {}
ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
table.column :title, :string
table.column :other, :string
table.column :avatar_file_name, :string
table.column :avatar_content_type, :string
table.column :avatar_file_size, :integer
table.column :avatar_updated_at, :datetime
table.column :avatar_fingerprint, :string
end
rebuild_class options
end
def rebuild_class options = {}
ActiveRecord::Base.send(:include, Paperclip)
ActiveRecord::Base.send(:include, Paperclip::Glue)
Object.send(:remove_const, "Dummy") rescue nil
Object.const_set("Dummy", Class.new(ActiveRecord::Base))
Dummy.class_eval do
include Paperclip
include Paperclip::Glue
has_attached_file :avatar, options
end
end
def temporary_rails_env(new_env)
old_env = Object.const_defined?("RAILS_ENV") ? RAILS_ENV : nil
silence_warnings do
Object.const_set("RAILS_ENV", new_env)
end
yield
silence_warnings do
Object.const_set("RAILS_ENV", old_env)
end
Dummy.reset_column_information
end
class FakeModel
attr_accessor :avatar_file_name,
:avatar_file_size,
:avatar_last_updated,
:avatar_updated_at,
:avatar_content_type,
:avatar_fingerprint,
:id
def errors
@errors ||= []
end
def run_callbacks name, *args
def run_paperclip_callbacks name, *args
end
end
def attachment options
......@@ -106,3 +117,33 @@ def silence_warnings
ensure
$VERBOSE = old_verbose
end
def should_accept_dummy_class
should "accept the class" do
assert_accepts @matcher, @dummy_class
end
should "accept an instance of that class" do
assert_accepts @matcher, @dummy_class.new
end
end
def should_reject_dummy_class
should "reject the class" do
assert_rejects @matcher, @dummy_class
end
should "reject an instance of that class" do
assert_rejects @matcher, @dummy_class.new
end
end
def with_exitstatus_returning(code)
saved_exitstatus = $?.nil? ? 0 : $?.exitstatus
begin
`ruby -e 'exit #{code.to_i}'`
yield
ensure
`ruby -e 'exit #{saved_exitstatus.to_i}'`
end
end
require 'test/helper'
require './test/helper'
class IntegrationTest < Test::Unit::TestCase
context "Many models at once" do
......@@ -9,7 +9,7 @@ class IntegrationTest < Test::Unit::TestCase
Dummy.create! :avatar => @file
end
end
should "not exceed the open file limit" do
assert_nothing_raised do
dummies = Dummy.find(:all)
......@@ -35,6 +35,22 @@ class IntegrationTest < Test::Unit::TestCase
assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
end
context 'reprocessing with unreadable original' do
setup { File.chmod(0000, @dummy.avatar.path) }
should "not raise an error" do
assert_nothing_raised do
@dummy.avatar.reprocess!
end
end
should "return false" do
assert ! @dummy.avatar.reprocess!
end
teardown { File.chmod(0644, @dummy.avatar.path) }
end
context "redefining its attachment styles" do
setup do
Dummy.class_eval do
......@@ -42,6 +58,7 @@ class IntegrationTest < Test::Unit::TestCase
has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } }
end
@d2 = Dummy.find(@dummy.id)
@original_timestamp = @d2.avatar_updated_at
@d2.avatar.reprocess!
@d2.save
end
......@@ -50,6 +67,82 @@ class IntegrationTest < Test::Unit::TestCase
assert_match /\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`
end
should "change the timestamp" do
assert_not_equal @original_timestamp, @d2.avatar_updated_at
end
end
end
context "Attachment" do
setup do
@thumb_path = "./test/../public/system/avatars/1/thumb/5k.png"
File.delete(@thumb_path) if File.exists?(@thumb_path)
rebuild_model :styles => { :thumb => "50x50#" }
@dummy = Dummy.new
@file = File.new(File.join(File.dirname(__FILE__),
"fixtures",
"5k.png"), 'rb')
end
teardown { @file.close }
should "not create the thumbnails upon saving when post-processing is disabled" do
@dummy.avatar.post_processing = false
@dummy.avatar = @file
assert @dummy.save
assert !File.exists?(@thumb_path)
end
should "create the thumbnails upon saving when post_processing is enabled" do
@dummy.avatar.post_processing = true
@dummy.avatar = @file
assert @dummy.save
assert File.exists?(@thumb_path)
end
end
context "Attachment with no generated thumbnails" do
setup do
@thumb_small_path = "./test/../public/system/avatars/1/thumb_small/5k.png"
@thumb_large_path = "./test/../public/system/avatars/1/thumb_large/5k.png"
File.delete(@thumb_small_path) if File.exists?(@thumb_small_path)
File.delete(@thumb_large_path) if File.exists?(@thumb_large_path)
rebuild_model :styles => { :thumb_small => "50x50#", :thumb_large => "60x60#" }
@dummy = Dummy.new
@file = File.new(File.join(File.dirname(__FILE__),
"fixtures",
"5k.png"), 'rb')
@dummy.avatar.post_processing = false
@dummy.avatar = @file
assert @dummy.save
@dummy.avatar.post_processing = true
end
teardown { @file.close }
should "allow us to create all thumbnails in one go" do
assert !File.exists?(@thumb_small_path)
assert !File.exists?(@thumb_large_path)
@dummy.avatar.reprocess!
assert File.exists?(@thumb_small_path)
assert File.exists?(@thumb_large_path)
end
should "allow us to selectively create each thumbnail" do
assert !File.exists?(@thumb_small_path)
assert !File.exists?(@thumb_large_path)
@dummy.avatar.reprocess! :thumb_small
assert File.exists?(@thumb_small_path)
assert !File.exists?(@thumb_large_path)
@dummy.avatar.reprocess! :thumb_large
assert File.exists?(@thumb_large_path)
end
end
......@@ -157,7 +250,7 @@ class IntegrationTest < Test::Unit::TestCase
end
end
end
context "A model with no convert_options setting" do
setup do
rebuild_model :styles => { :large => "300x300>",
......@@ -168,7 +261,7 @@ class IntegrationTest < Test::Unit::TestCase
:path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
@dummy = Dummy.new
end
should "have its definition return nil when asked about convert_options" do
assert ! Dummy.attachment_definitions[:avatar][:convert_options]
end
......@@ -189,7 +282,39 @@ class IntegrationTest < Test::Unit::TestCase
end
end
end
context "A model with no source_file_options setting" do
setup do
rebuild_model :styles => { :large => "300x300>",
:medium => "100x100",
:thumb => ["32x32#", :gif] },
:default_style => :medium,
:url => "/:attachment/:class/:style/:id/:basename.:extension",
:path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
@dummy = Dummy.new
end
should "have its definition return nil when asked about source_file_options" do
assert ! Dummy.attachment_definitions[:avatar][:source_file_options]
end
context "redefined to have source_file_options setting" do
setup do
rebuild_model :styles => { :large => "300x300>",
:medium => "100x100",
:thumb => ["32x32#", :gif] },
:source_file_options => "-density 400",
:default_style => :medium,
:url => "/:attachment/:class/:style/:id/:basename.:extension",
:path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
end
should "have its definition return source_file_options value when asked about source_file_options" do
assert_equal "-density 400", Dummy.attachment_definitions[:avatar][:source_file_options]
end
end
end
context "A model with a filesystem attachment" do
setup do
rebuild_model :styles => { :large => "300x300>",
......@@ -204,7 +329,7 @@ class IntegrationTest < Test::Unit::TestCase
@bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
assert @dummy.avatar = @file
assert @dummy.valid?
assert @dummy.valid?, @dummy.errors.full_messages.join(", ")
assert @dummy.save
end
......@@ -281,7 +406,7 @@ class IntegrationTest < Test::Unit::TestCase
Dummy.validates_attachment_presence :avatar
@d2 = Dummy.find(@dummy.id)
@d2.avatar = @file
assert @d2.valid?, @d2.errors.full_messages.inspect
assert @d2.valid?, @d2.errors.full_messages.inspect
@d2.avatar = @bad_file
assert ! @d2.valid?
end
......@@ -294,7 +419,25 @@ class IntegrationTest < Test::Unit::TestCase
@dummy.reload
assert_equal "5k.png", @dummy.avatar_file_name
end
[000,002,022].each do |umask|
context "when the umask is #{umask}" do
setup do
@umask = File.umask umask
end
teardown do
File.umask @umask
end
should "respect the current umask" do
@dummy.avatar = @file
@dummy.save
assert_equal 0666&~umask, 0666&File.stat(@dummy.avatar.path).mode
end
end
end
context "that is assigned its file from another Paperclip attachment" do
setup do
@dummy2 = Dummy.new
......@@ -302,7 +445,7 @@ class IntegrationTest < Test::Unit::TestCase
assert @dummy2.avatar = @file2
@dummy2.save
end
should "work when assigned a file" do
assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
`identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
......@@ -311,8 +454,9 @@ class IntegrationTest < Test::Unit::TestCase
@dummy.save
assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
`identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
assert_equal @dummy.avatar_file_name, @dummy2.avatar_file_name
end
end
end
end
......@@ -363,7 +507,6 @@ class IntegrationTest < Test::Unit::TestCase
:thumb => ["32x32#", :gif] },
:storage => :s3,
:whiny_thumbnails => true,
# :s3_options => {:logger => Logger.new(StringIO.new)},
:s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")),
:default_style => :medium,
:bucket => ENV['S3_TEST_BUCKET'],
......@@ -379,6 +522,18 @@ class IntegrationTest < Test::Unit::TestCase
@files_on_s3 = s3_files_for @dummy.avatar
end
context 'assigning itself to a new model' do
setup do
@d2 = Dummy.new
@d2.avatar = @dummy.avatar
@d2.save
end
should "have the same name as the old file" do
assert_equal @d2.avatar.original_filename, @dummy.avatar.original_filename
end
end
should "have the same contents as the original" do
@file.rewind
assert_equal @file.read, @files_on_s3[:original].read
......
require 'test/helper'
require './test/helper'
class InterpolationsTest < Test::Unit::TestCase
should "return all methods but the infrastructure when sent #all" do
......@@ -11,12 +11,12 @@ class InterpolationsTest < Test::Unit::TestCase
end
end
should "return the RAILS_ROOT" do
assert_equal RAILS_ROOT, Paperclip::Interpolations.rails_root(:attachment, :style)
should "return the Rails.root" do
assert_equal Rails.root, Paperclip::Interpolations.rails_root(:attachment, :style)
end
should "return the RAILS_ENV" do
assert_equal RAILS_ENV, Paperclip::Interpolations.rails_env(:attachment, :style)
should "return the Rails.env" do
assert_equal Rails.env, Paperclip::Interpolations.rails_env(:attachment, :style)
end
should "return the class of the Interpolations module when called with no params" do
......@@ -50,6 +50,37 @@ class InterpolationsTest < Test::Unit::TestCase
assert_equal "png", Paperclip::Interpolations.extension(attachment, :style)
end
should "return the extension of the file based on the content type" do
attachment = mock
attachment.expects(:content_type).returns('image/jpeg')
interpolations = Paperclip::Interpolations
interpolations.expects(:extension).returns('random')
assert_equal "jpeg", interpolations.content_type_extension(attachment, :style)
end
should "return the original extension of the file if it matches a content type extension" do
attachment = mock
attachment.expects(:content_type).returns('image/jpeg')
interpolations = Paperclip::Interpolations
interpolations.expects(:extension).returns('jpe')
assert_equal "jpe", interpolations.content_type_extension(attachment, :style)
end
should "return the latter half of the content type of the extension if no match found" do
attachment = mock
attachment.expects(:content_type).at_least_once().returns('not/found')
interpolations = Paperclip::Interpolations
interpolations.expects(:extension).returns('random')
assert_equal "found", interpolations.content_type_extension(attachment, :style)
end
should "return the #to_param of the attachment" do
attachment = mock
attachment.expects(:to_param).returns("23-awesome")
attachment.expects(:instance).returns(attachment)
assert_equal "23-awesome", Paperclip::Interpolations.param(attachment, :style)
end
should "return the id of the attachment" do
attachment = mock
attachment.expects(:id).returns(23)
......@@ -57,13 +88,20 @@ class InterpolationsTest < Test::Unit::TestCase
assert_equal 23, Paperclip::Interpolations.id(attachment, :style)
end
should "return the partitioned id of the attachment" do
should "return the partitioned id of the attachment when the id is an integer" do
attachment = mock
attachment.expects(:id).returns(23)
attachment.expects(:instance).returns(attachment)
assert_equal "000/000/023", Paperclip::Interpolations.id_partition(attachment, :style)
end
should "return the partitioned id of the attachment when the id is a string" do
attachment = mock
attachment.expects(:id).returns("32fnj23oio2f")
attachment.expects(:instance).returns(attachment)
assert_equal "32f/nj2/3oi", Paperclip::Interpolations.id_partition(attachment, :style)
end
should "return the name of the attachment" do
attachment = mock
attachment.expects(:name).returns("file")
......@@ -82,14 +120,17 @@ class InterpolationsTest < Test::Unit::TestCase
should "reinterpolate :url" do
attachment = mock
attachment.expects(:options).returns({:url => ":id"})
attachment.expects(:url).with(:style, false).returns("1234")
assert_equal "1234", Paperclip::Interpolations.url(attachment, :style)
end
should "raise if infinite loop detcted reinterpolating :url" do
attachment = mock
attachment.expects(:options).returns({:url => ":url"})
attachment = Object.new
class << attachment
def url(*args)
Paperclip::Interpolations.url(self, :style)
end
end
assert_raises(Paperclip::InfiniteInterpolationError){ Paperclip::Interpolations.url(attachment, :style) }
end
......@@ -107,11 +148,34 @@ class InterpolationsTest < Test::Unit::TestCase
assert_equal "one.png", Paperclip::Interpolations.filename(attachment, :style)
end
should "return the filename as basename when extension is blank" do
attachment = mock
attachment.stubs(:styles).returns({})
attachment.stubs(:original_filename).returns("one")
assert_equal "one", Paperclip::Interpolations.filename(attachment, :style)
end
should "return the timestamp" do
now = Time.now
zone = 'UTC'
attachment = mock
attachment.expects(:instance_read).with(:updated_at).returns(now)
assert_equal now.to_s, Paperclip::Interpolations.timestamp(attachment, :style)
attachment.expects(:time_zone).returns(zone)
assert_equal now.in_time_zone(zone).to_s, Paperclip::Interpolations.timestamp(attachment, :style)
end
should "return updated_at" do
attachment = mock
seconds_since_epoch = 1234567890
attachment.expects(:updated_at).returns(seconds_since_epoch)
assert_equal seconds_since_epoch, Paperclip::Interpolations.updated_at(attachment, :style)
end
should "return hash" do
attachment = mock
fake_hash = "a_wicked_secure_hash"
attachment.expects(:hash).returns(fake_hash)
assert_equal fake_hash, Paperclip::Interpolations.hash(attachment, :style)
end
should "call all expected interpolations with the given arguments" do
......
require 'test/helper'
require './test/helper'
class IOStreamTest < Test::Unit::TestCase
context "IOStream" do
should "be included in IO, File, Tempfile, and StringIO" do
[IO, File, Tempfile, StringIO].each do |klass|
assert klass.included_modules.include?(IOStream), "Not in #{klass}"
end
end
end
include IOStream
context "A file" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
......@@ -21,7 +14,7 @@ class IOStreamTest < Test::Unit::TestCase
context "and given a String" do
setup do
FileUtils.mkdir_p(File.join(ROOT, 'tmp'))
assert @result = @file.stream_to(File.join(ROOT, 'tmp', 'iostream.string.test'))
assert @result = stream_to(@file, File.join(ROOT, 'tmp', 'iostream.string.test'))
end
should "return a File" do
......@@ -38,7 +31,7 @@ class IOStreamTest < Test::Unit::TestCase
setup do
tempfile = Tempfile.new('iostream.test')
tempfile.binmode
assert @result = @file.stream_to(tempfile)
assert @result = stream_to(@file, tempfile)
end
should "return a Tempfile" do
......@@ -53,9 +46,9 @@ class IOStreamTest < Test::Unit::TestCase
end
context "that is sent #to_tempfile" do
context "that is converted #to_tempfile" do
setup do
assert @tempfile = @file.to_tempfile
assert @tempfile = to_tempfile(@file)
end
should "convert it to a Paperclip Tempfile" do
......@@ -66,7 +59,7 @@ class IOStreamTest < Test::Unit::TestCase
name = File.basename(@file.path)
extension = File.extname(name)
basename = File.basename(name, extension)
assert_match %r[^#{Regexp.quote(basename)}.*?#{Regexp.quote(extension)}], File.basename(@tempfile.path)
assert_match %r[^stream.*?#{Regexp.quote(extension)}], File.basename(@tempfile.path)
end
should "have the Tempfile contain the same data as the file" do
......
require 'test/helper'
require './test/helper'
class HaveAttachedFileMatcherTest < Test::Unit::TestCase
context "have_attached_file" do
......@@ -8,14 +8,17 @@ class HaveAttachedFileMatcherTest < Test::Unit::TestCase
@matcher = self.class.have_attached_file(:avatar)
end
should "reject a class with no attachment" do
assert_rejects @matcher, @dummy_class
context "given a class with no attachment" do
should_reject_dummy_class
end
should "accept a class with an attachment" do
modify_table("dummies"){|d| d.string :avatar_file_name }
@dummy_class.has_attached_file :avatar
assert_accepts @matcher, @dummy_class
context "given a class with an attachment" do
setup do
modify_table("dummies"){|d| d.string :avatar_file_name }
@dummy_class.has_attached_file :avatar
end
should_accept_dummy_class
end
end
end
require 'test/helper'
require './test/helper'
class ValidateAttachmentContentTypeMatcherTest < Test::Unit::TestCase
context "validate_attachment_content_type" do
setup do
reset_table("dummies") do |d|
d.string :title
d.string :avatar_file_name
d.string :avatar_content_type
end
......@@ -14,18 +15,73 @@ class ValidateAttachmentContentTypeMatcherTest < Test::Unit::TestCase
rejecting(%w(audio/mp3 application/octet-stream))
end
should "reject a class with no validation" do
assert_rejects @matcher, @dummy_class
context "given a class with no validation" do
should_reject_dummy_class
end
should "reject a class with a validation that doesn't match" do
@dummy_class.validates_attachment_content_type :avatar, :content_type => %r{audio/.*}
assert_rejects @matcher, @dummy_class
context "given a class with a validation that doesn't match" do
setup do
@dummy_class.validates_attachment_content_type :avatar, :content_type => %r{audio/.*}
end
should_reject_dummy_class
end
context "given a class with a matching validation" do
setup do
@dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
end
should_accept_dummy_class
end
context "given a class with other validations but matching types" do
setup do
@dummy_class.validates_presence_of :title
@dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
end
should_accept_dummy_class
end
context "given a class that matches and a matcher that only specifies 'allowing'" do
setup do
@dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
@matcher = self.class.validate_attachment_content_type(:avatar).
allowing(%w(image/png image/jpeg))
end
should_accept_dummy_class
end
context "given a class that does not match and a matcher that only specifies 'allowing'" do
setup do
@dummy_class.validates_attachment_content_type :avatar, :content_type => %r{audio/.*}
@matcher = self.class.validate_attachment_content_type(:avatar).
allowing(%w(image/png image/jpeg))
end
should_reject_dummy_class
end
should "accept a class with a validation" do
@dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
assert_accepts @matcher, @dummy_class
context "given a class that matches and a matcher that only specifies 'rejecting'" do
setup do
@dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
@matcher = self.class.validate_attachment_content_type(:avatar).
rejecting(%w(audio/mp3 application/octet-stream))
end
should_accept_dummy_class
end
context "given a class that does not match and a matcher that only specifies 'rejecting'" do
setup do
@dummy_class.validates_attachment_content_type :avatar, :content_type => %r{audio/.*}
@matcher = self.class.validate_attachment_content_type(:avatar).
rejecting(%w(audio/mp3 application/octet-stream))
end
should_reject_dummy_class
end
end
end
require 'test/helper'
require './test/helper'
class ValidateAttachmentPresenceMatcherTest < Test::Unit::TestCase
context "validate_attachment_presence" do
......@@ -11,13 +11,16 @@ class ValidateAttachmentPresenceMatcherTest < Test::Unit::TestCase
@matcher = self.class.validate_attachment_presence(:avatar)
end
should "reject a class with no validation" do
assert_rejects @matcher, @dummy_class
context "given a class with no validation" do
should_reject_dummy_class
end
should "accept a class with a validation" do
@dummy_class.validates_attachment_presence :avatar
assert_accepts @matcher, @dummy_class
context "given a class with a matching validation" do
setup do
@dummy_class.validates_attachment_presence :avatar
end
should_accept_dummy_class
end
end
end
require 'test/helper'
require './test/helper'
class ValidateAttachmentSizeMatcherTest < Test::Unit::TestCase
context "validate_attachment_size" do
......@@ -14,37 +14,37 @@ class ValidateAttachmentSizeMatcherTest < Test::Unit::TestCase
context "of limited size" do
setup{ @matcher = self.class.validate_attachment_size(:avatar).in(256..1024) }
should "reject a class with no validation" do
assert_rejects @matcher, @dummy_class
context "given a class with no validation" do
should_reject_dummy_class
end
should "reject a class with a validation that's too high" do
@dummy_class.validates_attachment_size :avatar, :in => 256..2048
assert_rejects @matcher, @dummy_class
context "given a class with a validation that's too high" do
setup { @dummy_class.validates_attachment_size :avatar, :in => 256..2048 }
should_reject_dummy_class
end
should "reject a class with a validation that's too low" do
@dummy_class.validates_attachment_size :avatar, :in => 0..1024
assert_rejects @matcher, @dummy_class
context "given a class with a validation that's too low" do
setup { @dummy_class.validates_attachment_size :avatar, :in => 0..1024 }
should_reject_dummy_class
end
should "accept a class with a validation that matches" do
@dummy_class.validates_attachment_size :avatar, :in => 256..1024
assert_accepts @matcher, @dummy_class
context "given a class with a validation that matches" do
setup { @dummy_class.validates_attachment_size :avatar, :in => 256..1024 }
should_accept_dummy_class
end
end
context "validates_attachment_size with infinite range" do
setup{ @matcher = self.class.validate_attachment_size(:avatar) }
should "accept a class with an upper limit" do
@dummy_class.validates_attachment_size :avatar, :less_than => 1
assert_accepts @matcher, @dummy_class
context "given a class with an upper limit" do
setup { @dummy_class.validates_attachment_size :avatar, :less_than => 1 }
should_accept_dummy_class
end
should "accept a class with no upper limit" do
@dummy_class.validates_attachment_size :avatar, :greater_than => 1
assert_accepts @matcher, @dummy_class
context "given a class with no upper limit" do
setup { @dummy_class.validates_attachment_size :avatar, :greater_than => 1 }
should_accept_dummy_class
end
end
end
......
require 'test/helper'
require './test/helper'
class PaperclipTest < Test::Unit::TestCase
[:image_magick_path, :command_path].each do |path|
context "Calling Paperclip.run with #{path} specified" do
setup do
Paperclip.options[:image_magick_path] = nil
Paperclip.options[:command_path] = nil
Paperclip.options[path] = "/usr/bin"
end
should "return the expected path for path_for_command" do
assert_equal "/usr/bin/convert", Paperclip.path_for_command("convert")
end
should "execute the right command" do
Paperclip.expects(:path_for_command).with("convert").returns("/usr/bin/convert")
Paperclip.expects(:bit_bucket).returns("/dev/null")
Paperclip.expects(:"`").with("/usr/bin/convert one.jpg two.jpg 2>/dev/null")
Paperclip.run("convert", "one.jpg two.jpg")
end
context "Calling Paperclip.run" do
should "run the command with Cocaine" do
Cocaine::CommandLine.expects(:new).with("convert", "stuff").returns(stub(:run))
Paperclip.run("convert", "stuff")
end
end
context "Calling Paperclip.run with no path specified" do
context "Paperclip.each_instance_with_attachment" do
setup do
Paperclip.options[:image_magick_path] = nil
Paperclip.options[:command_path] = nil
end
should "return the expected path fro path_for_command" do
assert_equal "convert", Paperclip.path_for_command("convert")
end
should "execute the right command" do
Paperclip.expects(:path_for_command).with("convert").returns("convert")
Paperclip.expects(:bit_bucket).returns("/dev/null")
Paperclip.expects(:"`").with("convert one.jpg two.jpg 2>/dev/null")
Paperclip.run("convert", "one.jpg two.jpg")
end
end
context "Calling Paperclip.run and logging" do
setup do
Paperclip.options[:image_magick_path] = nil
Paperclip.options[:command_path] = nil
Paperclip.stubs(:bit_bucket).returns("/dev/null")
Paperclip.stubs(:log)
Paperclip.stubs(:"`").with("this is the command 2>/dev/null")
end
should "log the command when :log_command is true" do
Paperclip.options[:log_command] = true
Paperclip.run("this","is the command")
assert_received(Paperclip, :log) do |p|
p.with("this is the command 2>/dev/null")
end
assert_received(Paperclip, :`) do |p|
p.with("this is the command 2>/dev/null")
end
end
should "not log the command when :log_command is false" do
Paperclip.options[:log_command] = false
Paperclip.run("this","is the command")
assert_received(Paperclip, :log) do |p|
p.with("this is the command 2>/dev/null").never
end
assert_received(Paperclip, :`) do |p|
p.with("this is the command 2>/dev/null")
end
end
end
context "Paperclip.bit_bucket" do
context "on systems without /dev/null" do
setup do
File.expects(:exists?).with("/dev/null").returns(false)
end
should "return 'NUL'" do
assert_equal "NUL", Paperclip.bit_bucket
end
@file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
d1 = Dummy.create(:avatar => @file)
d2 = Dummy.create
d3 = Dummy.create(:avatar => @file)
@expected = [d1, d3]
end
context "on systems with /dev/null" do
setup do
File.expects(:exists?).with("/dev/null").returns(true)
end
should "return '/dev/null'" do
assert_equal "/dev/null", Paperclip.bit_bucket
should "yield every instance of a model that has an attachment" do
actual = []
Paperclip.each_instance_with_attachment("Dummy", "avatar") do |instance|
actual << instance
end
assert_same_elements @expected, actual
end
end
......@@ -106,6 +37,19 @@ class PaperclipTest < Test::Unit::TestCase
assert_equal ::Paperclip::Thumbnail, Paperclip.processor(:thumbnail)
end
should "get a class from a namespaced class name" do
class ::One; class Two; end; end
assert_equal ::One::Two, Paperclip.class_for("One::Two")
end
should "raise when class doesn't exist in specified namespace" do
class ::Three; end
class ::Four; end
assert_raise NameError do
Paperclip.class_for("Three::Four")
end
end
context "An ActiveRecord model with an 'avatar' attachment" do
setup do
rebuild_model :path => "tmp/:class/omg/:style.:extension"
......@@ -133,7 +77,7 @@ class PaperclipTest < Test::Unit::TestCase
should "not assign the avatar on mass-set" do
@dummy.attributes = { :other => "I'm set!",
:avatar => @file }
assert_equal "I'm set!", @dummy.other
assert ! @dummy.avatar?
end
......@@ -141,7 +85,7 @@ class PaperclipTest < Test::Unit::TestCase
should "still allow assigment on normal set" do
@dummy.other = "I'm set!"
@dummy.avatar = @file
assert_equal "I'm set!", @dummy.other
assert @dummy.avatar?
end
......@@ -223,9 +167,16 @@ class PaperclipTest < Test::Unit::TestCase
end
end
should "not have Attachment in the ActiveRecord::Base namespace" do
assert_raises(NameError) do
ActiveRecord::Base::Attachment
end
end
def self.should_validate validation, options, valid_file, invalid_file
context "with #{validation} validation and #{options.inspect} options" do
setup do
rebuild_class
Dummy.send(:"validates_attachment_#{validation}", :avatar, options)
@dummy = Dummy.new
end
......@@ -236,11 +187,11 @@ class PaperclipTest < Test::Unit::TestCase
end
if validation == :presence
should "have an error on the attachment" do
assert @dummy.errors.on(:avatar_file_name)
assert @dummy.errors[:avatar_file_name]
end
else
should "not have an error on the attachment" do
assert_nil @dummy.errors.on(:avatar_file_name), @dummy.errors.full_messages.join(", ")
assert @dummy.errors.blank?, @dummy.errors.full_messages.join(", ")
end
end
end
......@@ -250,7 +201,7 @@ class PaperclipTest < Test::Unit::TestCase
@dummy.valid?
end
should "not have an error when assigned a valid file" do
assert_equal 0, @dummy.errors.length, @dummy.errors.full_messages.join(", ")
assert_equal 0, @dummy.errors.size, @dummy.errors.full_messages.join(", ")
end
end
context "and assigned an invalid file" do
......@@ -259,7 +210,7 @@ class PaperclipTest < Test::Unit::TestCase
@dummy.valid?
end
should "have an error when assigned a valid file" do
assert @dummy.errors.length > 0
assert @dummy.errors.size > 0
end
end
end
......@@ -275,10 +226,25 @@ class PaperclipTest < Test::Unit::TestCase
validation, options, valid_file, invalid_file = args
valid_file &&= File.open(File.join(FIXTURES_DIR, valid_file), "rb")
invalid_file &&= File.open(File.join(FIXTURES_DIR, invalid_file), "rb")
should_validate validation, options, valid_file, invalid_file
end
context "with content_type validation and lambda message" do
context "and assigned an invalid file" do
setup do
Dummy.send(:"validates_attachment_content_type", :avatar, :content_type => %r{image/.*}, :message => lambda {'lambda content type message'})
@dummy = Dummy.new
@dummy.avatar &&= File.open(File.join(FIXTURES_DIR, "text.txt"), "rb")
@dummy.valid?
end
should "have a content type error message" do
assert [@dummy.errors[:avatar_content_type]].flatten.any?{|error| error =~ %r/lambda content type message/ }
end
end
end
context "with size validation and less_than 10240 option" do
context "and assigned an invalid file" do
setup do
......@@ -287,9 +253,24 @@ class PaperclipTest < Test::Unit::TestCase
@dummy.avatar &&= File.open(File.join(FIXTURES_DIR, "12k.png"), "rb")
@dummy.valid?
end
should "have a file size min/max error message" do
assert [@dummy.errors[:avatar_file_size]].flatten.any?{|error| error =~ %r/between 0 and 10240 bytes/ }
end
end
end
context "with size validation and less_than 10240 option with lambda message" do
context "and assigned an invalid file" do
setup do
Dummy.send(:"validates_attachment_size", :avatar, :less_than => 10240, :message => lambda {'lambda between 0 and 10240 bytes'})
@dummy = Dummy.new
@dummy.avatar &&= File.open(File.join(FIXTURES_DIR, "12k.png"), "rb")
@dummy.valid?
end
should "have a file size min/max error message" do
assert_match %r/between 0 and 10240 bytes/, @dummy.errors.on(:avatar_file_size)
assert [@dummy.errors[:avatar_file_size]].flatten.any?{|error| error =~ %r/lambda between 0 and 10240 bytes/ }
end
end
end
......
require 'test/helper'
require './test/helper'
class ProcessorTest < Test::Unit::TestCase
should "instantiate and call #make when sent #make to the class" do
......
# encoding: utf-8
require 'test/helper'
require './test/helper'
class StyleTest < Test::Unit::TestCase
......@@ -9,7 +9,7 @@ class StyleTest < Test::Unit::TestCase
:styles => { :foo => {:geometry => "100x100#", :format => :png} }
@style = @attachment.styles[:foo]
end
should "be held as a Style object" do
assert_kind_of Paperclip::Style, @style
end
......@@ -21,18 +21,18 @@ class StyleTest < Test::Unit::TestCase
should "have the right geometry" do
assert_equal "100x100#", @style.geometry
end
should "be whiny if the attachment is" do
@attachment.expects(:whiny).returns(true)
assert @style.whiny?
end
should "respond to hash notation" do
assert_equal [:thumbnail], @style[:processors]
assert_equal "100x100#", @style[:geometry]
end
end
context "A style rule with properties supplied as procs" do
setup do
@attachment = attachment :path => ":basename.:extension",
......@@ -45,13 +45,13 @@ class StyleTest < Test::Unit::TestCase
}
}
end
should "defer processing of procs until they are needed" do
assert_kind_of Proc, @attachment.styles[:foo].instance_variable_get("@geometry")
assert_kind_of Proc, @attachment.styles[:bar].instance_variable_get("@geometry")
assert_kind_of Proc, @attachment.instance_variable_get("@processors")
end
should "call procs when they are needed" do
assert_equal "300x300#", @attachment.styles[:foo].geometry
assert_equal "300x300#", @attachment.styles[:bar].geometry
......@@ -62,24 +62,24 @@ class StyleTest < Test::Unit::TestCase
context "An attachment with style rules in various forms" do
setup do
styles = ActiveSupport::OrderedHash.new
styles[:aslist] = ["100x100", :png]
styles[:ashash] = {:geometry => "100x100", :format => :png}
styles[:asstring] = "100x100"
@attachment = attachment :path => ":basename.:extension",
:styles => {
:aslist => ["100x100", :png],
:ashash => {:geometry => "100x100", :format => :png},
:asstring => "100x100"
}
:styles => styles
end
should "have the right number of styles" do
assert_kind_of Hash, @attachment.styles
assert_equal 3, @attachment.styles.size
end
should "have styles as Style objects" do
[:aslist, :ashash, :aslist].each do |s|
assert_kind_of Paperclip::Style, @attachment.styles[s]
end
end
should "have the right geometries" do
[:aslist, :ashash, :aslist].each do |s|
assert_equal @attachment.styles[s].geometry, "100x100"
......@@ -92,6 +92,9 @@ class StyleTest < Test::Unit::TestCase
assert_nil @attachment.styles[:asstring].format
end
should "retain order" do
assert_equal [:aslist, :ashash, :asstring], @attachment.styles.keys
end
end
context "An attachment with :convert_options" do
......@@ -107,35 +110,77 @@ class StyleTest < Test::Unit::TestCase
before_should "not have called extra_options_for(:thumb/:large) on initialization" do
@attachment.expects(:extra_options_for).never
end
should "call extra_options_for(:thumb/:large) when convert options are requested" do
@attachment.expects(:extra_options_for).with(:thumb)
@attachment.styles[:thumb].convert_options
end
end
context "An attachment with :source_file_options" do
setup do
@attachment = attachment :path => ":basename.:extension",
:styles => {:thumb => "100x100", :large => "400x400"},
:source_file_options => {:all => "-density 400", :thumb => "-depth 8"}
@style = @attachment.styles[:thumb]
@file = StringIO.new("...")
@file.stubs(:original_filename).returns("file.jpg")
end
before_should "not have called extra_source_file_options_for(:thumb/:large) on initialization" do
@attachment.expects(:extra_source_file_options_for).never
end
should "call extra_options_for(:thumb/:large) when convert options are requested" do
@attachment.expects(:extra_source_file_options_for).with(:thumb)
@attachment.styles[:thumb].source_file_options
end
end
context "A style rule with its own :processors" do
setup do
@attachment = attachment :path => ":basename.:extension",
:styles => {
:styles => {
:foo => {
:geometry => "100x100#",
:geometry => "100x100#",
:format => :png,
:processors => [:test]
}
}
},
:processors => [:thumbnail]
@style = @attachment.styles[:foo]
end
should "not get processors from the attachment" do
@attachment.expects(:processors).never
assert_not_equal [:thumbnail], @style.processors
end
should "report its own processors" do
assert_equal [:test], @style.processors
end
end
context "A style rule with :processors supplied as procs" do
setup do
@attachment = attachment :path => ":basename.:extension",
:styles => {
:foo => {
:geometry => "100x100#",
:format => :png,
:processors => lambda{|a| [:test]}
}
},
:processors => [:thumbnail]
end
should "defer processing of procs until they are needed" do
assert_kind_of Proc, @attachment.styles[:foo].instance_variable_get("@processors")
end
should "call procs when they are needed" do
assert_equal [:test], @attachment.styles[:foo].processors
end
end
end
require 'test/helper'
require './test/helper'
class ThumbnailTest < Test::Unit::TestCase
context "A Paperclip Tempfile" do
setup do
@tempfile = Paperclip::Tempfile.new("file.jpg")
@tempfile = Paperclip::Tempfile.new(["file", ".jpg"])
end
should "have its path contain a real extension" do
......@@ -47,7 +47,7 @@ class ThumbnailTest < Test::Unit::TestCase
end
should "start with dimensions of 434x66" do
cmd = %Q[identify -format "%wx%h" "#{@file.path}"]
cmd = %Q[identify -format "%wx%h" "#{@file.path}"]
assert_equal "434x66", `#{cmd}`.chomp
end
......@@ -61,7 +61,7 @@ class ThumbnailTest < Test::Unit::TestCase
end
should "be the size we expect it to be" do
cmd = %Q[identify -format "%wx%h" "#{@thumb_result.path}"]
cmd = %Q[identify -format "%wx%h" "#{@thumb_result.path}"]
assert_equal args[1], `#{cmd}`.chomp
end
end
......@@ -73,6 +73,18 @@ class ThumbnailTest < Test::Unit::TestCase
@thumb = Paperclip::Thumbnail.new(@file, :geometry => "100x50#")
end
should "let us know when a command isn't found versus a processing error" do
old_path = ENV['PATH']
begin
ENV['PATH'] = ''
assert_raises(Paperclip::CommandNotFoundError) do
@thumb.make
end
ensure
ENV['PATH'] = old_path
end
end
should "report its correct current and target geometries" do
assert_equal "100x50#", @thumb.target_geometry.to_s
assert_equal "434x66", @thumb.current_geometry.to_s
......@@ -85,14 +97,20 @@ class ThumbnailTest < Test::Unit::TestCase
should "have whiny turned on by default" do
assert @thumb.whiny
end
should "have convert_options set to nil by default" do
assert_equal nil, @thumb.convert_options
end
should "have source_file_options set to nil by default" do
assert_equal nil, @thumb.source_file_options
end
should "send the right command to convert when sent #make" do
Paperclip.expects(:"`").with do |arg|
arg.match %r{convert\s+"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+\"x50\"\s+-crop\s+\"100x50\+114\+0\"\s+\+repage\s+".*?"}
Paperclip.expects(:run).with do |*arg|
arg[0] == 'convert' &&
arg[1] == ':source -resize "x50" -crop "100x50+114+0" +repage :dest' &&
arg[2][:source] == "#{File.expand_path(@thumb.file.path)}[0]"
end
@thumb.make
end
......@@ -102,7 +120,7 @@ class ThumbnailTest < Test::Unit::TestCase
assert_match /100x50/, `identify "#{dst.path}"`
end
end
context "being thumbnailed with source file options set" do
setup do
@thumb = Paperclip::Thumbnail.new(@file,
......@@ -111,12 +129,14 @@ class ThumbnailTest < Test::Unit::TestCase
end
should "have source_file_options value set" do
assert_equal "-strip", @thumb.source_file_options
assert_equal ["-strip"], @thumb.source_file_options
end
should "send the right command to convert when sent #make" do
Paperclip.expects(:"`").with do |arg|
arg.match %r{convert\s+-strip\s+"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+"x50"\s+-crop\s+"100x50\+114\+0"\s+\+repage\s+".*?"}
Paperclip.expects(:run).with do |*arg|
arg[0] == 'convert' &&
arg[1] == '-strip :source -resize "x50" -crop "100x50+114+0" +repage :dest' &&
arg[2][:source] == "#{File.expand_path(@thumb.file.path)}[0]"
end
@thumb.make
end
......@@ -125,7 +145,7 @@ class ThumbnailTest < Test::Unit::TestCase
dst = @thumb.make
assert_match /100x50/, `identify "#{dst.path}"`
end
context "redefined to have bad source_file_options setting" do
setup do
@thumb = Paperclip::Thumbnail.new(@file,
......@@ -138,7 +158,7 @@ class ThumbnailTest < Test::Unit::TestCase
@thumb.make
end
end
end
end
end
context "being thumbnailed with convert options set" do
......@@ -149,12 +169,14 @@ class ThumbnailTest < Test::Unit::TestCase
end
should "have convert_options value set" do
assert_equal "-strip -depth 8", @thumb.convert_options
assert_equal %w"-strip -depth 8", @thumb.convert_options
end
should "send the right command to convert when sent #make" do
Paperclip.expects(:"`").with do |arg|
arg.match %r{convert\s+"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+"x50"\s+-crop\s+"100x50\+114\+0"\s+\+repage\s+-strip\s+-depth\s+8\s+".*?"}
Paperclip.expects(:run).with do |*arg|
arg[0] == 'convert' &&
arg[1] == ':source -resize "x50" -crop "100x50+114+0" +repage -strip -depth 8 :dest' &&
arg[2][:source] == "#{File.expand_path(@thumb.file.path)}[0]"
end
@thumb.make
end
......@@ -163,7 +185,7 @@ class ThumbnailTest < Test::Unit::TestCase
dst = @thumb.make
assert_match /100x50/, `identify "#{dst.path}"`
end
context "redefined to have bad convert_options setting" do
setup do
@thumb = Paperclip::Thumbnail.new(@file,
......@@ -176,18 +198,30 @@ class ThumbnailTest < Test::Unit::TestCase
@thumb.make
end
end
end
should "let us know when a command isn't found versus a processing error" do
old_path = ENV['PATH']
begin
ENV['PATH'] = ''
assert_raises(Paperclip::CommandNotFoundError) do
@thumb.make
end
ensure
ENV['PATH'] = old_path
end
end
end
end
context "being thumbnailed with a blank geometry string" do
setup do
@thumb = Paperclip::Thumbnail.new(@file,
:geometry => "",
:convert_options => "-gravity center -crop \"300x300+0-0\"")
end
should "not get resized by default" do
assert_no_match(/-resize/, @thumb.transformation_command)
assert !@thumb.transformation_command.include?("-resize")
end
end
end
......@@ -224,4 +258,79 @@ class ThumbnailTest < Test::Unit::TestCase
end
end
end
context "An animated gif" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "animated.gif"), 'rb')
end
teardown { @file.close }
should "start with 12 frames with size 100x100" do
cmd = %Q[identify -format "%wx%h" "#{@file.path}"]
assert_equal "100x100"*12, `#{cmd}`.chomp
end
context "with static output" do
setup do
@thumb = Paperclip::Thumbnail.new(@file, :geometry => "50x50", :format => :jpg)
end
should "create the single frame thumbnail when sent #make" do
dst = @thumb.make
cmd = %Q[identify -format "%wx%h" "#{dst.path}"]
assert_equal "50x50", `#{cmd}`.chomp
end
end
context "with animated output format" do
setup do
@thumb = Paperclip::Thumbnail.new(@file, :geometry => "50x50", :format => :gif)
end
should "create the 12 frames thumbnail when sent #make" do
dst = @thumb.make
cmd = %Q[identify -format "%wx%h" "#{dst.path}"]
assert_equal "50x50"*12, `#{cmd}`.chomp
end
should "use the -coalesce option" do
assert_equal @thumb.transformation_command.first, "-coalesce"
end
end
context "with omitted output format" do
setup do
@thumb = Paperclip::Thumbnail.new(@file, :geometry => "50x50")
end
should "create the 12 frames thumbnail when sent #make" do
dst = @thumb.make
cmd = %Q[identify -format "%wx%h" "#{dst.path}"]
assert_equal "50x50"*12, `#{cmd}`.chomp
end
should "use the -coalesce option" do
assert_equal @thumb.transformation_command.first, "-coalesce"
end
end
context "with animated option set to false" do
setup do
@thumb = Paperclip::Thumbnail.new(@file, :geometry => "50x50", :animated => false)
end
should "output the gif format" do
dst = @thumb.make
cmd = %Q[identify "#{dst.path}"]
assert_match /GIF/, `#{cmd}`.chomp
end
should "create the single frame thumbnail when sent #make" do
dst = @thumb.make
cmd = %Q[identify -format "%wx%h" "#{dst.path}"]
assert_equal "50x50", `#{cmd}`.chomp
end
end
end
end
require 'test/helper'
require './test/helper'
class UpfileTest < Test::Unit::TestCase
{ %w(jpg jpe jpeg) => 'image/jpeg',
......@@ -6,12 +6,13 @@ class UpfileTest < Test::Unit::TestCase
%w(png) => 'image/png',
%w(gif) => 'image/gif',
%w(bmp) => 'image/bmp',
%w(svg) => 'image/svg+xml',
%w(txt) => 'text/plain',
%w(htm html) => 'text/html',
%w(csv) => 'text/csv',
%w(xml) => 'text/xml',
%w(xml) => 'application/xml',
%w(css) => 'text/css',
%w(js) => 'application/js',
%w(js) => 'application/javascript',
%w(foo) => 'application/x-foo'
}.each do |extensions, content_type|
extensions.each do |extension|
......@@ -33,4 +34,12 @@ class UpfileTest < Test::Unit::TestCase
end
end
end
should "return a content_type of text/plain on a real file whose content_type is determined with the file command" do
file = File.new(File.join(File.dirname(__FILE__), "..", "LICENSE"))
class << file
include Paperclip::Upfile
end
assert_equal 'text/plain', file.content_type
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment