Skip to content

Commit

Permalink
Merge branch 'thoughtbot/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
luke0x committed Sep 18, 2009
2 parents 6def8e6 + d6ebd32 commit 958cba5
Show file tree
Hide file tree
Showing 14 changed files with 157 additions and 27 deletions.
6 changes: 6 additions & 0 deletions cucumber/paperclip_steps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
When /^I attach an? "([^\"]*)" "([^\"]*)" file to an? "([^\"]*)" on S3$/ do |attachment, extension, model|
stub_paperclip_s3(model, attachment, extension)
attach_file attachment,
"features/support/paperclip/#{model.gsub(" ", "_").underscore}/#{attachment}.#{extension}"
end

7 changes: 5 additions & 2 deletions lib/paperclip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#
# See the +has_attached_file+ documentation for more details.

require 'erb'
require 'tempfile'
require 'paperclip/upfile'
require 'paperclip/iostream'
Expand All @@ -44,7 +45,7 @@
# documentation for Paperclip::ClassMethods for more useful information.
module Paperclip

VERSION = "2.3.0"
VERSION = "2.3.1"

class << self
# Provides configurability to Paperclip. There are a number of options available, such as:
Expand Down Expand Up @@ -257,7 +258,9 @@ def validates_attachment_size name, options = {}
range = (min..max)
message = options[:message] || "file size must be between :min and :max bytes."

