0
@@ -46,7 +46,7 @@ module Technoweenie # :nodoc:
0
# only need to define these once on a class
0
unless included_modules.include? InstanceMethods
0
- class_inheritable_accessor :attachment_options
0
+ class_inheritable_accessor :attachment_options
, :attachment_attributes0
after_destroy :destroy_file
0
@@ -67,341 +67,5 @@ module Technoweenie # :nodoc:
0
self.attachment_options = options
0
- delegate :content_types, :to => Technoweenie::ActsAsAttachment
0
- # Performs common validations for attachment models.
0
- def validates_as_attachment
0
- validates_presence_of :size, :content_type, :filename
0
- validate :attachment_attributes_valid?
0
- # Returns true or false if the given content type is recognized as an image.
0
- def image?(content_type)
0
- content_types.include?(content_type)
0
- # Yields a block containing an RMagick Image for the given binary data.
0
- def with_image(binary_data, &block)
0
- binary_data = Magick::Image::from_blob(binary_data).first unless !Object.const_defined?(:Magick) || binary_data.is_a?(Magick::Image)
0
- block.call binary_data if block && binary_data
0
- # Log the failure to load the image. This should match ::Magick::ImageMagickError
0
- # but that would cause acts_as_attachment to require rmagick.
0
- logger.debug("Exception working with image: #{$!}")
0
- # Callback after an image has been resized.
0
- # class Foo < ActiveRecord::Base
0
- # after_resize do |record, img|
0
- # record.aspect_ratio = img.columns.to_f / img.rows.to_f
0
- def after_resize(&block)
0
- write_inheritable_array(:after_resize, [block])
0
- # Callback after an attachment has been saved either to the file system or the DB.
0
- # Only called if the file has been changed, not necessarily if the record is updated.
0
- # class Foo < ActiveRecord::Base
0
- # after_attachment_saved do |record|
0
- def after_attachment_saved(&block)
0
- write_inheritable_array(:after_attachment_saved, [block])
0
- # Callback before a thumbnail is saved. Use this to pass any necessary extra attributes that may be required.
0
- # class Foo < ActiveRecord::Base
0
- # before_thumbnail_saved do |record, thumbnail|
0
- def before_thumbnail_saved(&block)
0
- write_inheritable_array(:before_thumbnail_saved, [block])
0
- # Get the thumbnail class, which is the current attachment class by default.
0
- # Configure this with the :thumbnail_class option.
0
- attachment_options[:thumbnail_class] = attachment_options[:thumbnail_class].constantize unless attachment_options[:thumbnail_class].is_a?(Class)
0
- attachment_options[:thumbnail_class]
0
- module InstanceMethods
0
- # Checks whether the attachment's content type is an image content type
0
- self.class.image?(content_type.to_s.strip)
0
- self.class.thumbnail_class
0
- # Gets the thumbnail name for a filename. 'foo.jpg' becomes 'foo_thumbnail.jpg'
0
- def thumbnail_name_for(thumbnail = nil)
0
- return filename unless thumbnail
0
- basename, ext = filename.split '.'
0
- "#{basename}_#{thumbnail}.#{ext}"
0
- # Creates or updates the thumbnail for the current attachment.
0
- def create_or_update_thumbnail(file_name_suffix, *size)
0
- returning thumbnail_class.find_or_initialize_by_thumbnail_and_parent_id(file_name_suffix.to_s, id) do |thumb|
0
- resized_image = resize_image_to(size)
0
- return if resized_image.nil?
0
- :content_type => content_type,
0
- :filename => thumbnail_name_for(file_name_suffix),
0
- :attachment_data => resized_image
0
- callback_with_args :before_thumbnail_saved, thumb
0
- # This method handles the uploaded file object. If you set the field name to uploaded_data, you don't need
0
- # any special code in your controller.
0
- # <% form_for :attachment, :html => { :multipart => true } do |f| -%>
0
- # <p><%= f.file_field :uploaded_data %></p>
0
- # <p><%= submit_tag :Save %>
0
- # @attachment = Attachment.create! params[:attachment]
0
- def uploaded_data=(file_data)
0
- return nil if file_data.nil? || file_data.size == 0
0
- self.content_type = file_data.content_type.strip
0
- self.filename = file_data.original_filename.strip
0
- self.attachment_data = file_data.read
0
- # Sets the actual binary data. This is typically called by uploaded_data=, but you can call this
0
- # manually if you're creating from the console. This is also where the resizing occurs.
0
- def attachment_data=(data)
0
- @attachment_data = nil
0
- @save_attachment = false
0
- with_image data do |img|
0
- resized_img = (attachment_options[:resize_to] && parent_id.nil?) ?
0
- thumbnail_for_image(img, attachment_options[:resize_to]) : img
0
- data = resized_img.to_blob
0
- self.width = resized_img.columns if respond_to?(:width)
0
- self.height = resized_img.rows if respond_to?(:height)
0
- callback_with_args :after_resize, resized_img
0
- self.size = data.length
0
- @attachment_data = data
0
- @save_attachment = true
0
- # Resizes a thumbnail.
0
- def resize_image_to(size)
0
- thumb = thumbnail_for_image(img, size)
0
- # Returns the width/height in a suitable format for the image_tag helper: (100x100)
0
- [width.to_s, height.to_s] * 'x'
0
- # Allows you to work with an RMagick representation of the attachment in a block.
0
- # @attachment.with_image do |img|
0
- # self.data = img.thumbnail(100, 100).to_blob
0
- def with_image(data = self.attachment_data, &block)
0
- self.class.with_image(data, &block)
0
- self.class.thumbnail_class
0
- # Performs the actual resizing operation for a thumbnail
0
- def thumbnail_for_image(img, size)
0
- size = size.first if size.is_a?(Array) && size.length == 1 && !size.first.is_a?(Fixnum)
0
- if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
0
- size = [size, size] if size.is_a?(Fixnum)
0
- img.thumbnail(size.first, size[1])
0
- img.change_geometry(size.to_s) { |cols, rows, image| image.resize(cols, rows) }
0
- return unless filename
0
- # NOTE: File.basename doesn't work right with Windows paths on Unix
0
- # get only the filename, not the whole path
0
- filename.gsub!(/^.*(\\|\/)/, '')
0
- # Finally, replace all non alphanumeric, underscore or periods with underscore
0
- filename.gsub!(/[^\w\.\-]/, '_')
0
- # creates default thumbnails for parent attachments
0
- def create_attachment_thumbnails
0
- if image? && @save_attachment && !attachment_options[:thumbnails].blank? && parent_id.nil?
0
- attachment_options[:thumbnails].each { |suffix, size| create_or_update_thumbnail(suffix, size) }
0
- @save_attachment = nil
0
- callback :after_attachment_saved
0
- # validates the size and content_type attributes according to the current model's options
0
- def attachment_attributes_valid?
0
- [:size, :content_type].each do |attr_name|
0
- enum = attachment_options[attr_name]
0
- errors.add attr_name, ActiveRecord::Errors.default_error_messages[:inclusion] unless enum.nil? || enum.include?(send(attr_name))
0
- # Yanked from ActiveRecord::Callbacks, modified so I can pass args to the callbacks besides self.
0
- # Only accept blocks, however
0
- def callback_with_args(method, arg = self)
0
- callbacks_for(method).each do |callback|
0
- result = callback.call(self, arg)
0
- return false if result == false
0
- # Methods for DB backed attachments
0
- def self.included(base) #:nodoc:
0
- base.belongs_to :db_file
0
- base.before_save :save_to_storage # so the db_file_id can be set
0
- # Gets the attachment data
0
- @attachment_data ||= db_file.data
0
- # Destroys the file. Called in the after_destroy callback
0
- db_file.destroy if db_file
0
- # Saves the data to the DbFile model
0
- (db_file || build_db_file).data = attachment_data
0
- self.db_file_id = db_file.id # needed for my own sanity, k thx
0
- # Methods for file system backed attachments
0
- module FileSystemMethods
0
- def self.included(base) #:nodoc:
0
- base.before_update :rename_file
0
- base.after_save :save_to_storage # so the id can be part of the url
0
- # Gets the attachment data
0
- return @attachment_data if @attachment_data
0
- filename = full_filename
0
- File.open(filename, 'rb') do |file|
0
- @attachment_data = file.read
0
- end if File.file?(filename)
0
- # Gets the full path to the filename in this format:
0
- # # This assumes a model name like MyModel
0
- # # public/#{table_name} is the default filesystem path
0
- # RAILS_ROOT/public/my_models/5/blah.jpg
0
- # Overwrite this method in your model to customize the filename.
0
- # The optional thumbnail argument will output the thumbnail's filename.
0
- def full_filename(thumbnail = nil)
0
- file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:file_system_path]
0
- File.join(RAILS_ROOT, file_system_path, (parent_id || id).to_s, thumbnail_name_for(thumbnail))
0
- # Used as the base path that #public_filename strips off full_filename to create the public path
0
- @base_path ||= File.join(RAILS_ROOT, 'public')
0
- # Gets the public path to the file
0
- # The optional thumbnail argument will output the thumbnail's filename.
0
- def public_filename(thumbnail = nil)
0
- full_filename(thumbnail).gsub %r(^#{Regexp.escape(base_path)}), ''
0
- @old_filename = full_filename unless filename.nil? || @old_filename
0
- write_attribute :filename, value
0
- # Destroys the file. Called in the after_destroy callback
0
- FileUtils.rm full_filename rescue nil
0
- return unless @old_filename && @old_filename != full_filename
0
- if @save_attachment && File.exists?(@old_filename)
0
- FileUtils.rm @old_filename
0
- elsif File.exists?(@old_filename)
0
- FileUtils.mv @old_filename, full_filename
0
- # Saves the file to the file system
0
- # TODO: This overwrites the file if it exists, maybe have an allow_overwrite option?
0
- FileUtils.mkdir_p(File.dirname(full_filename))
0
- # TODO Convert to streaming storage to prevent excessive memory usage
0
- # FileUtils.copy_stream is very efficient in regards to copies
0
- # OR - get the tmp filename for large files and do FileUtils.cp ? *agile*
0
- File.open(full_filename, "wb") do |file|
0
- file.write(attachment_data)