move processors and backends to separate modules

git-svn-id: http://svn.techno-weenie.net/projects/plugins/attachment_fu@2569 567b1171-46fb-0310-a4c9-b4bef9110e78
This commit is contained in:
technoweenie 2006-12-23 06:23:04 +00:00
Родитель 79f29133f3
Коммит c3b5889fb4
7 изменённых файлов: 242 добавлений и 227 удалений

Просмотреть файл

@ -1 +1,10 @@
require 'tempfile'
class Tempfile
# overwrite so tempfiles have no extension
def make_tmpname(basename, n)
sprintf("%s%d-%d", basename, $$, n)
end
end
ActiveRecord::Base.send(:extend, Technoweenie::AttachmentFu::ActMethods)

Просмотреть файл

@ -1,19 +1,9 @@
require File.join(File.dirname(__FILE__), 'attachment_fu', 'backends')
require File.join(File.dirname(__FILE__), 'attachment_fu', 'processors')
require 'tempfile'
class Tempfile
# overwrite so tempfiles have no extension
def make_tmpname(basename, n)
sprintf("%s%d-%d", basename, $$, n)
end
end
module Technoweenie # :nodoc:
module AttachmentFu # :nodoc:
@@default_processors = %w(Rmagick)
@@tempfile_path = File.join(RAILS_ROOT, 'tmp', 'attachment_fu')
@@content_types = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png']
mattr_reader :content_types, :tempfile_path
mattr_reader :content_types, :tempfile_path, :default_processors
class ThumbnailError < StandardError; end
class AttachmentError < StandardError; end
@ -57,7 +47,6 @@ module Technoweenie # :nodoc:
class_inheritable_accessor :attachment_options
attr_accessor :thumbnail_resize_options
options[:processor] ||= :rmagick
options[:storage] ||= options[:file_system_path] ? :file_system : :db_file
options[:file_system_path] ||= File.join("public", table_name)
options[:file_system_path] = options[:file_system_path][1..-1] if options[:file_system_path].first == '/'
@ -72,8 +61,20 @@ module Technoweenie # :nodoc:
after_destroy :destroy_file
extend ClassMethods
include InstanceMethods
include Technoweenie::AttachmentFu::const_get("#{options[:storage].to_s.classify}Backend")
include Technoweenie::AttachmentFu::const_get("#{options[:processor].to_s.classify}Processor")
include Technoweenie::AttachmentFu::Backends.const_get("#{options[:storage].to_s.classify}")
case options[:processor]
when :none
when nil
processors = Technoweenie::AttachmentFu.default_processors.dup
begin
include Technoweenie::AttachmentFu::Processors.const_get(processors.first) if processors.any?
rescue LoadError
processors.shift
retry
end
else
include Technoweenie::AttachmentFu::Processors.const_get("#{options[:processor].to_s.classify}")
end
before_save :process_attachment
end

Просмотреть файл