attachment_definitions[name][:validations] << [:size, {:range => range,
attachment_definitions[name][:validations] << [:size, {:min => min,
:max => max,
:range => range,
:message => message,
:if => options[:if],
:unless => options[:unless]}]
Expand Down
2 changes: 1 addition & 1 deletion lib/paperclip/attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def content_type
# lives in the <attachment>_updated_at attribute of the model.
def updated_at
time = instance_read(:updated_at)
time && time.to_i
time && time.to_f.to_i
end

# Paths and URLs can have a number of variables interpolated into them
Expand Down
5 changes: 4 additions & 1 deletion lib/paperclip/interpolations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ def rails_env attachment, style

# Returns the underscored, pluralized version of the class name.
# e.g. "users" for the User class.
def class attachment, style
# NOTE: The arguments need to be optional, because some tools fetch
# all class names. Calling #class will return the expected class.
def class attachment = nil, style = nil
return super() if attachment.nil? && style.nil?
attachment.instance.class.to_s.underscore.pluralize
end

Expand Down
16 changes: 11 additions & 5 deletions lib/paperclip/storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ def flush_deletes #:nodoc:
# * +s3_permissions+: This is a String that should be one of the "canned" access
# policies that S3 provides (more information can be found here:
# http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html#RESTCannedAccessPolicies)
# The default for Paperclip is "public-read".
# The default for Paperclip is :public_read.
# * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
# 'http' or 'https'. Defaults to 'http' when your :s3_permissions are 'public-read' (the
# 'http' or 'https'. Defaults to 'http' when your :s3_permissions are :public_read (the
# default), and 'https' when your :s3_permissions are anything else.
# * +s3_headers+: A hash of headers such as {'Expires' => 1.year.from_now.httpdate}
# * +bucket+: This is the name of the S3 bucket that will store your files. Remember
Expand Down Expand Up @@ -127,7 +127,13 @@ def flush_deletes #:nodoc:
# separate parts of your file name.
module S3
def self.extended base
require 'aws/s3'
begin
require 'aws/s3'
rescue LoadError => e
e.message << " (You may need to install the aws-s3 gem)"
raise e
end

base.instance_eval do
@s3_credentials = parse_credentials(@options[:s3_credentials])
@bucket = @options[:bucket] || @s3_credentials[:bucket]
Expand Down Expand Up @@ -221,9 +227,9 @@ def flush_deletes #:nodoc:
def find_credentials creds
case creds
when File
YAML.load_file(creds.path)
YAML::load(ERB.new(File.read(creds.path)).result)
when String
YAML.load_file(creds)
YAML::load(ERB.new(File.read(creds)).result)
when Hash
creds
else
Expand Down
4 changes: 2 additions & 2 deletions lib/paperclip/thumbnail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def crop?

# Returns true if the image is meant to make use of additional convert options.
def convert_options?
not @convert_options.blank?
!@convert_options.nil? && !@convert_options.empty?
end

# Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile
Expand Down Expand Up @@ -64,7 +64,7 @@ def make
def transformation_command
scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
trans = ""
trans << " -resize \"#{scale}\"" unless scale.blank?
trans << " -resize \"#{scale}\"" unless scale.nil? || scale.empty?
trans << " -crop \"#{crop}\" +repage" if crop
trans << " #{convert_options}" if convert_options?
trans
Expand Down
4 changes: 2 additions & 2 deletions paperclip.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

Gem::Specification.new do |s|
s.name = %q{paperclip}
s.version = "2.3.0"
s.version = "2.3.1"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Jon Yurek"]
s.date = %q{2009-06-18}
s.date = %q{2009-08-21}
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", "shoulda_macros/paperclip.rb"]
Expand Down
49 changes: 49 additions & 0 deletions shoulda_macros/paperclip.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'paperclip/matchers'
require 'action_controller'

module Paperclip
# =Paperclip Shoulda Macros
Expand Down Expand Up @@ -60,9 +61,57 @@ def should_validate_attachment_size name, options = {}
assert_accepts(matcher, klass)
end
end

# Stubs the HTTP PUT for an attachment using S3 storage.
#
# @example
# stub_paperclip_s3('user', 'avatar', 'png')
def stub_paperclip_s3(model, attachment, extension)
definition = model.gsub(" ", "_").classify.constantize.
attachment_definitions[attachment.to_sym]

path = "http://s3.amazonaws.com/:id/#{definition[:path]}"
path.gsub!(/:([^\/\.]+)/) do |match|
"([^\/\.]+)"
end

begin
FakeWeb.register_uri(:put, Regexp.new(path), :body => "OK")
rescue NameError
raise NameError, "the stub_paperclip_s3 shoulda macro requires the fakeweb gem."
end
end

# Stub S3 and return a file for attachment. Best with Factory Girl.
# Uses a strict directory convention:
#
# features/support/paperclip
#
# This method is used by the Paperclip-provided Cucumber step:
#
# When I attach a "demo_tape" "mp3" file to a "band" on S3
#
# @example
# Factory.define :band_with_demo_tape, :parent => :band do |band|
# band.demo_tape { band.paperclip_fixture("band", "demo_tape", "png") }
# end
def paperclip_fixture(model, attachment, extension)
stub_paperclip_s3(model, attachment, extension)
base_path = File.join(File.dirname(__FILE__), "..", "..",
"features", "support", "paperclip")
File.new(File.join(base_path, model, "#{attachment}.#{extension}"))
end
end
end

class ActionController::Integration::Session #:nodoc:
include Paperclip::Shoulda
end

class Factory
include Paperclip::Shoulda #:nodoc:
end

class Test::Unit::TestCase #:nodoc:
extend Paperclip::Shoulda
end
11 changes: 6 additions & 5 deletions test/attachment_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -531,9 +531,10 @@ def do_after_all; end
@attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
@attachment.stubs(:instance_read).with(:content_type).returns("image/png")
@attachment.stubs(:instance_read).with(:file_size).returns(12345)
now = Time.now
Time.stubs(:now).returns(now)
@attachment.stubs(:instance_read).with(:updated_at).returns(Time.now)
dtnow = DateTime.now
@now = Time.now
Time.stubs(:now).returns(@now)
@attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
end

should "return a correct url even if the file does not exist" do
Expand All @@ -542,11 +543,11 @@ def do_after_all; end
end

should "make sure the updated_at mtime is in the url if it is defined" do
assert_match %r{#{Time.now.to_i}$}, @attachment.url(:blah)
assert_match %r{#{@now.to_i}$}, @attachment.url(:blah)
end

should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
assert_no_match %r{#{Time.now.to_i}$}, @attachment.url(:blah, false)
assert_no_match %r{#{@now.to_i}$}, @attachment.url(:blah, false)
end

context "with the updated_at field removed" do
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/s3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ development:
key: 54321
production:
key: 12345
test:
bucket: <%= ENV['S3_BUCKET'] %>
access_key_id: <%= ENV['S3_KEY'] %>
secret_access_key: <%= ENV['S3_SECRET'] %>
11 changes: 10 additions & 1 deletion test/helper.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
require 'rubygems'
require 'test/unit'
require 'shoulda'
require 'mocha'
require 'tempfile'

gem 'jferris-mocha', '0.9.5.0.1241126838'
require 'mocha'

gem 'sqlite3-ruby'

require 'active_record'
Expand Down Expand Up @@ -97,3 +99,10 @@ def run_callbacks name, *args
def attachment options
Paperclip::Attachment.new(:avatar, FakeModel.new, options)
end

def silence_warnings
old_verbose, $VERBOSE = $VERBOSE, nil
yield
ensure
$VERBOSE = old_verbose
end
6 changes: 5 additions & 1 deletion test/interpolations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class InterpolationsTest < Test::Unit::TestCase
assert ! methods.include?(:[]=)
assert ! methods.include?(:all)
methods.each do |m|
assert Paperclip::Interpolations.respond_to? m
assert Paperclip::Interpolations.respond_to?(m)
end
end

Expand All @@ -19,6 +19,10 @@ class InterpolationsTest < Test::Unit::TestCase
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
assert_equal Module, Paperclip::Interpolations.class
end

should "return the class of the instance" do
attachment = mock
attachment.expects(:instance).returns(attachment)
Expand Down
15 changes: 15 additions & 0 deletions test/paperclip_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,21 @@ def self.should_validate validation, options, valid_file, invalid_file

should_validate validation, options, valid_file, invalid_file
end

context "with size validation and less_than 10240 option" do
context "and assigned an invalid file" do
setup do
Dummy.send(:"validates_attachment_size", :avatar, :less_than => 10240)
@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 /between 0 and 10240 bytes/, @dummy.errors.on(:avatar)
end
end
end

end
end
44 changes: 37 additions & 7 deletions test/storage_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
require 'test/helper'
require 'aws/s3'

class StorageTest < Test::Unit::TestCase
def rails_env(env)
silence_warnings do
Object.const_set(:RAILS_ENV, env)
end
end

context "Parsing S3 credentials" do
setup do
AWS::S3::Base.stubs(:establish_connection!)
Expand All @@ -15,25 +22,25 @@ class StorageTest < Test::Unit::TestCase
end

teardown do
Object.const_set("RAILS_ENV", @current_env)
rails_env(@current_env)
end

should "get the correct credentials when RAILS_ENV is production" do
Object.const_set('RAILS_ENV', "production")
rails_env("production")
assert_equal({:key => "12345"},
@avatar.parse_credentials('production' => {:key => '12345'},
:development => {:key => "54321"}))
end

should "get the correct credentials when RAILS_ENV is development" do
Object.const_set('RAILS_ENV', "development")
rails_env("development")
assert_equal({:key => "54321"},
@avatar.parse_credentials('production' => {:key => '12345'},
:development => {:key => "54321"}))
end

should "return the argument if the key does not exist" do
Object.const_set('RAILS_ENV', "not really an env")
rails_env("not really an env")
assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345"))
end
end
Expand Down Expand Up @@ -102,15 +109,15 @@ class StorageTest < Test::Unit::TestCase
@old_env = RAILS_ENV
end

teardown{ Object.const_set("RAILS_ENV", @old_env) }
teardown{ rails_env(@old_env) }

should "get the right bucket in production" do
Object.const_set("RAILS_ENV", "production")
rails_env("production")
assert_equal "prod_bucket", @dummy.avatar.bucket_name
end

should "get the right bucket in development" do
Object.const_set("RAILS_ENV", "development")
rails_env("development")
assert_equal "dev_bucket", @dummy.avatar.bucket_name
end
end
Expand Down Expand Up @@ -229,6 +236,29 @@ class StorageTest < Test::Unit::TestCase
end
end

context "with S3 credentials in a YAML file" do
setup do
ENV['S3_KEY'] = 'env_key'
ENV['S3_BUCKET'] = 'env_bucket'
ENV['S3_SECRET'] = 'env_secret'

rails_env('test')

rebuild_model :storage => :s3,
:s3_credentials => File.new(File.join(File.dirname(__FILE__), "fixtures/s3.yml"))

Dummy.delete_all

@dummy = Dummy.new
end

should "run it the file through ERB" do
assert_equal 'env_bucket', @dummy.avatar.bucket_name
assert_equal 'env_key', AWS::S3::Base.connection.options[:access_key_id]
assert_equal 'env_secret', AWS::S3::Base.connection.options[:secret_access_key]
end
end

unless ENV["S3_TEST_BUCKET"].blank?
context "Using S3 for real, an attachment with S3 storage" do
setup do
Expand Down

0 comments on commit 958cba5

Please sign in to comment.