From 6539e5401e7f625d99c43a375915a3a37e5c2ad5 Mon Sep 17 00:00:00 2001 From: Jon Yurek Date: Sun, 10 May 2009 18:07:34 -0400 Subject: [PATCH] Updated documentation and gemspec for 2.2.9 --- README.rdoc | 16 ++++++++------- lib/paperclip.rb | 36 +++++++++++++++++++++++++-------- lib/paperclip/attachment.rb | 20 +++++++++--------- lib/paperclip/interpolations.rb | 30 +++++++++++++++++++++++++++ paperclip.gemspec | 9 +++------ 5 files changed, 80 insertions(+), 31 deletions(-) diff --git a/README.rdoc b/README.rdoc index 85fcf4b43..2254c9a0b 100644 --- a/README.rdoc +++ b/README.rdoc @@ -79,8 +79,7 @@ validates_attachment_size. The files that are assigned as attachments are, by default, placed in the directory specified by the :path option to has_attached_file. By default, this -location is -":rails_root/public/system/:attachment/:id/:style/:basename.:extension". This +location is ":rails_root/public/system/:attachment/:id/:style/:filename". This location was chosen because on standard Capistrano deployments, the public/system directory is symlinked to the app's shared directory, meaning it will survive between deployments. For example, using that :path, you may have a @@ -89,7 +88,7 @@ file at /data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png NOTE: This is a change from previous versions of Paperclip, but is overall a -safer choice for the defaul file store. +safer choice for the default file store. You may also choose to store your files using Amazon's S3 service. You can find more information about S3 storage at the description for @@ -104,9 +103,9 @@ variables. ==Post Processing -Paperclip supports an extendible selection of post-processors. When you define +Paperclip supports an extensible selection of post-processors. When you define a set of styles for an attachment, by default it is expected that those -"styles" are actually "thumbnails". However, you can do more than just +"styles" are actually "thumbnails". However, you can do much more than just thumbnail images. By defining a subclass of Paperclip::Processor, you can perform any processing you want on the files that are attached. Any file in your Rails app's lib/paperclip_processors directory is automatically loaded by @@ -141,7 +140,10 @@ For example, assuming we had this definition: then both the :rotator processor and the :ocr processor would receive the options "{ :quality => :better }". This parameter may not mean anything to one -or more or the processors, and they are free to ignore it. +or more or the processors, and they are expected to ignore it. + +NOTE: Because processors operate by turning the original attachment into the +styles, no processors will be run if there are no styles defined. ==Events @@ -157,7 +159,7 @@ will halt. Returning false in an after_ filter will not halt anything, but you can access the model and the attachment if necessary. NOTE: Post processing will not even *start* if the attachment is not valid -according to the validations. Your callbacks (and processors) will only be +according to the validations. Your callbacks and processors will *only* be called with valid attachments. ==Contributing diff --git a/lib/paperclip.rb b/lib/paperclip.rb index ba2651768..d183d9bb4 100644 --- a/lib/paperclip.rb +++ b/lib/paperclip.rb @@ -5,7 +5,7 @@ # columns to your table. # # Author:: Jon Yurek -# Copyright:: Copyright (c) 2008 thoughtbot, inc. +# Copyright:: Copyright (c) 2008-2009 thoughtbot, inc. # License:: MIT License (http://www.opensource.org/licenses/mit-license.php) # # Paperclip defines an attachment as any file, though it makes special considerations @@ -48,7 +48,7 @@ module Paperclip class << self # Provides configurability to Paperclip. There are a number of options available, such as: - # * whiny_thumbnails: Will raise an error if Paperclip cannot process thumbnails of + # * 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. @@ -88,6 +88,10 @@ def interpolates key, &block # If the command returns with a result code that is not one of the # expected_outcodes, a PaperclipCommandLineError will be raised. Generally # a code of 0 is expected, but a list of codes may be passed if necessary. + # + # This method can log the command being run when + # Paperclip.options[:log_command] is set to true (defaults to false). This + # will only log if logging in general is set to true as well. def run cmd, params = "", expected_outcodes = 0 command = %Q<#{%Q[#{path_for_command(cmd)} #{params}].gsub(/\s+/, " ")}> command = "#{command} 2>#{bit_bucket}" if Paperclip.options[:swallow_stderr] @@ -119,15 +123,17 @@ def processor name #:nodoc: processor end + # Log a paperclip-specific line. Uses ActiveRecord::Base.logger + # by default. Set Paperclip.options[:log] to false to turn off. def log message logger.info("[paperclip] #{message}") if logging? end - def logger + def logger #:nodoc: ActiveRecord::Base.logger end - def logging? + def logging? #:nodoc: options[:log] end end @@ -159,9 +165,9 @@ module ClassMethods # that can control permissions. You can specify the full domain and path, but usually # just an absolute path is sufficient. The leading slash *must* be included manually for # absolute paths. The default value is - # "/system/:attachment/:id/:style/:basename.:extension". See + # "/system/:attachment/:id/:style/:filename". See # Paperclip::Attachment#interpolate for more information on variable interpolaton. - # :url => "/:class/:attachment/:id/:style_:basename.:extension" + # :url => "/:class/:attachment/:id/:style_:filename" # :url => "http://some.other.host/stuff/:class/:id_:extension" # * +default_url+: The URL that will be returned if there is no attachment assigned. # This field is interpolated just as the url is. The default value is @@ -179,9 +185,10 @@ module ClassMethods # has_attached_file :avatar, :styles => { :normal => "100x100#" }, # :default_style => :normal # user.avatar.url # => "/avatars/23/normal_me.png" - # * +whiny_thumbnails+: Will raise an error if Paperclip cannot post_process an uploaded file due + # * +whiny+: Will raise an error if Paperclip cannot post_process an uploaded file due # to a command line error. This will override the global setting for this attachment. - # Defaults to true. + # Defaults to true. This option used to be called :whiny_thumbanils, but this is + # deprecated. # * +convert_options+: When creating thumbnails, use this free-form options # field to pass in various convert command options. Typical options are "-strip" to # remove all Exif data from the image (save space for thumbnails and avatars) or @@ -197,6 +204,9 @@ module ClassMethods # :all => "-strip", # :negative => "-negate" # } + # NOTE: While not deprecated yet, it is not recommended to specify options this way. + # It is recommended that :convert_options option be included in the hash passed to each + # :styles for compatability with future versions. # * +storage+: Chooses the storage backend where the files will be stored. The current # choices are :filesystem and :s3. The default is :filesystem. Make sure you read the # documentation for Paperclip::Storage::Filesystem and Paperclip::Storage::S3 @@ -238,6 +248,9 @@ def has_attached_file name, options = {} # * +less_than+: equivalent to :in => 0..options[:less_than] # * +greater_than+: equivalent to :in => options[:greater_than]..Infinity # * +message+: error message to display, use :min and :max as replacements + # * +if+: A lambda or name of a method on the instance. Validation will only + # be run is this lambda or method returns true. + # * +unless+: Same as +if+ but validates if lambda or method returns false. def validates_attachment_size name, options = {} min = options[:greater_than] || (options[:in] && options[:in].first) || 0 max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0) @@ -259,6 +272,10 @@ def validates_attachment_thumbnails name, options = {} end # Places ActiveRecord-style validations on the presence of a file. + # Options: + # * +if+: A lambda or name of a method on the instance. Validation will only + # be run is this lambda or method returns true. + # * +unless+: Same as +if+ but validates if lambda or method returns false. def validates_attachment_presence name, options = {} message = options[:message] || "must be set." attachment_definitions[name][:validations] << [:presence, {:message => message, @@ -276,6 +293,9 @@ def validates_attachment_presence name, options = {} # match. Allows all by default. # * +message+: The message to display when the uploaded file has an invalid # content type. + # * +if+: A lambda or name of a method on the instance. Validation will only + # be run is this lambda or method returns true. + # * +unless+: Same as +if+ but validates if lambda or method returns false. # NOTE: If you do not specify an [attachment]_content_type field on your # model, content_type validation will work _ONLY upon assignment_ and # re-validation after the instance has been reloaded will always succeed. diff --git a/lib/paperclip/attachment.rb b/lib/paperclip/attachment.rb index a56710dde..e34cc8c6f 100644 --- a/lib/paperclip/attachment.rb +++ b/lib/paperclip/attachment.rb @@ -189,11 +189,11 @@ def updated_at time && time.to_i end - # A hash of procs that are run during the interpolation of a path or url. - # A variable of the format :name will be replaced with the return value of - # the proc named ":name". Each lambda takes the attachment and the current - # style as arguments. This hash can be added to with your own proc if - # necessary. + # Paths and URLs can have a number of variables interpolated into them + # to vary the storage location based on name, id, style, class, etc. + # This method is a deprecated access into supplying and retrieving these + # interpolations. Future access should use either Paperclip.interpolates + # or extend the Paperclip::Interpolations module directly. def self.interpolations warn('[DEPRECATION] Paperclip::Attachment.interpolations is deprecated ' + 'and will be removed from future versions. ' + @@ -250,7 +250,7 @@ def instance_read(attr) private - def ensure_required_accessors! + 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}'") @@ -279,11 +279,11 @@ def validate #:nodoc: @validation_errors end - def allow_validation? options + def allow_validation? options #:nodoc: (options[:if].nil? || check_guard(options[:if])) && (options[:unless].nil? || !check_guard(options[:unless])) end - def check_guard guard + def check_guard guard #:nodoc: if guard.respond_to? :call guard.call(instance) elsif ! guard.blank? @@ -364,7 +364,7 @@ def post_process #:nodoc: return if fire_events(:after) end - def fire_events(which) + def fire_events(which) #:nodoc: return true if callback(:"#{which}_post_process") == false return true if callback(:"#{which}_#{name}_post_process") == false end @@ -373,7 +373,7 @@ def callback which #:nodoc: instance.run_callbacks(which, @queued_for_write){|result, obj| result == false } end - def post_process_styles + def post_process_styles #:nodoc: @styles.each do |name, args| begin raise RuntimeError.new("Style #{name} has no processors defined.") if args[:processors].blank? diff --git a/lib/paperclip/interpolations.rb b/lib/paperclip/interpolations.rb index 0b17fd443..97743a7f6 100644 --- a/lib/paperclip/interpolations.rb +++ b/lib/paperclip/interpolations.rb @@ -1,19 +1,30 @@ module Paperclip + # This module contains all the methods that are available for interpolation + # in paths and urls. To add your own (or override an existing one), you + # can either open this module and define it, or call the + # Paperclip.interpolates method. module Interpolations extend self + # Hash assignment of interpolations. Included only for compatability, + # and is not intended for normal use. def self.[]= name, block define_method(name, &block) end + # Hash access of interpolations. Included only for compatability, + # and is not intended for normal use. def self.[] name method(name) end + # Returns a sorted list of all interpolations. def self.all self.instance_methods(false).sort end + # Perform the actual interpolation. Takes the pattern to interpolate + # and the arguments to pass, which are the attachment and style name. def self.interpolate pattern, *args all.reverse.inject( pattern.dup ) do |result, tag| result.gsub(/:#{tag}/) do |match| @@ -22,52 +33,71 @@ def self.interpolate pattern, *args end end + # Returns the filename, the same way as ":basename.:extension" would. def filename attachment, style "#{basename(attachment, style)}.#{extension(attachment, style)}" end + # Returns the interpolated URL. Will raise an error if the url itself + # contains ":url" to prevent infinite recursion. This interpolation + # is used in the default :path to ease default specifications. def url attachment, style raise InfiniteInterpolationError if attachment.options[:url].include?(":url") attachment.url(style) end + # Returns the timestamp as defined by the _updated_at field def timestamp attachment, style attachment.instance_read(:updated_at).to_s end + # Returns the RAILS_ROOT constant. def rails_root attachment, style RAILS_ROOT end + # Returns the RAILS_ENV constant. def rails_env attachment, style RAILS_ENV end + # Returns the underscored, pluralized version of the class name. + # e.g. "users" for the User class. def class attachment, style attachment.instance.class.to_s.underscore.pluralize end + # Returns the basename of the file. e.g. "file" for "file.jpg" def basename attachment, style attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "") end + # Returns the extension of the file. e.g. "jpg" for "file.jpg" + # If the style has a format defined, it will return the format instead + # of the actual extension. def extension attachment, style ((style = attachment.styles[style]) && style[:format]) || File.extname(attachment.original_filename).gsub(/^\.+/, "") end + # Returns the id of the instance. def id attachment, style attachment.instance.id end + # Returns the id of the instance in a split path form. e.g. returns + # 000/001/234 for an id of 1234. def id_partition attachment, style ("%09d" % attachment.instance.id).scan(/\d{3}/).join("/") end + # Returns the pluralized form of the attachment name. e.g. + # "avatars" for an attachment of :avatar def attachment attachment, style attachment.name.to_s.downcase.pluralize end + # Returns the style, or the default style if nil is supplied. def style attachment, style style || attachment.default_style end diff --git a/paperclip.gemspec b/paperclip.gemspec index 12ce4c2b4..a42f4aece 100644 --- a/paperclip.gemspec +++ b/paperclip.gemspec @@ -2,14 +2,14 @@ Gem::Specification.new do |s| s.name = %q{paperclip} - s.version = "2.2.8" + s.version = "2.2.9" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Jon Yurek"] - s.date = %q{2009-04-02} + s.date = %q{2009-05-10} s.email = %q{jyurek@thoughtbot.com} s.extra_rdoc_files = ["README.rdoc"] - s.files = ["README.rdoc", "LICENSE", "Rakefile", "init.rb", "generators/paperclip", "generators/paperclip/paperclip_generator.rb", "generators/paperclip/templates", "generators/paperclip/templates/paperclip_migration.rb.erb", "generators/paperclip/USAGE", "lib/paperclip", "lib/paperclip/attachment.rb", "lib/paperclip/callback_compatability.rb", "lib/paperclip/geometry.rb", "lib/paperclip/iostream.rb", "lib/paperclip/matchers", "lib/paperclip/matchers/have_attached_file_matcher.rb", "lib/paperclip/matchers/validate_attachment_content_type_matcher.rb", "lib/paperclip/matchers/validate_attachment_presence_matcher.rb", "lib/paperclip/matchers/validate_attachment_size_matcher.rb", "lib/paperclip/matchers.rb", "lib/paperclip/processor.rb", "lib/paperclip/storage.rb", "lib/paperclip/thumbnail.rb", "lib/paperclip/upfile.rb", "lib/paperclip.rb", "tasks/paperclip_tasks.rake", "test/attachment_test.rb", "test/database.yml", "test/debug.log", "test/fixtures", "test/fixtures/12k.png", "test/fixtures/50x50.png", "test/fixtures/5k.png", "test/fixtures/bad.png", "test/fixtures/s3.yml", "test/fixtures/text.txt", "test/fixtures/twopage.pdf", "test/geometry_test.rb", "test/helper.rb", "test/integration_test.rb", "test/iostream_test.rb", "test/matchers", "test/matchers/have_attached_file_matcher_test.rb", "test/matchers/validate_attachment_content_type_matcher_test.rb", "test/matchers/validate_attachment_presence_matcher_test.rb", "test/matchers/validate_attachment_size_matcher_test.rb", "test/paperclip_test.rb", "test/processor_test.rb", "test/s3.yml", "test/storage_test.rb", "test/thumbnail_test.rb", "test/tmp", "test/tmp/storage.txt", "shoulda_macros/paperclip.rb"] + s.files = ["README.rdoc", "LICENSE", "Rakefile", "init.rb", "generators/paperclip", "generators/paperclip/paperclip_generator.rb", "generators/paperclip/templates", "generators/paperclip/templates/paperclip_migration.rb.erb", "generators/paperclip/USAGE", "lib/paperclip", "lib/paperclip/attachment.rb", "lib/paperclip/callback_compatability.rb", "lib/paperclip/geometry.rb", "lib/paperclip/interpolations.rb", "lib/paperclip/iostream.rb", "lib/paperclip/matchers", "lib/paperclip/matchers/have_attached_file_matcher.rb", "lib/paperclip/matchers/validate_attachment_content_type_matcher.rb", "lib/paperclip/matchers/validate_attachment_presence_matcher.rb", "lib/paperclip/matchers/validate_attachment_size_matcher.rb", "lib/paperclip/matchers.rb", "lib/paperclip/processor.rb", "lib/paperclip/storage.rb", "lib/paperclip/thumbnail.rb", "lib/paperclip/upfile.rb", "lib/paperclip.rb", "tasks/paperclip_tasks.rake", "test/attachment_test.rb", "test/database.yml", "test/debug.log", "test/fixtures", "test/fixtures/12k.png", "test/fixtures/50x50.png", "test/fixtures/5k.png", "test/fixtures/bad.png", "test/fixtures/s3.yml", "test/fixtures/text.txt", "test/fixtures/twopage.pdf", "test/geometry_test.rb", "test/helper.rb", "test/integration_test.rb", "test/interpolations_test.rb", "test/iostream_test.rb", "test/matchers", "test/matchers/have_attached_file_matcher_test.rb", "test/matchers/validate_attachment_content_type_matcher_test.rb", "test/matchers/validate_attachment_presence_matcher_test.rb", "test/matchers/validate_attachment_size_matcher_test.rb", "test/paperclip_test.rb", "test/processor_test.rb", "test/s3.yml", "test/storage_test.rb", "test/thumbnail_test.rb", "test/tmp", "test/tmp/storage.txt", "shoulda_macros/paperclip.rb"] s.has_rdoc = true s.homepage = %q{http://www.thoughtbot.com/projects/paperclip} s.rdoc_options = ["--line-numbers", "--inline-source"] @@ -24,16 +24,13 @@ Gem::Specification.new do |s| s.specification_version = 2 if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) else - s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) end else - s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) end