Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

I'm stupid and forgot to add files.

git-svn-id: https://svn.thoughtbot.com/plugins/paperclip/trunk@231 7bbfaf0e-4d1d-0410-9690-a8bb5f8ef2aa
  • Loading branch information...
commit 94925b26f2de502dff236b7939651654a5d3b9da 1 parent d46eea5
jyurek authored
View
245 lib/paperclip/attachment.rb
@@ -0,0 +1,245 @@
+module Thoughtbot
+ module Paperclip
+
+ class Attachment
+
+ attr_reader :name, :instance, :original_filename, :content_type, :original_file_size, :definition, :errors
+
+ def initialize name, active_record
+ @instance = active_record
+ @definition = @instance.class.attachment_definitions[name]
+ @name = name
+ @errors = []
+
+ self.original_filename = @instance["#{name}_file_name"]
+ self.content_type = @instance["#{name}_content_type"]
+ self.original_file_size = @instance["#{name}_file_size"]
+ @files = {}
+ @dirty = false
+
+ self.class.send :include, definition.storage_module
+ end
+
+ def assign uploaded_file
+ uploaded_file = fetch_uri(uploaded_file) if uploaded_file.is_a? URI
+ return queue_destroy if uploaded_file.nil?
+ return unless is_a_file? uploaded_file
+
+ self.original_filename = sanitize_filename(uploaded_file.original_filename)
+ self.content_type = uploaded_file.content_type
+ self.original_file_size = uploaded_file.size
+ self[:original] = uploaded_file
+ @dirty = true
+
+ if definition.type == :image
+ make_thumbnails_from uploaded_file
+ end
+ end
+
+ def [](style)
+ @files ||= {}
+ @files[style]
+ end
+ alias_method :file_for, :[]
+
+ def []=(style, data)
+ @files ||= {}
+ @files[style] = data
+ end
+
+ def clear_files
+ @files = nil
+ @dirty = false
+ end
+
+ def for_attached_files
+ @files.each do |style, data|
+ if data
+ data.rewind if data.respond_to? :rewind
+ yield style, (data.respond_to?(:read) ? data.read : data)
+ end
+ end
+ end
+
+ def dirty?
+ @dirty
+ end
+
+ # Validations
+
+ def valid?
+ definition.validations.each do |validation, constraints|
+ send("validate_#{validation}", *constraints)
+ end
+ errors.empty?
+ end
+
+ # ActiveRecord Callbacks
+
+ def save
+ write_attachment if dirty?
+ delete_attachment if @delete_on_save
+ @delete_on_save = false
+ clear_files
+ end
+
+ def queue_destroy(complain = false)
+ returning true do
+ @delete_on_save = true
+ @complain_on_delete = complain
+ self.original_filename = nil
+ self.content_type = nil
+ clear_files
+ end
+ end
+
+ def destroy
+ delete_attachment if definition.delete_on_destroy
+ end
+
+ # Image Methods
+
+ def make_thumbnails_from data
+ begin
+ definition.styles.each do |style, geometry|
+ self[style] = make_thumbnail geometry, data
+ end
+ rescue PaperclipError => e
+ errors << e.message
+ clear_files
+ self[:original] = data
+ end
+ end
+
+ def make_thumbnail geometry, data
+ return data if geometry.nil?
+
+ operator = geometry[-1,1]
+ begin
+ geometry, crop_geometry = geometry_for_crop(geometry, data) if operator == '#'
+ convert = Thoughtbot::Paperclip.path_for_command("convert")
+ command = "#{convert} - -scale '#{geometry}' #{operator == '#' ? "-crop '#{crop_geometry}'" : ""} - 2>/dev/null"
+ thumb = IO.popen(command, "w+") do |io|
+ data.rewind
+ io.write(data.read)
+ io.close_write
+ StringIO.new(io.read)
+ end
+ rescue Errno::EPIPE => e
+ raise PaperclipError.new(self), "could not be thumbnailed. Is ImageMagick or GraphicsMagick installed and available?"
+ rescue SystemCallError => e
+ raise PaperclipError.new(self), "could not be thumbnailed."
+ end
+ if Thoughtbot::Paperclip.options[:whiny_thumbnails] && !$?.success?
+ raise PaperclipError.new(self), "could not be thumbaniled because of an error with 'convert'."
+ end
+ thumb
+ end
+
+ def geometry_for_crop geometry, orig_io
+ identify = Thoughtbot::Paperclip.path_for_command("identify")
+ IO.popen("#{identify} -", "w+") do |io|
+ orig_io.rewind
+ io.write(orig_io.read)
+ io.close_write
+ if match = io.read.split[2].match(/(\d+)x(\d+)/)
+ src = match[1,2].map(&:to_f)
+ srch = src[0] > src[1]
+ dst = geometry.match(/(\d+)x(\d+)/)[1,2].map(&:to_f)
+ dsth = dst[0] > dst[1]
+ ar = src[0] / src[1]
+
+ scale_geometry, scale = if dst[0] == dst[1]
+ if srch
+ [ "x#{dst[1]}", src[1] / dst[1] ]
+ else
+ [ "#{dst[0]}x", src[0] / dst[0] ]
+ end
+ elsif dsth
+ [ "#{dst[0]}x", src[0] / dst[0] ]
+ else
+ [ "x#{dst[1]}", src[1] / dst[1] ]
+ end
+
+ crop_geometry = if dsth
+ "%dx%d+%d+%d" % [ dst[0], dst[1], 0, (src[1] / scale - dst[1]) / 2 ]
+ else
+ "%dx%d+%d+%d" % [ dst[0], dst[1], (src[0] / scale - dst[0]) / 2, 0 ]
+ end
+
+ [ scale_geometry, crop_geometry ]
+ end
+ end
+ end
+
+ # Helper Methods
+
+ def interpolate style, source
+ returning source.dup do |s|
+ s.gsub!(/:rails_root/, RAILS_ROOT)
+ s.gsub!(/:id/, instance.id.to_s) if instance.id
+ s.gsub!(/:class/, instance.class.to_s.underscore.pluralize)
+ s.gsub!(/:style/, style.to_s)
+ s.gsub!(/:attachment/, name.to_s.pluralize)
+ if original_filename
+ file_bits = original_filename.split(".")
+ s.gsub!(/:name/, original_filename)
+ s.gsub!(/:base/, [file_bits[0], *file_bits[1..-2]].join("."))
+ s.gsub!(/:ext/, file_bits.last )
+ end
+ end
+ end
+
+ def original_filename= new_name
+ instance["#{name}_file_name"] = @original_filename = new_name
+ end
+
+ def content_type= new_type
+ instance["#{name}_content_type"] = @content_type = new_type
+ end
+
+ def original_file_size= new_size
+ instance["#{name}_file_size"] = @original_file_size = new_size
+ end
+
+ def fetch_uri uri
+ image = if uri.scheme == 'file'
+ path = url.gsub(%r{^file://}, '/')
+ open(path)
+ else
+ require 'open-uri'
+ uri
+ end
+ begin
+ data = StringIO.new(image.read)
+ uri.extend(Upfile)
+ class << data
+ attr_accessor :original_filename, :content_type
+ end
+ data.original_filename = uri.original_filename
+ data.content_type = uri.content_type
+ data
+ rescue OpenURI::HTTPError => e
+ errors << "The file at #{uri.to_s} could not be found."
+ return nil
+ end
+ end
+
+ def is_a_file? data
+ [:size, :content_type, :original_filename, :read].map do |meth|
+ data.respond_to? meth
+ end.all?
+ end
+
+ def sanitize_filename filename
+ File.basename(filename).gsub(/[^\w\.\_]/,'_')
+ end
+ protected :sanitize_filename
+
+ def to_s
+ url
+ end
+
+ end
+ end
+end
View
93 lib/paperclip/attachment_definition.rb
@@ -0,0 +1,93 @@
+module Thoughtbot
+ module Paperclip
+ class AttachmentDefinition
+
+ def self.defaults
+ {
+ :path_prefix => ":rails_root/public",
+ :url_prefix => "",
+ :path => ":attachment/:id/:style_:name",
+ :url => nil,
+ :attachment_type => :image,
+ :thumbnails => {},
+ :delete_on_destroy => true,
+ :default_style => :original,
+ :missing_url => "",
+ :missing_path => "",
+ :storage => :filesystem
+ }
+ end
+
+ def initialize name, options
+ @name = name
+ @options = AttachmentDefinition.defaults.merge options
+ end
+
+ def name
+ @name
+ end
+
+ def styles
+ unless @styles
+ @styles = @options[:thumbnails]
+ @styles[:original] = nil
+ end
+ @styles
+ end
+
+ def validate thing, *constraints
+ @options[:"validate_#{thing}"] = (constraints.length == 1 ? constraints.first : constraints)
+ end
+
+ def validations
+ @validations ||= @options.inject({}) do |valids, opts|
+ key, val = opts
+ if (m = key.to_s.match(/^validate_(.+)/))
+ valids[m[1]] = val
+ end
+ valids
+ end
+ end
+
+ def storage_module
+ @storage_module ||= Thoughtbot::Paperclip::Storage.const_get(@options[:storage].to_s.camelize)
+ end
+
+ def type
+ @options[:attachment_type]
+ end
+
+ def default_style
+ @options[:default_style]
+ end
+
+ def path_prefix
+ @options[:path_prefix]
+ end
+
+ def url_prefix
+ @options[:url_prefix]
+ end
+
+ def path
+ @options[:path]
+ end
+
+ def url
+ @options[:url]
+ end
+
+ def missing_file_name
+ @options[:missing_path]
+ end
+
+ def missing_url
+ @options[:missing_url]
+ end
+
+ def delete_on_destroy
+ @options[:delete_on_destroy]
+ end
+ end
+ end
+end
View
33 lib/paperclip/upfile.rb
@@ -0,0 +1,33 @@
+module Thoughtbot
+ module Paperclip
+ # The Upfile module is a convenience module for adding uploaded-file-looking methods
+ # to the +File+ class. Useful for testing.
+ # f = File.new("test/test_avatar.jpg")
+ # f.original_filename # => "test_avatar.jpg"
+ # f.content_type # => "image/jpg"
+ # user.avatar = f
+ module Upfile
+ # Infer the MIME-type of the file from the extension.
+ def content_type
+ type = self.path.match(/\.(\w+)$/)[1]
+ case type
+ when "jpg", "png", "gif" then "image/#{type}"
+ when "txt", "csv", "xml", "html", "htm" then "text/#{type}"
+ else "x-application/#{type}"
+ end
+ end
+
+ # Returns the file's normal name.
+ def original_filename
+ self.path
+ end
+
+ # Returns the size of the file.
+ def size
+ File.size(self)
+ end
+ end
+ end
+end
+
+File.send(:include, Thoughtbot::Paperclip::Upfile)
Please sign in to comment.
Something went wrong with that request. Please try again.