Commit bc5c51d1 by Mike Burns Committed by Mike Burns

Separate the UrlGenerator out from the Attachment. Some example plugins that…

Separate the UrlGenerator out from the Attachment. Some example plugins that could be written include generating thumbnails on the fly for different thumbnail sizes, or delaying the thumbnail generation until it is first called.
parent af689b44
# encoding: utf-8 # encoding: utf-8
require 'uri' require 'uri'
require 'paperclip/url_generator'
module Paperclip module Paperclip
# The Attachment class manages the files for a given attachment. It saves # The Attachment class manages the files for a given attachment. It saves
...@@ -25,7 +26,9 @@ module Paperclip ...@@ -25,7 +26,9 @@ module Paperclip
:use_default_time_zone => true, :use_default_time_zone => true,
:hash_digest => "SHA1", :hash_digest => "SHA1",
:hash_data => ":class/:attachment/:id/:style/:updated_at", :hash_data => ":class/:attachment/:id/:style/:updated_at",
:preserve_files => false :preserve_files => false,
:interpolator => Paperclip::Interpolations,
:url_generator => Paperclip::UrlGenerator
} }
end end
...@@ -38,25 +41,26 @@ module Paperclip ...@@ -38,25 +41,26 @@ module Paperclip
# #
# Options include: # Options include:
# #
# +url+ - a relative URL of the attachment. This is interpolated using +interpolator+ # +url+ - a relative URL of the attachment. This is interpolated using +interpolator+
# +path+ - where on the filesystem to store the attachment. This is interpolated using +interpolator+ # +path+ - where on the filesystem to store the attachment. This is interpolated using +interpolator+
# +styles+ - a hash of options for processing the attachment. See +has_attached_file+ for the details # +styles+ - a hash of options for processing the attachment. See +has_attached_file+ for the details
# +only_process+ - style args to be run through the post-processor. This defaults to the empty list # +only_process+ - style args to be run through the post-processor. This defaults to the empty list
# +default_url+ - a URL for the missing image # +default_url+ - a URL for the missing image
# +default_style+ - the style to use when don't specify an argument to e.g. #url, #path # +default_style+ - the style to use when don't specify an argument to e.g. #url, #path
# +storage+ - the storage mechanism. Defaults to :filesystem # +storage+ - the storage mechanism. Defaults to :filesystem
# +use_timestamp+ - whether to append an anti-caching timestamp to image URLs. Defaults to true # +use_timestamp+ - whether to append an anti-caching timestamp to image URLs. Defaults to true
# +whiny+, +whiny_thumbnails+ - whether to raise when thumbnailing fails # +whiny+, +whiny_thumbnails+ - whether to raise when thumbnailing fails
# +use_default_time_zone+ - related to +use_timestamp+. Defaults to true # +use_default_time_zone+ - related to +use_timestamp+. Defaults to true
# +hash_digest+ - a string representing a class that will be used to hash URLs for obfuscation # +hash_digest+ - a string representing a class that will be used to hash URLs for obfuscation
# +hash_data+ - the relative URL for the hash data. This is interpolated using +interpolator+ # +hash_data+ - the relative URL for the hash data. This is interpolated using +interpolator+
# +hash_secret+ - a secret passed to the +hash_digest+ # +hash_secret+ - a secret passed to the +hash_digest+
# +convert_options+ - flags passed to the +convert+ command for processing # +convert_options+ - flags passed to the +convert+ command for processing
# +source_file_options+ - flags passed to the +convert+ command that controls how the file is read # +source_file_options+ - flags passed to the +convert+ command that controls how the file is read
# +processors+ - classes that transform the attachment. Defaults to [:thumbnail] # +processors+ - classes that transform the attachment. Defaults to [:thumbnail]
# +preserve_files+ - whether to keep files on the filesystem when deleting to clearing the attachment. Defaults to false # +preserve_files+ - whether to keep files on the filesystem when deleting to clearing the attachment. Defaults to false
# +interpolator+ - the object used to interpolate filenames and URLs. Defaults to Paperclip::Interpolations # +interpolator+ - the object used to interpolate filenames and URLs. Defaults to Paperclip::Interpolations
def initialize name, instance, options = {} # +url_generator+ - the object used to generate URLs, using the interpolator. Defaults to Paperclip::UrlGenerator
def initialize(name, instance, options = {})
@name = name @name = name
@instance = instance @instance = instance
...@@ -68,7 +72,8 @@ module Paperclip ...@@ -68,7 +72,8 @@ module Paperclip
@queued_for_write = {} @queued_for_write = {}
@errors = {} @errors = {}
@dirty = false @dirty = false
@interpolator = (options[:interpolator] || Paperclip::Interpolations) @interpolator = options[:interpolator]
@url_generator = options[:url_generator].new(self, @options)
initialize_storage initialize_storage
end end
...@@ -124,19 +129,36 @@ module Paperclip ...@@ -124,19 +129,36 @@ module Paperclip
uploaded_file.close if close_uploaded_file uploaded_file.close if close_uploaded_file
end end
# Returns the public URL of the attachment, with a given style. Note that # Returns the public URL of the attachment with a given style. This does
# this does not necessarily need to point to a file that your web server # not necessarily need to point to a file that your Web server can access
# can access and can point to an action in your app, if you need fine # and can instead point to an action in your app, for example for fine grained
# grained security. This is not recommended if you don't need the # security; this has a serious performance tradeoff.
# security, however, for performance reasons. Set use_timestamp to false #
# if you want to stop the attachment update time appended to the url # Options:
#
# +timestamp+ - Add a timestamp to the end of the URL. Default: true.
# +escape+ - Perform URI escaping to the URL. Default: true.
#
# Global controls (set on has_attached_file):
#
# +interpolator+ - The object that fills in a URL pattern's variables.
# +default_url+ - The image to show when the attachment has no image.
# +url+ - The URL for a saved image.
# +url_generator+ - The object that generates a URL. Default: Paperclip::UrlGenerator.
#
# As mentioned just above, the object that generates this URL can be passed
# in, for finer control. This object must respond to two methods:
#
# +#new(Paperclip::Attachment, Paperclip::Options)+
# +#for(style_name, options_hash)+
def url(style_name = default_style, options = {}) def url(style_name = default_style, options = {})
options = handle_url_options(options) default_options = {:timestamp => @options.use_timestamp, :escape => true}
url = interpolate(most_appropriate_url, style_name)
url = url_timestamp(url) if options[:timestamp] if options == true || options == false # Backwards compatibility.
url = escape_url(url) if options[:escape] @url_generator.for(style_name, default_options.merge(:timestamp => options))
url else
@url_generator.for(style_name, default_options.merge(options))
end
end end
# Returns the path of the attachment as defined by the :path option. If the # Returns the path of the attachment as defined by the :path option. If the
...@@ -328,44 +350,6 @@ module Paperclip ...@@ -328,44 +350,6 @@ module Paperclip
private private
def handle_url_options(options)
timestamp = extract_timestamp(options)
options = {} if options == true || options == false
options[:timestamp] = timestamp
options[:escape] = true if options[:escape].nil?
options
end
def extract_timestamp(options)
possibilities = [((options == true || options == false) ? options : nil),
(options.respond_to?(:[]) ? options[:timestamp] : nil),
@options.use_timestamp]
possibilities.find{|n| !n.nil? }
end
def default_url
return @options.default_url.call(self) if @options.default_url.is_a?(Proc)
@options.default_url
end
def most_appropriate_url
if original_filename.nil?
default_url
else
@options.url
end
end
def url_timestamp(url)
return url unless updated_at
delimiter_char = url.include?("?") ? "&" : "?"
"#{url}#{delimiter_char}#{updated_at.to_s}"
end
def escape_url(url)
url.respond_to?(:escape) ? url.escape : URI.escape(url)
end
def ensure_required_accessors! #:nodoc: def ensure_required_accessors! #:nodoc:
%w(file_name).each do |field| %w(file_name).each do |field|
unless @instance.respond_to?("#{name}_#{field}") && @instance.respond_to?("#{name}_#{field}=") unless @instance.respond_to?("#{name}_#{field}") && @instance.respond_to?("#{name}_#{field}=")
......
...@@ -34,6 +34,9 @@ module Paperclip ...@@ -34,6 +34,9 @@ module Paperclip
@processors = hash[:processors] @processors = hash[:processors]
@preserve_files = hash[:preserve_files] @preserve_files = hash[:preserve_files]
@http_proxy = hash[:http_proxy] @http_proxy = hash[:http_proxy]
@interpolator = hash[:interpolator]
@escape = hash[:escape]
@url_generator = hash[:url_generator]
#s3 options #s3 options
@s3_credentials = hash[:s3_credentials] @s3_credentials = hash[:s3_credentials]
......
require 'uri'
module Paperclip
class UrlGenerator
def initialize(attachment, attachment_options)
@attachment = attachment
@attachment_options = attachment_options
end
def for(style_name, options)
escape_url_as_needed(
timestamp_as_needed(
@attachment_options.interpolator.interpolate(most_appropriate_url, @attachment, style_name),
options
), options)
end
private
# This method is all over the place.
def default_url
if @attachment_options.default_url.respond_to?(:call)
@attachment_options.default_url.call(@attachment)
elsif @attachment_options.default_url.is_a?(Symbol)
@attachment.instance.send(@attachment_options.default_url)
else
@attachment_options.default_url
end
end
def most_appropriate_url
if @attachment.original_filename.nil?
default_url
else
@attachment_options.url
end
end
def timestamp_as_needed(url, options)
if options[:timestamp] && timestamp_possible?
delimiter_char = url.include?('?') ? '&' : '?'
"#{url}#{delimiter_char}#{@attachment.updated_at.to_s}"
else
url
end
end
def timestamp_possible?
@attachment.respond_to?(:updated_at) && @attachment.updated_at.present?
end
def escape_url_as_needed(url, options)
if options[:escape]
escape_url(url)
else
url
end
end
def escape_url(url)
url.respond_to?(:escape) ? url.escape : URI.escape(url)
end
end
end
# encoding: utf-8 # encoding: utf-8
require './test/helper' require './test/helper'
require 'paperclip/attachment'
class Dummy class Dummy; end
# This is a dummy class
end
class AttachmentTest < Test::Unit::TestCase class AttachmentTest < Test::Unit::TestCase
should "return the path based on the url by default" do should "handle a boolean second argument to #url" do
@attachment = attachment :url => "/:class/:id/:basename" mock_url_generator_builder = MockUrlGeneratorBuilder.new
@model = @attachment.instance attachment = Paperclip::Attachment.new(:name, :instance, :url_generator => mock_url_generator_builder)
@model.id = 1234
@model.avatar_file_name = "fake.jpg" attachment.url(:style_name, true)
assert_equal "#{Rails.root}/public/fake_models/1234/fake", @attachment.path assert mock_url_generator_builder.has_generated_url_with_options?(:timestamp => true, :escape => true)
attachment.url(:style_name, false)
assert mock_url_generator_builder.has_generated_url_with_options?(:timestamp => false, :escape => true)
end end
should "return the url by interpolating the default_url option when no file assigned" do should "pass the style and options through to the URL generator on #url" do
@attachment = attachment :default_url => ":class/blegga.png" mock_url_generator_builder = MockUrlGeneratorBuilder.new
@model = @attachment.instance attachment = Paperclip::Attachment.new(:name, :instance, :url_generator => mock_url_generator_builder)
assert_nil @model.avatar_file_name
assert_equal "fake_models/blegga.png", @attachment.url attachment.url(:style_name, :options => :values)
assert mock_url_generator_builder.has_generated_url_with_options?(:options => :values)
end end
should "return the url by executing and interpolating the default_url Proc when no file assigned" do should "pass default options through when #url is given one argument" do
@attachment = attachment :default_url => lambda { |a| ":class/blegga.png" } mock_url_generator_builder = MockUrlGeneratorBuilder.new
@model = @attachment.instance attachment = Paperclip::Attachment.new(:name,
assert_nil @model.avatar_file_name :instance,
assert_equal "fake_models/blegga.png", @attachment.url :url_generator => mock_url_generator_builder,
:use_timestamp => true)
attachment.url(:style_name)
assert mock_url_generator_builder.has_generated_url_with_options?(:escape => true, :timestamp => true)
end end
should "return the url by executing and interpolating the default_url Proc with attachment arg when no file assigned" do should "pass default style and options through when #url is given no arguments" do
@attachment = attachment :default_url => lambda { |a| a.instance.some_method_to_determine_default_url } mock_url_generator_builder = MockUrlGeneratorBuilder.new
@model = @attachment.instance attachment = Paperclip::Attachment.new(:name,
@model.stubs(:some_method_to_determine_default_url).returns(":class/blegga.png") :instance,
assert_nil @model.avatar_file_name :default_style => 'default style',
assert_equal "fake_models/blegga.png", @attachment.url :url_generator => mock_url_generator_builder,
:use_timestamp => true)
attachment.url
assert mock_url_generator_builder.has_generated_url_with_options?(:escape => true, :timestamp => true)
assert mock_url_generator_builder.has_generated_url_with_style_name?('default style')
end end
should "return the url by executing and interpolating the default_url when assigned with symbol as method in attachment model" do should "pass the option :timestamp => true if :use_timestamp is true and :timestamp is not passed" do
@attachment = attachment :default_url => :some_method_to_determine_default_url mock_url_generator_builder = MockUrlGeneratorBuilder.new
attachment = Paperclip::Attachment.new(:name,
:instance,
:url_generator => mock_url_generator_builder,
:use_timestamp => true)
attachment.url(:style_name)
assert mock_url_generator_builder.has_generated_url_with_options?(:escape => true, :timestamp => true)
end
should "pass the option :timestamp => false if :use_timestamp is false and :timestamp is not passed" do
mock_url_generator_builder = MockUrlGeneratorBuilder.new
attachment = Paperclip::Attachment.new(:name,
:instance,
:url_generator => mock_url_generator_builder,
:use_timestamp => false)
attachment.url(:style_name)
assert mock_url_generator_builder.has_generated_url_with_options?(:escape => true, :timestamp => false)
end
should "not change the :timestamp if :timestamp is passed" do
mock_url_generator_builder = MockUrlGeneratorBuilder.new
attachment = Paperclip::Attachment.new(:name,
:instance,
:url_generator => mock_url_generator_builder,
:use_timestamp => false)
attachment.url(:style_name, :timestamp => true)
assert mock_url_generator_builder.has_generated_url_with_options?(:escape => true, :timestamp => true)
end
should "return the path based on the url by default" do
@attachment = attachment :url => "/:class/:id/:basename"
@model = @attachment.instance @model = @attachment.instance
@model.stubs(:some_method_to_determine_default_url).returns(":class/female_:style_blegga.png") @model.id = 1234
assert_equal "fake_models/female_foostyle_blegga.png", @attachment.url(:foostyle) @model.avatar_file_name = "fake.jpg"
assert_equal "#{Rails.root}/public/fake_models/1234/fake", @attachment.path
end end
context "Attachment default_options" do context "Attachment default_options" do
...@@ -160,36 +206,6 @@ class AttachmentTest < Test::Unit::TestCase ...@@ -160,36 +206,6 @@ class AttachmentTest < Test::Unit::TestCase
end end
end end
context "An attachment" do
setup do
@file = StringIO.new("...")
end
context "using default time zone" do
setup do
rebuild_model :url => "X"
@dummy = Dummy.new
@dummy.avatar = @file
end
should "generate a url with a timestamp when passing true" do
assert_equal "X?#{@dummy.avatar_updated_at.to_i.to_s}", @dummy.avatar.url(:style, true)
end
should "not generate a url with a timestamp when passing false" do
assert_equal "X", @dummy.avatar.url(:style, false)
end
should "generate a url with a timestamp when setting a timestamp option" do
assert_equal "X?#{@dummy.avatar_updated_at.to_i.to_s}", @dummy.avatar.url(:style, :timestamp => true)
end
should "not generate a url with a timestamp when setting a timestamp option to false" do
assert_equal "X", @dummy.avatar.url(:style, :timestamp => false)
end
end
end
context "An attachment with :hash interpolations" do context "An attachment with :hash interpolations" do
setup do setup do
@file = StringIO.new("...") @file = StringIO.new("...")
...@@ -406,27 +422,6 @@ class AttachmentTest < Test::Unit::TestCase ...@@ -406,27 +422,6 @@ class AttachmentTest < Test::Unit::TestCase
end end
end end
context "An attachment with :url that is a proc" do
setup do
rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
@file = File.new(File.join(File.dirname(__FILE__),
"fixtures",
"5k.png"), 'rb')
@dummyA = Dummy.new(:other => 'a')
@dummyA.avatar = @file
@dummyB = Dummy.new(:other => 'b')
@dummyB.avatar = @file
end
teardown { @file.close }
should "return correct url" do
assert_equal "path/a.png", @dummyA.avatar.url(:original, false)
assert_equal "path/b.png", @dummyB.avatar.url(:original, false)
end
end
geometry_specs = [ geometry_specs = [
[ lambda{|z| "50x50#" }, :png ], [ lambda{|z| "50x50#" }, :png ],
lambda{|z| "50x50#" }, lambda{|z| "50x50#" },
...@@ -552,16 +547,16 @@ class AttachmentTest < Test::Unit::TestCase ...@@ -552,16 +547,16 @@ class AttachmentTest < Test::Unit::TestCase
rebuild_model :storage => :FileSystem rebuild_model :storage => :FileSystem
@dummy = Dummy.new @dummy = Dummy.new
assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem) assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
rebuild_model :storage => :Filesystem rebuild_model :storage => :Filesystem
@dummy = Dummy.new @dummy = Dummy.new
assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem) assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
end end
should "convert underscored storage name to camelcase" do should "convert underscored storage name to camelcase" do
rebuild_model :storage => :not_here rebuild_model :storage => :not_here
@dummy = Dummy.new @dummy = Dummy.new
exception = assert_raises(Paperclip::StorageMethodNotFound) do |e| exception = assert_raises(Paperclip::StorageMethodNotFound) do
@dummy.avatar @dummy.avatar
end end
assert exception.message.include?("NotHere") assert exception.message.include?("NotHere")
...@@ -770,12 +765,6 @@ class AttachmentTest < Test::Unit::TestCase ...@@ -770,12 +765,6 @@ class AttachmentTest < Test::Unit::TestCase
end end
end end
should "return its default_url when no file assigned" do
assert @attachment.to_file.nil?
assert_equal "/avatars/original/missing.png", @attachment.url
assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
end
should "return nil as path when no file assigned" do should "return nil as path when no file assigned" do
assert @attachment.to_file.nil? assert @attachment.to_file.nil?
assert_equal nil, @attachment.path assert_equal nil, @attachment.path
...@@ -802,29 +791,6 @@ class AttachmentTest < Test::Unit::TestCase ...@@ -802,29 +791,6 @@ class AttachmentTest < Test::Unit::TestCase
@attachment.stubs(:instance_read).with(:updated_at).returns(dtnow) @attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
end end
should "return a correct url even if the file does not exist" do
assert_nil @attachment.to_file
assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
end
should "make sure the updated_at mtime is in the url if it is defined" do
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{#{@now.to_i}$}, @attachment.url(:blah, false)
end
context "with the updated_at field removed" do
setup do
@attachment.stubs(:instance_read).with(:updated_at).returns(nil)
end
should "only return the url without the updated_at when sent #url" do
assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
end
end
should "return the proper path when filename has a single .'s" do should "return the proper path when filename has a single .'s" do
assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path) assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
end end
...@@ -864,14 +830,6 @@ class AttachmentTest < Test::Unit::TestCase ...@@ -864,14 +830,6 @@ class AttachmentTest < Test::Unit::TestCase
@attachment.save @attachment.save
end end
should "return the real url" do
file = @attachment.to_file
assert file
assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
file.close
end
should "commit the files to disk" do should "commit the files to disk" do
[:large, :medium, :small].each do |style| [:large, :medium, :small].each do |style|
io = @attachment.to_file(style) io = @attachment.to_file(style)
...@@ -941,22 +899,6 @@ class AttachmentTest < Test::Unit::TestCase ...@@ -941,22 +899,6 @@ class AttachmentTest < Test::Unit::TestCase
end end
end end
context "with a file that has space in file name" do
setup do
@attachment.stubs(:instance_read).with(:file_name).returns("spaced file.png")
@attachment.stubs(:instance_read).with(:content_type).returns("image/png")
@attachment.stubs(:instance_read).with(:file_size).returns(12345)
dtnow = DateTime.now
@now = Time.now
Time.stubs(:now).returns(@now)
@attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
end
should "returns an escaped version of the URL" do
assert_match /\/spaced%20file\.png/, @attachment.url
end
end
context "when trying a nonexistant storage type" do context "when trying a nonexistant storage type" do
setup do setup do
rebuild_model :storage => :not_here rebuild_model :storage => :not_here
...@@ -1109,21 +1051,6 @@ class AttachmentTest < Test::Unit::TestCase ...@@ -1109,21 +1051,6 @@ class AttachmentTest < Test::Unit::TestCase
end end
end end
context "setting an interpolation class" do
should "produce the URL with the given interpolations" do
Interpolator = Class.new do
def self.interpolate(pattern, attachment, style_name)
"hello"
end
end
instance = Dummy.new
attachment = Paperclip::Attachment.new(:avatar, instance, :interpolator => Interpolator)
assert_equal "hello", attachment.url
end
end
context "An attached file" do context "An attached file" do
setup do setup do
rebuild_model rebuild_model
......
...@@ -53,6 +53,10 @@ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(__FIL ...@@ -53,6 +53,10 @@ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(__FIL
ActiveRecord::Base.establish_connection(config['test']) ActiveRecord::Base.establish_connection(config['test'])
Paperclip.options[:logger] = ActiveRecord::Base.logger Paperclip.options[:logger] = ActiveRecord::Base.logger
Dir[File.join(File.dirname(__FILE__), 'support','*')].each do |f|
require f
end
def reset_class class_name def reset_class class_name
ActiveRecord::Base.send(:include, Paperclip::Glue) ActiveRecord::Base.send(:include, Paperclip::Glue)
Object.send(:remove_const, class_name) rescue nil Object.send(:remove_const, class_name) rescue nil
......
# encoding: utf-8 # encoding: utf-8
require './test/helper' require './test/helper'
class MockAttachment < Struct.new(:one, :two) class DSO < Struct.new(:one, :two)
def instance def instance
self self
end end
...@@ -17,7 +17,7 @@ class OptionsTest < Test::Unit::TestCase ...@@ -17,7 +17,7 @@ class OptionsTest < Test::Unit::TestCase
context "#styles with a plain hash" do context "#styles with a plain hash" do
setup do setup do
@attachment = MockAttachment.new(nil, nil) @attachment = DSO.new(nil, nil)
@options = Paperclip::Options.new(@attachment, @options = Paperclip::Options.new(@attachment,
:styles => { :styles => {
:something => ["400x400", :png] :something => ["400x400", :png]
...@@ -35,7 +35,7 @@ class OptionsTest < Test::Unit::TestCase ...@@ -35,7 +35,7 @@ class OptionsTest < Test::Unit::TestCase
context "#styles is a proc" do context "#styles is a proc" do
setup do setup do
@attachment = MockAttachment.new("123x456", :doc) @attachment = DSO.new("123x456", :doc)
@options = Paperclip::Options.new(@attachment, @options = Paperclip::Options.new(@attachment,
:styles => lambda {|att| :styles => lambda {|att|
{:something => {:geometry => att.one, :format => att.two}} {:something => {:geometry => att.one, :format => att.two}}
...@@ -59,7 +59,7 @@ class OptionsTest < Test::Unit::TestCase ...@@ -59,7 +59,7 @@ class OptionsTest < Test::Unit::TestCase
context "#processors" do context "#processors" do
setup do setup do
@attachment = MockAttachment.new(nil, nil) @attachment = DSO.new(nil, nil)
end end
should "return processors if not a proc" do should "return processors if not a proc" do
@options = Paperclip::Options.new(@attachment, :processors => [:one]) @options = Paperclip::Options.new(@attachment, :processors => [:one])
......
class MockAttachment
attr_accessor :updated_at, :original_filename
def initialize(options = {})
@model = options[:model]
@responds_to_updated_at = options[:responds_to_updated_at]
@updated_at = options[:updated_at]
@original_filename = options[:original_filename]
end
def instance
@model
end
def respond_to?(meth)
if meth.to_s == "updated_at"
@responds_to_updated_at || @updated_at
else
super
end
end
end
class MockInterpolator
def initialize(options = {})
@options = options
end
def interpolate(pattern, attachment, style_name)
@interpolated_pattern = pattern
@interpolated_attachment = attachment
@interpolated_style_name = style_name
@options[:result]
end
def has_interpolated_pattern?(pattern)
@interpolated_pattern == pattern
end
def has_interpolated_style_name?(style_name)
@interpolated_style_name == style_name
end
def has_interpolated_attachment?(attachment)
@interpolated_attachment == attachment
end
end
class MockModel
end
class MockUrlGeneratorBuilder
def initializer
end
def new(attachment, attachment_options)
@attachment = attachment
@attachment_options = attachment_options
self
end
def for(style_name, options)
@generated_url_with_style_name = style_name
@generated_url_with_options = options
"hello"
end
def has_generated_url_with_options?(options)
# options.is_a_subhash_of(@generated_url_with_options)
options.inject(true) do |acc,(k,v)|
acc && @generated_url_with_options[k] == v
end
end
def has_generated_url_with_style_name?(style_name)
@generated_url_with_style_name == style_name
end
end
# encoding: utf-8
require './test/helper'
require 'paperclip/url_generator'
require 'paperclip/options'
class UrlGeneratorTest < Test::Unit::TestCase
should "use the given interpolator" do
expected = "the expected result"
mock_attachment = MockAttachment.new
mock_interpolator = MockInterpolator.new(:result => expected)
url_generator = Paperclip::UrlGenerator.new(mock_attachment,
Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator))
result = url_generator.for(:style_name, {})
assert_equal expected, result
assert mock_interpolator.has_interpolated_attachment?(mock_attachment)
assert mock_interpolator.has_interpolated_style_name?(:style_name)
end
should "use the default URL when no file is assigned" do
mock_attachment = MockAttachment.new
mock_interpolator = MockInterpolator.new
default_url = "the default url"
options = Paperclip::Options.new(mock_attachment,
:interpolator => mock_interpolator,
:default_url => default_url)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
url_generator.for(:style_name, {})
assert mock_interpolator.has_interpolated_pattern?(default_url),
"expected the interpolator to be passed #{default_url.inspect} but it wasn't"
end
should "execute the default URL lambda when no file is assigned" do
mock_attachment = MockAttachment.new
mock_interpolator = MockInterpolator.new
default_url = lambda {|attachment| "the #{attachment.class.name} default url" }
options = Paperclip::Options.new(mock_attachment,
:interpolator => mock_interpolator,
:default_url => default_url)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
url_generator.for(:style_name, {})
assert mock_interpolator.has_interpolated_pattern?("the MockAttachment default url"),
%{expected the interpolator to be passed "the MockAttachment default url", but it wasn't}
end
should "execute the method named by the symbol as the default URL when no file is assigned" do
mock_model = MockModel.new
mock_attachment = MockAttachment.new(:model => mock_model)
mock_interpolator = MockInterpolator.new
default_url = :to_s
options = Paperclip::Options.new(mock_attachment,
:interpolator => mock_interpolator,
:default_url => default_url)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
url_generator.for(:style_name, {})
assert mock_interpolator.has_interpolated_pattern?(mock_model.to_s),
%{expected the interpolator to be passed #{mock_model.to_s}, but it wasn't}
end
should "URL-escape spaces if asked to" do
expected = "the expected result"
mock_attachment = MockAttachment.new
mock_interpolator = MockInterpolator.new(:result => expected)
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
result = url_generator.for(:style_name, {:escape => true})
assert_equal "the%20expected%20result", result
end
should "escape the result of the interpolator using a method on the object, if asked to escape" do
expected = Class.new do
def escape
"the escaped result"
end
end.new
mock_attachment = MockAttachment.new
mock_interpolator = MockInterpolator.new(:result => expected)
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
result = url_generator.for(:style_name, {:escape => true})
assert_equal "the escaped result", result
end
should "leave spaces unescaped as asked to" do
expected = "the expected result"
mock_attachment = MockAttachment.new
mock_interpolator = MockInterpolator.new(:result => expected)
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
result = url_generator.for(:style_name, {:escape => false})
assert_equal "the expected result", result
end
should "default to leaving spaces unescaped" do
expected = "the expected result"
mock_attachment = MockAttachment.new
mock_interpolator = MockInterpolator.new(:result => expected)
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
result = url_generator.for(:style_name, {})
assert_equal "the expected result", result
end
should "produce URLs without the updated_at value when the object does not respond to updated_at" do
expected = "the expected result"
mock_interpolator = MockInterpolator.new(:result => expected)
mock_attachment = MockAttachment.new(:responds_to_updated_at => false)
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
result = url_generator.for(:style_name, {:timestamp => true})
assert_equal expected, result
end
should "produce URLs without the updated_at value when the updated_at value is nil" do
expected = "the expected result"
mock_interpolator = MockInterpolator.new(:result => expected)
mock_attachment = MockAttachment.new(:responds_to_updated_at => true, :updated_at => nil)
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
result = url_generator.for(:style_name, {:timestamp => true})
assert_equal expected, result
end
should "produce URLs with the updated_at when it exists" do
expected = "the expected result"
updated_at = 1231231234
mock_interpolator = MockInterpolator.new(:result => expected)
mock_attachment = MockAttachment.new(:updated_at => updated_at)
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
result = url_generator.for(:style_name, {:timestamp => true})
assert_equal "#{expected}?#{updated_at}", result
end
should "produce URLs with the updated_at when it exists, separated with a & if a ? already exists" do
expected = "the?expected result"
updated_at = 1231231234
mock_interpolator = MockInterpolator.new(:result => expected)
mock_attachment = MockAttachment.new(:updated_at => updated_at)
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
result = url_generator.for(:style_name, {:timestamp => true})
assert_equal "#{expected}&#{updated_at}", result
end
should "produce URLs without the updated_at when told to do as much" do
expected = "the expected result"
updated_at = 1231231234
mock_interpolator = MockInterpolator.new(:result => expected)
mock_attachment = MockAttachment.new(:updated_at => updated_at)
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
result = url_generator.for(:style_name, {:timestamp => false})
assert_equal expected, result
end
should "produce the correct URL when the instance has a file name" do
expected = "the expected result"
mock_attachment = MockAttachment.new(:original_filename => 'exists')
mock_interpolator = MockInterpolator.new
options = Paperclip::Options.new(mock_attachment, :interpolator => mock_interpolator, :url => expected)
url_generator = Paperclip::UrlGenerator.new(mock_attachment, options)
url_generator.for(:style_name, {})
assert mock_interpolator.has_interpolated_pattern?(expected),
"expected the interpolator to be passed #{expected.inspect} but it wasn't"
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