Skip to content
This repository has been archived by the owner on Jul 13, 2023. It is now read-only.

Commit

Permalink
Extract some modules from Paperclip.rb
Browse files Browse the repository at this point in the history
  • Loading branch information
sikachu committed Mar 23, 2012
1 parent 5232b19 commit 03f777f
Show file tree
Hide file tree
Showing 14 changed files with 242 additions and 217 deletions.
220 changes: 27 additions & 193 deletions lib/paperclip.rb
Expand Up @@ -41,170 +41,40 @@
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'

# The base module that gets included in ActiveRecord::Base. See the
# documentation for Paperclip::ClassMethods for more useful information.
module Paperclip

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.
# * log: Logs progress to the Rails log. Uses ActiveRecord's logger, so honors
# log levels, etc. Defaults to true.
# * 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
@options ||= {
:whiny => true,
:image_magick_path => nil,
:command_path => nil,
:log => true,
:log_command => true,
:swallow_stderr => true
}
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:
extend Helpers
extend Logger
extend ProcessorHelpers

# 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.
# * log: Logs progress to the Rails log. Uses ActiveRecord's logger, so honors
# log levels, etc. Defaults to true.
# * 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 self.options
@options ||= {
:whiny => true,
:image_magick_path => nil,
:command_path => nil,
:log => true,
:log_command => true,
:swallow_stderr => true
}
end

module ClassMethods
Expand Down Expand Up @@ -287,7 +157,7 @@ module ClassMethods
# "/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?
Expand Down Expand Up @@ -407,40 +277,4 @@ def attachment_definitions
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
6 changes: 3 additions & 3 deletions lib/paperclip/attachment.rb
Expand Up @@ -365,7 +365,7 @@ def path_option
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
Expand All @@ -383,7 +383,7 @@ def initialize_storage #:nodoc:
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
Expand Down Expand Up @@ -428,7 +428,7 @@ def post_process_style(name, style) #:nodoc:
@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
Expand Down
27 changes: 27 additions & 0 deletions lib/paperclip/errors.rb
@@ -0,0 +1,27 @@
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
6 changes: 3 additions & 3 deletions lib/paperclip/geometry.rb
Expand Up @@ -17,16 +17,16 @@ def initialize width = nil, height = nil, modifier = nil
# 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.
Expand Down
71 changes: 71 additions & 0 deletions lib/paperclip/helpers.rb
@@ -0,0 +1,71 @@
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

0 comments on commit 03f777f

Please sign in to comment.