Commit 03f777f8 by Prem Sichanugrist

Extract some modules from Paperclip.rb

parent 5232b192
......@@ -41,7 +41,12 @@ require 'paperclip/attachment_options'
require 'paperclip/storage'
require 'paperclip/callbacks'
require 'paperclip/glue'
require 'paperclip/errors'
require 'paperclip/missing_attachment_styles'
require 'paperclip/validators'
require 'paperclip/instance_methods'
require 'paperclip/logger'
require 'paperclip/helpers'
require 'paperclip/railtie'
require 'logger'
require 'cocaine'
......@@ -49,8 +54,10 @@ require 'cocaine'
# The base module that gets included in ActiveRecord::Base. See the
# documentation for Paperclip::ClassMethods for more useful information.
module Paperclip
extend Helpers
extend Logger
extend ProcessorHelpers
class << self
# Provides configurability to Paperclip. The options available are:
# * whiny: Will raise an error if Paperclip cannot process thumbnails of
# an uploaded image. Defaults to true.
......@@ -59,7 +66,7 @@ module Paperclip
# * command_path: Defines the path at which to find the command line
# programs if they are not visible to Rails the system's search path. Defaults to
# nil, which uses the first executable found in the user's search path.
def options
def self.options
@options ||= {
:whiny => true,
:image_magick_path => nil,
......@@ -70,143 +77,6 @@ module Paperclip
}
end
def configure
yield(self) if block_given?
end
def interpolates key, &block
Paperclip::Interpolations[key] = block
end
# The run method takes the name of a binary to run, the arguments to that binary
# and some options:
#
# :command_path -> A $PATH-like variable that defines where to look for the binary
# on the filesystem. Colon-separated, just like $PATH.
#
# :expected_outcodes -> An array of integers that defines the expected exit codes
# of the binary. Defaults to [0].
#
# :log_command -> Log the command being run when set to true (defaults to false).
# This will only log if logging in general is set to true as well.
#
# :swallow_stderr -> Set to true if you don't care what happens on STDERR.
#
def run(cmd, arguments = "", local_options = {})
command_path = options[:command_path]
Cocaine::CommandLine.path = ( Cocaine::CommandLine.path ? [Cocaine::CommandLine.path].flatten | [command_path] : command_path )
local_options = local_options.merge(:logger => logger) if logging? && (options[:log_command] || local_options[:log_command])
Cocaine::CommandLine.new(cmd, arguments, local_options).run
end
def processor(name) #:nodoc:
@known_processors ||= {}
if @known_processors[name.to_s]
@known_processors[name.to_s]
else
name = name.to_s.camelize
load_processor(name) unless Paperclip.const_defined?(name)
processor = Paperclip.const_get(name)
@known_processors[name.to_s] = processor
end
end
def load_processor(name)
if defined?(Rails.root) && Rails.root
require File.expand_path(Rails.root.join("lib", "paperclip_processors", "#{name.underscore}.rb"))
end
end
def clear_processors!
@known_processors.try(:clear)
end
# You can add your own processor via the Paperclip configuration. Normally
# Paperclip will load all processors from the
# Rails.root/lib/paperclip_processors directory, but here you can add any
# existing class using this mechanism.
#
# Paperclip.configure do |c|
# c.register_processor :watermarker, WatermarkingProcessor.new
# end
def register_processor(name, processor)
@known_processors ||= {}
@known_processors[name.to_s] = processor
end
# Find all instances of the given Active Record model +klass+ with attachment +name+.
# This method is used by the refresh rake tasks.
def each_instance_with_attachment(klass, name)
unscope_method = class_for(klass).respond_to?(:unscoped) ? :unscoped : :with_exclusive_scope
class_for(klass).send(unscope_method) do
class_for(klass).find_each(:conditions => "#{name}_file_name is not null") do |instance|
yield(instance)
end
end
end
# Log a paperclip-specific line. This will log to STDOUT
# by default. Set Paperclip.options[:log] to false to turn off.
def log message
logger.info("[paperclip] #{message}") if logging?
end
def logger #:nodoc:
@logger ||= options[:logger] || Logger.new(STDOUT)
end
def logger=(logger)
@logger = logger
end
def logging? #:nodoc:
options[:log]
end
def class_for(class_name)
# Ruby 1.9 introduces an inherit argument for Module#const_get and
# #const_defined? and changes their default behavior.
# https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L89
if Module.method(:const_get).arity == 1
class_name.split('::').inject(Object) do |klass, partial_class_name|
klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name) : klass.const_missing(partial_class_name)
end
else
class_name.split('::').inject(Object) do |klass, partial_class_name|
klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name, false) : klass.const_missing(partial_class_name)
end
end
end
def check_for_url_clash(name,url,klass)
@names_url ||= {}
default_url = url || Attachment.default_options[:url]
if @names_url[name] && @names_url[name][:url] == default_url && @names_url[name][:class] != klass && @names_url[name][:url] !~ /:class/
log("Duplicate URL for #{name} with #{default_url}. This will clash with attachment defined in #{@names_url[name][:class]} class")
end
@names_url[name] = {:url => default_url, :class => klass}
end
def reset_duplicate_clash_check!
@names_url = nil
end
end
class PaperclipError < StandardError #:nodoc:
end
class StorageMethodNotFound < PaperclipError
end
class CommandNotFoundError < PaperclipError
end
class NotIdentifiedByImageMagickError < PaperclipError #:nodoc:
end
class InfiniteInterpolationError < PaperclipError #:nodoc:
end
module ClassMethods
# +has_attached_file+ gives the class it is called on an attribute that maps to a file. This
# is typically a file stored somewhere on the filesystem and has been uploaded by a user.
......@@ -287,7 +157,7 @@ module Paperclip
# "/assets/avatars/default_#{gender}.png"
# end
# end
def has_attached_file name, options = {}
def has_attached_file(name, options = {})
include InstanceMethods
if attachment_definitions.nil?
......@@ -407,40 +277,4 @@ module Paperclip
self.attachment_definitions
end
end
module InstanceMethods #:nodoc:
def attachment_for name
@_paperclip_attachments ||= {}
@_paperclip_attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
end
def each_attachment
self.class.attachment_definitions.each do |name, definition|
yield(name, attachment_for(name))
end
end
def save_attached_files
Paperclip.log("Saving attachments.")
each_attachment do |name, attachment|
attachment.send(:save)
end
end
def destroy_attached_files
Paperclip.log("Deleting attachments.")
each_attachment do |name, attachment|
attachment.send(:flush_deletes)
end
end
def prepare_for_destroy
Paperclip.log("Scheduling attachments for deletion.")
each_attachment do |name, attachment|
attachment.send(:queue_existing_for_delete)
end
end
end
end
......@@ -365,7 +365,7 @@ module Paperclip
def ensure_required_accessors! #:nodoc:
%w(file_name).each do |field|
unless @instance.respond_to?("#{name}_#{field}") && @instance.respond_to?("#{name}_#{field}=")
raise PaperclipError.new("#{@instance.class} model missing required attr_accessor for '#{name}_#{field}'")
raise Paperclip::Error.new("#{@instance.class} model missing required attr_accessor for '#{name}_#{field}'")
end
end
end
......@@ -383,7 +383,7 @@ module Paperclip
begin
storage_module = Paperclip::Storage.const_get(storage_class_name)
rescue NameError
raise StorageMethodNotFound, "Cannot load storage module '#{storage_class_name}'"
raise Errors::StorageMethodNotFound, "Cannot load storage module '#{storage_class_name}'"
end
self.extend(storage_module)
end
......@@ -428,7 +428,7 @@ module Paperclip
@queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
Paperclip.processor(processor).make(file, style.processor_options, self)
end
rescue PaperclipError => e
rescue Paperclip::Error => e
log("An error was received while processing: #{e.inspect}")
(@errors[:processing] ||= []) << e.message if @options[:whiny]
end
......
module Paperclip
# A base error class for Paperclip. Most of the error that will be thrown
# from Paperclip will inherits from this class.
class Error < StandardError
end
module Errors
# Will be thrown when a storage method is not found.
class StorageMethodNotFound < Paperclip::Error
end
# Will be thrown when a command or executable is not found.
class CommandNotFoundError < Paperclip::Error
end
# Will be thrown when ImageMagic cannot determine the uploaded file's
# metadata, usually this would mean the file is not an image.
class NotIdentifiedByImageMagickError < Paperclip::Error
end
# Will be thrown if the interpolation is creating an infinite loop. If you
# are creating an interpolator which might cause an infinite loop, you
# should be throwing this error upon the infinite loop as well.
class InfiniteInterpolationError < Paperclip::Error
end
end
end
......@@ -17,16 +17,16 @@ module Paperclip
# a Tempfile object, which would be eligible for file deletion when no longer referenced.
def self.from_file file
file_path = file.respond_to?(:path) ? file.path : file
raise(Paperclip::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name")) if file_path.blank?
raise(Errors::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name")) if file_path.blank?
geometry = begin
Paperclip.run("identify", "-format %wx%h :file", :file => "#{file_path}[0]")
rescue Cocaine::ExitStatusError
""
rescue Cocaine::CommandNotFoundError => e
raise Paperclip::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
raise Errors::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
end
parse(geometry) ||
raise(NotIdentifiedByImageMagickError.new("#{file_path} is not recognized by the 'identify' command."))
raise(Errors::NotIdentifiedByImageMagickError.new("#{file_path} is not recognized by the 'identify' command."))
end
# Parses a "WxH" formatted string, where W is the width and H is the height.
......
module Paperclip
module Helpers
def configure
yield(self) if block_given?
end
def interpolates key, &block
Paperclip::Interpolations[key] = block
end
# The run method takes the name of a binary to run, the arguments to that binary
# and some options:
#
# :command_path -> A $PATH-like variable that defines where to look for the binary
# on the filesystem. Colon-separated, just like $PATH.
#
# :expected_outcodes -> An array of integers that defines the expected exit codes
# of the binary. Defaults to [0].
#
# :log_command -> Log the command being run when set to true (defaults to false).
# This will only log if logging in general is set to true as well.
#
# :swallow_stderr -> Set to true if you don't care what happens on STDERR.
#
def run(cmd, arguments = "", local_options = {})
command_path = options[:command_path]
Cocaine::CommandLine.path = ( Cocaine::CommandLine.path ? [Cocaine::CommandLine.path].flatten | [command_path] : command_path )
local_options = local_options.merge(:logger => logger) if logging? && (options[:log_command] || local_options[:log_command])
Cocaine::CommandLine.new(cmd, arguments, local_options).run
end
# Find all instances of the given Active Record model +klass+ with attachment +name+.
# This method is used by the refresh rake tasks.
def each_instance_with_attachment(klass, name)
unscope_method = class_for(klass).respond_to?(:unscoped) ? :unscoped : :with_exclusive_scope
class_for(klass).send(unscope_method) do
class_for(klass).find_each(:conditions => "#{name}_file_name is not null") do |instance|
yield(instance)
end
end
end
def class_for(class_name)
# Ruby 1.9 introduces an inherit argument for Module#const_get and
# #const_defined? and changes their default behavior.
# https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L89
if Module.method(:const_get).arity == 1
class_name.split('::').inject(Object) do |klass, partial_class_name|
klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name) : klass.const_missing(partial_class_name)
end
else
class_name.split('::').inject(Object) do |klass, partial_class_name|
klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name, false) : klass.const_missing(partial_class_name)
end
end
end
def check_for_url_clash(name,url,klass)
@names_url ||= {}
default_url = url || Attachment.default_options[:url]
if @names_url[name] && @names_url[name][:url] == default_url && @names_url[name][:class] != klass && @names_url[name][:url] !~ /:class/
log("Duplicate URL for #{name} with #{default_url}. This will clash with attachment defined in #{@names_url[name][:class]} class")
end
@names_url[name] = {:url => default_url, :class => klass}
end
def reset_duplicate_clash_check!
@names_url = nil
end
end
end
module Paperclip
module InstanceMethods #:nodoc:
def attachment_for name
@_paperclip_attachments ||= {}
@_paperclip_attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
end
def each_attachment
self.class.attachment_definitions.each do |name, definition|
yield(name, attachment_for(name))
end
end
def save_attached_files
Paperclip.log("Saving attachments.")
each_attachment do |name, attachment|
attachment.send(:save)
end
end
def destroy_attached_files
Paperclip.log("Deleting attachments.")
each_attachment do |name, attachment|
attachment.send(:flush_deletes)
end
end
def prepare_for_destroy
Paperclip.log("Scheduling attachments for deletion.")
each_attachment do |name, attachment|
attachment.send(:queue_existing_for_delete)
end
end
end
end
......@@ -46,7 +46,7 @@ module Paperclip
# 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 caller.any?{|b| b.index(RIGHT_HERE) }
raise Errors::InfiniteInterpolationError if caller.any?{|b| b.index(RIGHT_HERE) }
attachment.url(style_name, :timestamp => false, :escape => false)
end
......
module Paperclip
module Logger
# Log a paperclip-specific line. This will log to STDOUT
# by default. Set Paperclip.options[:log] to false to turn off.
def log message
logger.info("[paperclip] #{message}") if logging?
end
def logger #:nodoc:
@logger ||= options[:logger] || Logger.new(STDOUT)
end
def logger=(logger)
@logger = logger
end
def logging? #:nodoc:
options[:log]
end
end
end
......@@ -34,6 +34,43 @@ module Paperclip
end
end
module ProcessorHelpers
def processor(name) #:nodoc:
@known_processors ||= {}
if @known_processors[name.to_s]
@known_processors[name.to_s]
else
name = name.to_s.camelize
load_processor(name) unless Paperclip.const_defined?(name)
processor = Paperclip.const_get(name)
@known_processors[name.to_s] = processor
end
end
def load_processor(name)
if defined?(Rails.root) && Rails.root
require File.expand_path(Rails.root.join("lib", "paperclip_processors", "#{name.underscore}.rb"))
end
end
def clear_processors!
@known_processors.try(:clear)
end
# You can add your own processor via the Paperclip configuration. Normally
# Paperclip will load all processors from the
# Rails.root/lib/paperclip_processors directory, but here you can add any
# existing class using this mechanism.
#
# Paperclip.configure do |c|
# c.register_processor :watermarker, WatermarkingProcessor.new
# end
def register_processor(name, processor)
@known_processors ||= {}
@known_processors[name.to_s] = processor
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
......
......@@ -76,9 +76,9 @@ module Paperclip
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
raise Paperclip::Error, "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.")
raise Paperclip::Errors::CommandNotFoundError.new("Could not run the `convert` command. Please install ImageMagick.")
end
dst
......
......@@ -512,7 +512,7 @@ class AttachmentTest < Test::Unit::TestCase
setup do
rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
@dummy = Dummy.new
Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.")
Paperclip::Thumbnail.expects(:make).raises(Paperclip::Error, "cannot be processed.")
@file = StringIO.new("...")
@file.stubs(:to_tempfile).returns(@file)
@dummy.avatar = @file
......@@ -579,7 +579,7 @@ class AttachmentTest < Test::Unit::TestCase
should "convert underscored storage name to camelcase" do
rebuild_model :storage => :not_here
@dummy = Dummy.new
exception = assert_raises(Paperclip::StorageMethodNotFound) do
exception = assert_raises(Paperclip::Errors::StorageMethodNotFound) do
@dummy.avatar
end
assert exception.message.include?("NotHere")
......@@ -588,7 +588,7 @@ class AttachmentTest < Test::Unit::TestCase
should "raise an error if you try to include a storage module that doesn't exist" do
rebuild_model :storage => :not_here
@dummy = Dummy.new
assert_raises(Paperclip::StorageMethodNotFound) do
assert_raises(Paperclip::Errors::StorageMethodNotFound) do
@dummy.avatar
end
end
......@@ -825,7 +825,7 @@ class AttachmentTest < Test::Unit::TestCase
should "raise if there are not the correct columns when you try to assign" do
@other_attachment = Paperclip::Attachment.new(:not_here, @instance)
assert_raises(Paperclip::PaperclipError) do
assert_raises(Paperclip::Error) do
@other_attachment.assign(@file)
end
end
......@@ -1011,7 +1011,7 @@ class AttachmentTest < Test::Unit::TestCase
end
should "not be able to find the module" do
assert_raise(Paperclip::StorageMethodNotFound){ Dummy.new.avatar }
assert_raise(Paperclip::Errors::StorageMethodNotFound){ Dummy.new.avatar }
end
end
end
......
......@@ -117,30 +117,30 @@ class GeometryTest < Test::Unit::TestCase
should "not generate from a bad file" do
file = "/home/This File Does Not Exist.omg"
assert_raise(Paperclip::NotIdentifiedByImageMagickError){ @geo = Paperclip::Geometry.from_file(file) }
assert_raise(Paperclip::Errors::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) }
assert_raise(Paperclip::Errors::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) }
assert_raise(Paperclip::Errors::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) }
assert_raise(Paperclip::Errors::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
assert_raises(Paperclip::Errors::CommandNotFoundError) do
file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
@geo = Paperclip::Geometry.from_file(file)
end
......
......@@ -155,7 +155,7 @@ class InterpolationsTest < Test::Unit::TestCase
Paperclip::Interpolations.url(self, :style)
end
end
assert_raises(Paperclip::InfiniteInterpolationError){ Paperclip::Interpolations.url(attachment, :style) }
assert_raises(Paperclip::Errors::InfiniteInterpolationError){ Paperclip::Interpolations.url(attachment, :style) }
end
should "return the filename as basename.extension" do
......
......@@ -77,7 +77,7 @@ class ThumbnailTest < Test::Unit::TestCase
old_path = ENV['PATH']
begin
ENV['PATH'] = ''
assert_raises(Paperclip::CommandNotFoundError) do
assert_raises(Paperclip::Errors::CommandNotFoundError) do
@thumb.make
end
ensure
......@@ -154,7 +154,7 @@ class ThumbnailTest < Test::Unit::TestCase
end
should "error when trying to create the thumbnail" do
assert_raises(Paperclip::PaperclipError) do
assert_raises(Paperclip::Error) do
@thumb.make
end
end
......@@ -194,7 +194,7 @@ class ThumbnailTest < Test::Unit::TestCase
end
should "error when trying to create the thumbnail" do
assert_raises(Paperclip::PaperclipError) do
assert_raises(Paperclip::Error) do
@thumb.make
end
end
......@@ -203,7 +203,7 @@ class ThumbnailTest < Test::Unit::TestCase
old_path = ENV['PATH']
begin
ENV['PATH'] = ''
assert_raises(Paperclip::CommandNotFoundError) do
assert_raises(Paperclip::Errors::CommandNotFoundError) do
@thumb.make
end
ensure
......
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