@ -1,110 +0,0 @@
module Technoweenie # :nodoc:
module AttachmentFu # :nodoc:
# Methods for file system backed attachments
module FileSystemBackend
def self.included(base) #:nodoc:
base.before_update :rename_file
end
# Gets the full path to the filename in this format:
#
# # This assumes a model name like MyModel
# # public/#{table_name} is the default filesystem path
# RAILS_ROOT/public/my_models/5/blah.jpg
#
# Overwrite this method in your model to customize the filename.
# The optional thumbnail argument will output the thumbnail's filename.
def full_filename(thumbnail = nil)
file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:file_system_path].to_s
File.join(RAILS_ROOT, file_system_path, attachment_path_id, thumbnail_name_for(thumbnail))
end
# Used as the base path that #public_filename strips off full_filename to create the public path
def base_path
@base_path ||= File.join(RAILS_ROOT, 'public')
end
# The attachment ID used in the full path of a file
def attachment_path_id
((respond_to?(:parent_id) && parent_id) || id).to_s
end
# Gets the public path to the file
# The optional thumbnail argument will output the thumbnail's filename.
def public_filename(thumbnail = nil)
full_filename(thumbnail).gsub %r(^#{Regexp.escape(base_path)}), ''
end
def filename=(value)
@old_filename = full_filename unless filename.nil? || @old_filename
write_attribute :filename, sanitize_filename(value)
end
def create_temp_file
copy_to_temp_file full_filename
end
# Destroys the file. Called in the after_destroy callback
def destroy_file
FileUtils.rm full_filename rescue nil
end
def rename_file
return unless @old_filename && @old_filename != full_filename
if save_attachment? && File.exists?(@old_filename)
FileUtils.rm @old_filename
elsif File.exists?(@old_filename)
FileUtils.mv @old_filename, full_filename
end
@old_filename = nil
true
end
# Saves the file to the file system
def save_to_storage
if save_attachment?
# TODO: This overwrites the file if it exists, maybe have an allow_overwrite option?
FileUtils.mkdir_p(File.dirname(full_filename))
FileUtils.mv temp_path, full_filename
end
@old_filename = nil
true
end
def current_data
File.file?(full_filename) ? File.read(full_filename) : nil
end
end
# Methods for DB backed attachments
module DbFileBackend
def self.included(base) #:nodoc:
Object.const_set(:DbFile, Class.new(ActiveRecord::Base)) unless Object.const_defined?(:DbFile)
base.belongs_to :db_file, :class_name => '::DbFile', :foreign_key => 'db_file_id'
end
def create_temp_file
write_to_temp_file db_file.data
end
# Destroys the file. Called in the after_destroy callback
def destroy_file
db_file.destroy if db_file
end
# Saves the data to the DbFile model
def save_to_storage
if save_attachment?
(db_file || build_db_file).data = temp_data
db_file.save!
self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
end
true
end
def current_data
db_file.data
end
end
end
end

Просмотреть файл

@ -0,0 +1,36 @@
module Technoweenie # :nodoc:
module AttachmentFu # :nodoc:
module Backends
# Methods for DB backed attachments
module DbFile
def self.included(base) #:nodoc:
Object.const_set(:DbFile, Class.new(ActiveRecord::Base)) unless Object.const_defined?(:DbFile)
base.belongs_to :db_file, :class_name => '::DbFile', :foreign_key => 'db_file_id'
end
def create_temp_file
write_to_temp_file db_file.data
end
# Destroys the file. Called in the after_destroy callback
def destroy_file
db_file.destroy if db_file
end
# Saves the data to the DbFile model
def save_to_storage
if save_attachment?
(db_file || build_db_file).data = temp_data
db_file.save!
self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
end
true
end
def current_data
db_file.data
end
end
end
end
end

Просмотреть файл

@ -0,0 +1,81 @@
module Technoweenie # :nodoc:
module AttachmentFu # :nodoc:
module Backends
# Methods for file system backed attachments
module FileSystem
def self.included(base) #:nodoc:
base.before_update :rename_file
end
# Gets the full path to the filename in this format:
#
# # This assumes a model name like MyModel
# # public/#{table_name} is the default filesystem path
# RAILS_ROOT/public/my_models/5/blah.jpg
#
# Overwrite this method in your model to customize the filename.
# The optional thumbnail argument will output the thumbnail's filename.
def full_filename(thumbnail = nil)
file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:file_system_path].to_s
File.join(RAILS_ROOT, file_system_path, attachment_path_id, thumbnail_name_for(thumbnail))
end
# Used as the base path that #public_filename strips off full_filename to create the public path
def base_path
@base_path ||= File.join(RAILS_ROOT, 'public')
end
# The attachment ID used in the full path of a file
def attachment_path_id
((respond_to?(:parent_id) && parent_id) || id).to_s
end
# Gets the public path to the file
# The optional thumbnail argument will output the thumbnail's filename.
def public_filename(thumbnail = nil)
full_filename(thumbnail).gsub %r(^#{Regexp.escape(base_path)}), ''
end
def filename=(value)
@old_filename = full_filename unless filename.nil? || @old_filename
write_attribute :filename, sanitize_filename(value)
end
def create_temp_file
copy_to_temp_file full_filename
end
# Destroys the file. Called in the after_destroy callback
def destroy_file
FileUtils.rm full_filename rescue nil
end
def rename_file
return unless @old_filename && @old_filename != full_filename
if save_attachment? && File.exists?(@old_filename)
FileUtils.rm @old_filename
elsif File.exists?(@old_filename)
FileUtils.mv @old_filename, full_filename
end
@old_filename = nil
true
end
# Saves the file to the file system
def save_to_storage
if save_attachment?
# TODO: This overwrites the file if it exists, maybe have an allow_overwrite option?
FileUtils.mkdir_p(File.dirname(full_filename))
FileUtils.mv temp_path, full_filename
end
@old_filename = nil
true
end
def current_data
File.file?(full_filename) ? File.read(full_filename) : nil
end
end
end
end
end

Просмотреть файл

@ -1,100 +0,0 @@
module Technoweenie # :nodoc:
module AttachmentFu # :nodoc:
module RmagickProcessor
def self.included(base)
begin
require 'RMagick'
rescue LoadError
# boo hoo no rmagick
end
base.send :extend, ClassMethods
base.alias_method_chain :process_attachment, :processing
base.alias_method_chain :after_process_attachment, :processing
end
module ClassMethods
# Yields a block containing an RMagick Image for the given binary data.
def with_image(file, &block)
begin
binary_data = file.is_a?(Magick::Image) ? file : Magick::Image.read(file).first unless !Object.const_defined?(:Magick)
rescue
# Log the failure to load the image. This should match ::Magick::ImageMagickError
# but that would cause acts_as_attachment to require rmagick.
logger.debug("Exception working with image: #{$!}")
binary_data = nil
end
block.call binary_data if block && binary_data
ensure
!binary_data.nil?
end
end
# Allows you to work with an RMagick representation of the attachment in a block.
#
# @attachment.with_image do |img|
# self.data = img.thumbnail(100, 100).to_blob
# end
#
def with_image(&block)
self.class.with_image(temp_path, &block)
end
# Creates or updates the thumbnail for the current attachment.
def create_or_update_thumbnail(temp_file, file_name_suffix, *size)
thumbnailable? || raise(ThumbnailError.new("Can't create a thumbnail if the content type is not an image or there is no parent_id column"))
returning find_or_initialize_thumbnail(file_name_suffix) do |thumb|
thumb.attributes = {
:content_type => content_type,
:filename => thumbnail_name_for(file_name_suffix),
:temp_path => temp_file,
:thumbnail_resize_options => size
}
callback_with_args :before_thumbnail_saved, thumb
thumb.save!
end
end
protected
def process_attachment_with_processing
return unless process_attachment_without_processing
with_image do |img|
if !respond_to?(:parent_id) || parent_id.nil? # parent image
thumbnail_for_image(img, attachment_options[:resize_to]) if attachment_options[:resize_to]
else # thumbnail
thumbnail_for_image(img, thumbnail_resize_options) if thumbnail_resize_options
end
self.width = img.columns if respond_to?(:width)
self.height = img.rows if respond_to?(:height)
callback_with_args :after_resize, img
end if image?
end
def after_process_attachment_with_processing
return unless @saved_attachment
if thumbnailable? && !attachment_options[:thumbnails].blank? && parent_id.nil?
temp_file = temp_path || create_temp_file
attachment_options[:thumbnails].each { |suffix, size| create_or_update_thumbnail(temp_file, suffix, *size) }
end
after_process_attachment_without_processing
end
# Performs the actual resizing operation for a thumbnail
def thumbnail_for_image(img, size)
size = size.first if size.is_a?(Array) && size.length == 1 && !size.first.is_a?(Fixnum)
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
size = [size, size] if size.is_a?(Fixnum)
img.thumbnail!(*size)
else
img.change_geometry(size.to_s) { |cols, rows, image| image.resize!(cols, rows) }
end
self.temp_path = write_to_temp_file(img.to_blob)
end
def find_or_initialize_thumbnail(file_name_suffix)
respond_to?(:parent_id) ?
thumbnail_class.find_or_initialize_by_thumbnail_and_parent_id(file_name_suffix.to_s, id) :
thumbnail_class.find_or_initialize_by_thumbnail(file_name_suffix.to_s)
end
end
end
end

Просмотреть файл

@ -0,0 +1,98 @@
require 'RMagick'
module Technoweenie # :nodoc:
module AttachmentFu # :nodoc:
module Processors
module Rmagick
def self.included(base)
base.send :extend, ClassMethods
base.alias_method_chain :process_attachment, :processing
base.alias_method_chain :after_process_attachment, :processing
end
module ClassMethods
# Yields a block containing an RMagick Image for the given binary data.
def with_image(file, &block)
begin
binary_data = file.is_a?(Magick::Image) ? file : Magick::Image.read(file).first unless !Object.const_defined?(:Magick)
rescue
# Log the failure to load the image. This should match ::Magick::ImageMagickError
# but that would cause acts_as_attachment to require rmagick.
logger.debug("Exception working with image: #{$!}")
binary_data = nil
end
block.call binary_data if block && binary_data
ensure
!binary_data.nil?
end
end
# Allows you to work with an RMagick representation of the attachment in a block.
#
# @attachment.with_image do |img|
# self.data = img.thumbnail(100, 100).to_blob
# end
#
def with_image(&block)
self.class.with_image(temp_path, &block)
end
# Creates or updates the thumbnail for the current attachment.
def create_or_update_thumbnail(temp_file, file_name_suffix, *size)
thumbnailable? || raise(ThumbnailError.new("Can't create a thumbnail if the content type is not an image or there is no parent_id column"))
returning find_or_initialize_thumbnail(file_name_suffix) do |thumb|
thumb.attributes = {
:content_type => content_type,
:filename => thumbnail_name_for(file_name_suffix),
:temp_path => temp_file,
:thumbnail_resize_options => size
}
callback_with_args :before_thumbnail_saved, thumb
thumb.save!
end
end
protected
def process_attachment_with_processing
return unless process_attachment_without_processing
with_image do |img|
if !respond_to?(:parent_id) || parent_id.nil? # parent image
thumbnail_for_image(img, attachment_options[:resize_to]) if attachment_options[:resize_to]
else # thumbnail
thumbnail_for_image(img, thumbnail_resize_options) if thumbnail_resize_options
end
self.width = img.columns if respond_to?(:width)
self.height = img.rows if respond_to?(:height)
callback_with_args :after_resize, img
end if image?
end
def after_process_attachment_with_processing
return unless @saved_attachment
if thumbnailable? && !attachment_options[:thumbnails].blank? && parent_id.nil?
temp_file = temp_path || create_temp_file
attachment_options[:thumbnails].each { |suffix, size| create_or_update_thumbnail(temp_file, suffix, *size) }
end
after_process_attachment_without_processing
end
# Performs the actual resizing operation for a thumbnail
def thumbnail_for_image(img, size)
size = size.first if size.is_a?(Array) && size.length == 1 && !size.first.is_a?(Fixnum)
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
size = [size, size] if size.is_a?(Fixnum)
img.thumbnail!(*size)
else
img.change_geometry(size.to_s) { |cols, rows, image| image.resize!(cols, rows) }
end
self.temp_path = write_to_temp_file(img.to_blob)
end
def find_or_initialize_thumbnail(file_name_suffix)
respond_to?(:parent_id) ?
thumbnail_class.find_or_initialize_by_thumbnail_and_parent_id(file_name_suffix.to_s, id) :
thumbnail_class.find_or_initialize_by_thumbnail(file_name_suffix.to_s)
end
end
end
end
end