Permalink
Browse files

Start pulling out the URL generator.

  • Loading branch information...
1 parent af689b4 commit 2002376a44b109241dae4caf443176e5ec9df138 @mike-burns mike-burns committed Oct 28, 2011
Showing with 284 additions and 199 deletions.
  1. +7 −47 lib/paperclip/attachment.rb
  2. +1 −1 lib/paperclip/options.rb
  3. +61 −0 lib/paperclip/url_generator.rb
  4. +4 −151 test/attachment_test.rb
  5. +211 −0 test/url_generator_test.rb
@@ -1,5 +1,6 @@
# encoding: utf-8
require 'uri'
+require 'paperclip/url_generator'
module Paperclip
# The Attachment class manages the files for a given attachment. It saves
@@ -25,7 +26,8 @@ def self.default_options
:use_default_time_zone => true,
:hash_digest => "SHA1",
:hash_data => ":class/:attachment/:id/:style/:updated_at",
- :preserve_files => false
+ :preserve_files => false,
+ :interpolator => Paperclip::Interpolations
}
end
@@ -56,7 +58,7 @@ def self.default_options
# +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
# +interpolator+ - the object used to interpolate filenames and URLs. Defaults to Paperclip::Interpolations
- def initialize name, instance, options = {}
+ def initialize(name, instance, options = {})
@name = name
@instance = instance
@@ -68,7 +70,7 @@ def initialize name, instance, options = {}
@queued_for_write = {}
@errors = {}
@dirty = false
- @interpolator = (options[:interpolator] || Paperclip::Interpolations)
+ @interpolator = options[:interpolator]
initialize_storage
end
@@ -131,12 +133,8 @@ def assign uploaded_file
# security, however, for performance reasons. Set use_timestamp to false
# if you want to stop the attachment update time appended to the url
def url(style_name = default_style, options = {})
- options = handle_url_options(options)
- url = interpolate(most_appropriate_url, style_name)
-
- url = url_timestamp(url) if options[:timestamp]
- url = escape_url(url) if options[:escape]
- url
+ url_generator = UrlGenerator.new(self, @options)
+ url_generator.for(style_name, options)
end
# Returns the path of the attachment as defined by the :path option. If the
@@ -328,44 +326,6 @@ def instance_read(attr)
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:
%w(file_name).each do |field|
unless @instance.respond_to?("#{name}_#{field}") && @instance.respond_to?("#{name}_#{field}=")
View
@@ -34,7 +34,7 @@ def initialize(attachment, hash)
@processors = hash[:processors]
@preserve_files = hash[:preserve_files]
@http_proxy = hash[:http_proxy]
-
+ @interpolator = hash[:interpolator]
#s3 options
@s3_credentials = hash[:s3_credentials]
@s3_host_name = hash[:s3_host_name]
@@ -0,0 +1,61 @@
+class UrlGenerator
+ def initialize(attachment, options)
+ @attachment = attachment
+ @options = options
+ end
+
+ def for(style_name, options)
+ options = handle_url_options(options)
+ url = @options.interpolator.interpolate(most_appropriate_url, @attachment, style_name)
+
+ url = url_timestamp(url) if options[:timestamp]
+ url = escape_url(url) if options[:escape]
+ url
+ end
+
+ 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
+ if @options.default_url.respond_to?(:call)
+ @options.default_url.call(@attachment)
+ else
+ @options.default_url
+ end
+ end
+
+ def most_appropriate_url
+ if @attachment.original_filename.nil?
+ default_url
+ else
+ @options.url
+ end
+ end
+
+ def url_timestamp(url)
+ if @attachment.updated_at
+ delimiter_char = url.include?("?") ? "&" : "?"
+ "#{url}#{delimiter_char}#{@attachment.updated_at.to_s}"
+ else
+ url
+ end
+ end
+
+ def escape_url(url)
+ url.respond_to?(:escape) ? url.escape : URI.escape(url)
+ end
+end
View
@@ -6,6 +6,7 @@ class Dummy
end
class AttachmentTest < Test::Unit::TestCase
+ # this test depends on the UrlGenerator.
should "return the path based on the url by default" do
@attachment = attachment :url => "/:class/:id/:basename"
@model = @attachment.instance
@@ -14,35 +15,6 @@ class AttachmentTest < Test::Unit::TestCase
assert_equal "#{Rails.root}/public/fake_models/1234/fake", @attachment.path
end
- should "return the url by interpolating the default_url option when no file assigned" do
- @attachment = attachment :default_url => ":class/blegga.png"
- @model = @attachment.instance
- assert_nil @model.avatar_file_name
- assert_equal "fake_models/blegga.png", @attachment.url
- end
-
- should "return the url by executing and interpolating the default_url Proc when no file assigned" do
- @attachment = attachment :default_url => lambda { |a| ":class/blegga.png" }
- @model = @attachment.instance
- assert_nil @model.avatar_file_name
- assert_equal "fake_models/blegga.png", @attachment.url
- end
-
- should "return the url by executing and interpolating the default_url Proc with attachment arg when no file assigned" do
- @attachment = attachment :default_url => lambda { |a| a.instance.some_method_to_determine_default_url }
- @model = @attachment.instance
- @model.stubs(:some_method_to_determine_default_url).returns(":class/blegga.png")
- assert_nil @model.avatar_file_name
- assert_equal "fake_models/blegga.png", @attachment.url
- end
-
- should "return the url by executing and interpolating the default_url when assigned with symbol as method in attachment model" do
- @attachment = attachment :default_url => :some_method_to_determine_default_url
- @model = @attachment.instance
- @model.stubs(:some_method_to_determine_default_url).returns(":class/female_:style_blegga.png")
- assert_equal "fake_models/female_foostyle_blegga.png", @attachment.url(:foostyle)
- end
-
context "Attachment default_options" do
setup do
rebuild_model
@@ -160,36 +132,6 @@ class AttachmentTest < Test::Unit::TestCase
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
setup do
@file = StringIO.new("...")
@@ -406,27 +348,6 @@ def thumb; "-thumb"; 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 = [
[ lambda{|z| "50x50#" }, :png ],
lambda{|z| "50x50#" },
@@ -552,16 +473,16 @@ class Paperclip::Test < Paperclip::Processor; end
rebuild_model :storage => :FileSystem
@dummy = Dummy.new
assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
-
+
rebuild_model :storage => :Filesystem
@dummy = Dummy.new
assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
end
-
+
should "convert underscored storage name to camelcase" do
rebuild_model :storage => :not_here
@dummy = Dummy.new
- exception = assert_raises(Paperclip::StorageMethodNotFound) do |e|
+ exception = assert_raises(Paperclip::StorageMethodNotFound) do
@dummy.avatar
end
assert exception.message.include?("NotHere")
@@ -770,12 +691,6 @@ def do_after_all; 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
assert @attachment.to_file.nil?
assert_equal nil, @attachment.path
@@ -802,29 +717,6 @@ def do_after_all; end
@attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
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
assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
end
@@ -864,14 +756,6 @@ def do_after_all; end
@attachment.save
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
[:large, :medium, :small].each do |style|
io = @attachment.to_file(style)
@@ -941,22 +825,6 @@ def do_after_all; 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
setup do
rebuild_model :storage => :not_here
@@ -1109,21 +977,6 @@ def do_after_all; 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
setup do
rebuild_model
Oops, something went wrong.

0 comments on commit 2002376

Please sign in to comment.