paperclip 2.1.5 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of paperclip might be problematic. Click here for more details.

@@ -1,10 +1,21 @@
1
1
  =Paperclip
2
2
 
3
- Paperclip is intended as an easy file attachment library for ActiveRecord. The intent behind it was to keep setup as easy as possible and to treat files as much like other attributes as possible. This means they aren't saved to their final locations on disk, nor are they deleted if set to nil, until ActiveRecord::Base#save is called. It manages validations based on size and presence, if required. It can transform its assigned image into thumbnails if needed, and the prerequisites are as simple as installing ImageMagick (which, for most modern Unix-based systems, is as easy as installing the right packages). Attached files are saved to the filesystem and referenced in the browser by an easily understandable specification, which has sensible and useful defaults.
4
-
5
- See the documentation for the +has_attached_file+ method for options.
6
-
7
- ==Usage
3
+ Paperclip is intended as an easy file attachment library for ActiveRecord. The
4
+ intent behind it was to keep setup as easy as possible and to treat files as
5
+ much like other attributes as possible. This means they aren't saved to their
6
+ final locations on disk, nor are they deleted if set to nil, until
7
+ ActiveRecord::Base#save is called. It manages validations based on size and
8
+ presence, if required. It can transform its assigned image into thumbnails if
9
+ needed, and the prerequisites are as simple as installing ImageMagick (which,
10
+ for most modern Unix-based systems, is as easy as installing the right
11
+ packages). Attached files are saved to the filesystem and referenced in the
12
+ browser by an easily understandable specification, which has sensible and
13
+ useful defaults.
14
+
15
+ See the documentation for +has_attached_file+ in Paperclip::ClassMethods for
16
+ more detailed options.
17
+
18
+ ==Quick Start
8
19
 
9
20
  In your model:
10
21
 
@@ -48,12 +59,114 @@ In your show view:
48
59
  <%= image_tag @user.avatar.url(:medium) %>
49
60
  <%= image_tag @user.avatar.url(:thumb) %>
50
61
 
62
+ ==Usage
63
+
64
+ The basics of paperclip are quite simple: Declare that your model has an
65
+ attachment with the has_attached_file method, and give it a name. Paperclip
66
+ will wrap up up to four attributes (all prefixed with that attachment's name,
67
+ so you can have multiple attachments per model if you wish) and give the a
68
+ friendly front end. The attributes are <attachment>_file_name,
69
+ <attachment>_file_size, <attachment>_content_type, and <attachment>_updated_at.
70
+ Only <attachment>_file_name is required for paperclip to operate. More
71
+ information about the options to has_attached_file is available in the
72
+ documentation of Paperclip::ClassMethods.
73
+
74
+ Attachments can be validated with Paperclip's validation methods,
75
+ validates_attachment_presence, validates_attachment_content_type, and
76
+ validates_attachment_size.
77
+
78
+ ==Storage
79
+
80
+ The files that are assigned as attachments are, by default, placed in the
81
+ directory specified by the :path option to has_attached_file. By default, this
82
+ location is
83
+ ":rails_root/public/system/:attachment/:id/:style/:basename.:extension". This
84
+ location was chosen because on standard Capistrano deployments, the
85
+ public/system directory is symlinked to the app's shared directory, meaning it
86
+ will survive between deployments. For example, using that :path, you may have a
87
+ file at
88
+
89
+ /data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png
90
+
91
+ NOTE: This is a change from previous versions of Paperclip, but is overall a
92
+ safer choice for the defaul file store.
93
+
94
+ You may also choose to store your files using Amazon's S3 service. You can find
95
+ more information about S3 storage at the description for
96
+ Paperclip::Storage::S3.
97
+
98
+ Files on the local filesystem (and in the Rails app's public directory) will be
99
+ available to the internet at large. If you require access control, it's
100
+ possible to place your files in a different location. You will need to change
101
+ both the :path and :url options in order to make sure the files are unavailable
102
+ to the public. Both :path and :url allow the same set of interpolated
103
+ variables.
104
+
105
+ ==Post Processing
106
+
107
+ Paperclip supports an extendible selection of post-processors. When you define
108
+ a set of styles for an attachment, by default it is expected that those
109
+ "styles" are actually "thumbnails". However, you can do more than just
110
+ thumbnail images. By defining a subclass of Paperclip::Processor, you can
111
+ perform any processing you want on the files that are attached. Any file in
112
+ your Rails app's lib/paperclip_processor directory is automatically loaded by
113
+ paperclip, allowing you to easily define custom processors. You can specify a
114
+ processor with the :processors option to has_attached_file:
115
+
116
+ has_attached_file :scan, :styles => { :text => { :quality => :better } },
117
+ :processors => [:ocr]
118
+
119
+ This would load the hypothetical class Paperclip::Ocr, which would have the
120
+ hash "{ :quality => :better }" passed to it along with the uploaded file. For
121
+ more information about defining processors, see Paperclip::Processor.
122
+
123
+ The default processor is Paperclip::Thumbnail. For backwards compatability
124
+ reasons, you can pass a single geometry string or an array containing a
125
+ geometry and a format, which the file will be converted to, like so:
126
+
127
+ has_attached_file :avatar, :styles => { :thumb => ["32x32#", :png] }
128
+
129
+ This will convert the "thumb" style to a 32x32 square in png format, regardless
130
+ of what was uploaded. If the format is not specified, it is kept the same (i.e.
131
+ jpgs will remain jpgs).
132
+
133
+ Multiple processors can be specified, and they will be invoked in the order
134
+ they are defined in the :processors array. Each successive processor will
135
+ be given the result of the previous processor's execution. All processors will
136
+ receive the same parameters, which are what you define in the :styles hash.
137
+ For example, assuming we had this definition:
138
+
139
+ has_attached_file :scan, :styles => { :text => { :quality => :better } },
140
+ :processors => [:rotator, :ocr]
141
+
142
+ then both the :rotator processor and the :ocr processor would receive the
143
+ options "{ :quality => :better }". This parameter may not mean anything to one
144
+ or more or the processors, and they are free to ignore it.
145
+
146
+ ==Events
147
+
148
+ Before and after the Post Processing step, Paperclip calls back to the model
149
+ with a few callbacks, allowing the model to change or cancel the processing
150
+ step. The callbacks are "before_post_process" and "after_post_process" (which
151
+ are called before and after the processing of each attachment), and the
152
+ attachment-specific "before_<attachment>_post_process" and
153
+ "after_<attachment>_post_process". The callbacks are intended to be as close to
154
+ normal ActiveRecord callbacks as possible, so if you return false (specifically
155
+ -- returning nil is not the same) in a before_ filter, the post processing step
156
+ will halt. Returning false in an after_ filter will not halt anything, but you
157
+ can access the model and the attachment if necessary.
158
+
159
+ NOTE: Post processing will not even *start* if the attachment is not valid
160
+ according to the validations. Your callbacks (and processors) will only be
161
+ called with valid attachments.
162
+
51
163
  ==Contributing
52
164
 
53
- If you'd like to contribute a feature or bugfix, thanks! To make sure your fix/feature
54
- has a high chance of being added, please read the following guidelines:
165
+ If you'd like to contribute a feature or bugfix: Thanks! To make sure your
166
+ fix/feature has a high chance of being included, please read the following
167
+ guidelines:
55
168
 
56
169
  1. Ask on the mailing list, or post a ticket in Lighthouse.
57
- 2. Make sure there are tests! I will not accept any patch that is not tested.
58
- It's a rare time when explicit tests aren't needed. If you have questions about
59
- writing tests for paperclip, please ask the mailing list.
170
+ 2. Make sure there are tests! We will not accept any patch that is not tested.
171
+ It's a rare time when explicit tests aren't needed. If you have questions
172
+ about writing tests for paperclip, please ask the mailing list.
data/Rakefile CHANGED
@@ -27,7 +27,7 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
27
27
  rdoc.rdoc_dir = 'doc'
28
28
  rdoc.title = 'Paperclip'
29
29
  rdoc.options << '--line-numbers' << '--inline-source'
30
- rdoc.rdoc_files.include('README')
30
+ rdoc.rdoc_files.include('README*')
31
31
  rdoc.rdoc_files.include('lib/**/*.rb')
32
32
  end
33
33
 
@@ -70,10 +70,6 @@ spec = Gem::Specification.new do |s|
70
70
  s.add_development_dependency 'mocha'
71
71
  end
72
72
 
73
- Rake::GemPackageTask.new(spec) do |pkg|
74
- pkg.need_tar = true
75
- end
76
-
77
73
  desc "Release new version"
78
74
  task :release => [:test, :sync_docs, :gem] do
79
75
  require 'rubygems'
@@ -29,35 +29,57 @@ require 'tempfile'
29
29
  require 'paperclip/upfile'
30
30
  require 'paperclip/iostream'
31
31
  require 'paperclip/geometry'
32
+ require 'paperclip/processor'
32
33
  require 'paperclip/thumbnail'
33
34
  require 'paperclip/storage'
34
35
  require 'paperclip/attachment'
36
+ if defined? RAILS_ROOT
37
+ Dir.glob(File.join(File.expand_path(RAILS_ROOT), "lib", "paperclip_processors", "*.rb")).each do |processor|
38
+ require processor
39
+ end
40
+ end
35
41
 
36
42
  # The base module that gets included in ActiveRecord::Base. See the
37
43
  # documentation for Paperclip::ClassMethods for more useful information.
38
44
  module Paperclip
39
45
 
40
- VERSION = "2.1.5"
46
+ VERSION = "2.2.0"
41
47
 
42
48
  class << self
43
49
  # Provides configurability to Paperclip. There are a number of options available, such as:
44
50
  # * whiny_thumbnails: Will raise an error if Paperclip cannot process thumbnails of
45
51
  # an uploaded image. Defaults to true.
46
- # * image_magick_path: Defines the path at which to find the +convert+ and +identify+
52
+ # * command_path: Defines the path at which to find the command line
47
53
  # programs if they are not visible to Rails the system's search path. Defaults to
48
- # nil, which uses the first executable found in the search path.
54
+ # nil, which uses the first executable found in the user's search path.
55
+ # * image_magick_path: Deprecated alias of command_path.
49
56
  def options
50
57
  @options ||= {
51
58
  :whiny_thumbnails => true,
52
- :image_magick_path => nil
59
+ :image_magick_path => nil,
60
+ :command_path => nil
53
61
  }
54
62
  end
55
63
 
56
64
  def path_for_command command #:nodoc:
57
- path = [options[:image_magick_path], command].compact
65
+ if options[:image_magick_path]
66
+ ActiveSupport::Deprecation.warn(":image_magick_path is deprecated and "+
67
+ "will be removed. Use :command_path "+
68
+ "instead")
69
+ end
70
+ path = [options[:image_magick_path] || options[:command_path], command].compact
58
71
  File.join(*path)
59
72
  end
60
73
 
74
+ # The run method takes a command to execute and a string of parameters
75
+ # that get passed to it. The command is prefixed with the :command_path
76
+ # option from Paperclip.options. If you have many commands to run and
77
+ # they are in different paths, the suggested course of action is to
78
+ # symlink them so they are all in the same directory.
79
+ #
80
+ # If the command returns with a result code that is not one of the
81
+ # expected_outcodes, a PaperclipCommandLineError will be raised. Generally
82
+ # a code of 0 is expected, but a list of codes may be passed if necessary.
61
83
  def run cmd, params = "", expected_outcodes = 0
62
84
  output = `#{%Q[#{path_for_command(cmd)} #{params} 2>#{bit_bucket}].gsub(/\s+/, " ")}`
63
85
  unless [expected_outcodes].flatten.include?($?.exitstatus)
@@ -66,12 +88,24 @@ module Paperclip
66
88
  output
67
89
  end
68
90
 
69
- def bit_bucket
91
+ def bit_bucket #:nodoc:
70
92
  File.exists?("/dev/null") ? "/dev/null" : "NUL"
71
93
  end
72
94
 
73
95
  def included base #:nodoc:
74
96
  base.extend ClassMethods
97
+ unless base.respond_to?(:define_callbacks)
98
+ base.send(:include, Paperclip::CallbackCompatability)
99
+ end
100
+ end
101
+
102
+ def processor name #:nodoc:
103
+ name = name.to_s.camelize
104
+ processor = Paperclip.const_get(name)
105
+ unless processor.ancestors.include?(Paperclip::Processor)
106
+ raise PaperclipError.new("Processor #{name} was not found")
107
+ end
108
+ processor
75
109
  end
76
110
  end
77
111
 
@@ -97,15 +131,15 @@ module Paperclip
97
131
  # * +url+: The full URL of where the attachment is publically accessible. This can just
98
132
  # as easily point to a directory served directly through Apache as it can to an action
99
133
  # that can control permissions. You can specify the full domain and path, but usually
100
- # just an absolute path is sufficient. The leading slash must be included manually for
134
+ # just an absolute path is sufficient. The leading slash *must* be included manually for
101
135
  # absolute paths. The default value is
102
- # "/:class/:attachment/:id/:style_:basename.:extension". See
136
+ # "/system/:attachment/:id/:style/:basename.:extension". See
103
137
  # Paperclip::Attachment#interpolate for more information on variable interpolaton.
104
- # :url => "/:attachment/:id/:style_:basename:extension"
138
+ # :url => "/:class/:attachment/:id/:style_:basename.:extension"
105
139
  # :url => "http://some.other.host/stuff/:class/:id_:extension"
106
140
  # * +default_url+: The URL that will be returned if there is no attachment assigned.
107
141
  # This field is interpolated just as the url is. The default value is
108
- # "/:class/:attachment/missing_:style.png"
142
+ # "/:attachment/:style/missing.png"
109
143
  # has_attached_file :avatar, :default_url => "/images/default_:style_avatar.png"
110
144
  # User.new.avatar_url(:small) # => "/images/default_small_avatar.png"
111
145
  # * +styles+: A hash of thumbnail styles and their geometries. You can find more about
@@ -119,8 +153,8 @@ module Paperclip
119
153
  # has_attached_file :avatar, :styles => { :normal => "100x100#" },
120
154
  # :default_style => :normal
121
155
  # user.avatar.url # => "/avatars/23/normal_me.png"
122
- # * +whiny_thumbnails+: Will raise an error if Paperclip cannot process thumbnails of an
123
- # uploaded image. This will ovrride the global setting for this attachment.
156
+ # * +whiny_thumbnails+: Will raise an error if Paperclip cannot post_process an uploaded file due
157
+ # to a command line error. This will override the global setting for this attachment.
124
158
  # Defaults to true.
125
159
  # * +convert_options+: When creating thumbnails, use this free-form options
126
160
  # field to pass in various convert command options. Typical options are "-strip" to
@@ -150,6 +184,9 @@ module Paperclip
150
184
  after_save :save_attached_files
151
185
  before_destroy :destroy_attached_files
152
186
 
187
+ define_callbacks :before_post_process, :after_post_process
188
+ define_callbacks :"before_#{name}_post_process", :"after_#{name}_post_process"
189
+
153
190
  define_method name do |*args|
154
191
  a = attachment_for(name)
155
192
  (args.length > 0) ? a.to_s(args.first) : a
@@ -200,14 +237,16 @@ module Paperclip
200
237
  end
201
238
  end
202
239
 
203
- # Places ActiveRecord-style validations on the content type of the file assigned. The
204
- # possible options are:
205
- # * +content_type+: Allowed content types. Can be a single content type or an array.
206
- # Each type can be a String or a Regexp. It should be noted that Internet Explorer uploads
207
- # files with content_types that you may not expect. For example, JPEG images are given
208
- # image/pjpeg and PNGs are image/x-png, so keep that in mind when determining how you match.
209
- # Allows all by default.
210
- # * +message+: The message to display when the uploaded file has an invalid content type.
240
+ # Places ActiveRecord-style validations on the content type of the file
241
+ # assigned. The possible options are:
242
+ # * +content_type+: Allowed content types. Can be a single content type
243
+ # or an array. Each type can be a String or a Regexp. It should be
244
+ # noted that Internet Explorer upload files with content_types that you
245
+ # may not expect. For example, JPEG images are given image/pjpeg and
246
+ # PNGs are image/x-png, so keep that in mind when determining how you
247
+ # match. Allows all by default.
248
+ # * +message+: The message to display when the uploaded file has an invalid
249
+ # content type.
211
250
  def validates_attachment_content_type name, options = {}
212
251
  attachment_definitions[name][:validations][:content_type] = lambda do |attachment, instance|
213
252
  valid_types = [options[:content_type]].flatten
@@ -223,17 +262,17 @@ module Paperclip
223
262
  end
224
263
  end
225
264
 
226
- # Returns the attachment definitions defined by each call to has_attached_file.
265
+ # Returns the attachment definitions defined by each call to
266
+ # has_attached_file.
227
267
  def attachment_definitions
228
268
  read_inheritable_attribute(:attachment_definitions)
229
269
  end
230
-
231
270
  end
232
271
 
233
272
  module InstanceMethods #:nodoc:
234
273
  def attachment_for name
235
- @attachments ||= {}
236
- @attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
274
+ @_paperclip_attachments ||= {}
275
+ @_paperclip_attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
237
276
  end
238
277
 
239
278
  def each_attachment
@@ -1,12 +1,13 @@
1
1
  module Paperclip
2
- # The Attachment class manages the files for a given attachment. It saves when the model saves,
3
- # deletes when the model is destroyed, and processes the file upon assignment.
2
+ # The Attachment class manages the files for a given attachment. It saves
3
+ # when the model saves, deletes when the model is destroyed, and processes
4
+ # the file upon assignment.
4
5
  class Attachment
5
6
 
6
7
  def self.default_options
7
8
  @default_options ||= {
8
- :url => "/:attachment/:id/:style/:basename.:extension",
9
- :path => ":rails_root/public/:attachment/:id/:style/:basename.:extension",
9
+ :url => "/system/:attachment/:id/:style/:basename.:extension",
10
+ :path => ":rails_root/public/system/:attachment/:id/:style/:basename.:extension",
10
11
  :styles => {},
11
12
  :default_url => "/:attachment/:style/missing.png",
12
13
  :default_style => :original,
@@ -15,11 +16,11 @@ module Paperclip
15
16
  }
16
17
  end
17
18
 
18
- attr_reader :name, :instance, :styles, :default_style, :convert_options
19
+ attr_reader :name, :instance, :styles, :default_style, :convert_options, :queued_for_write
19
20
 
20
- # Creates an Attachment object. +name+ is the name of the attachment, +instance+ is the
21
- # ActiveRecord object instance it's attached to, and +options+ is the same as the hash
22
- # passed to +has_attached_file+.
21
+ # Creates an Attachment object. +name+ is the name of the attachment,
22
+ # +instance+ is the ActiveRecord object instance it's attached to, and
23
+ # +options+ is the same as the hash passed to +has_attached_file+.
23
24
  def initialize name, instance, options = {}
24
25
  @name = name
25
26
  @instance = instance
@@ -33,8 +34,9 @@ module Paperclip
33
34
  @validations = options[:validations]
34
35
  @default_style = options[:default_style]
35
36
  @storage = options[:storage]
36
- @whiny_thumbnails = options[:whiny_thumbnails]
37
+ @whiny = options[:whiny_thumbnails]
37
38
  @convert_options = options[:convert_options] || {}
39
+ @processors = options[:processors] || [:thumbnail]
38
40
  @options = options
39
41
  @queued_for_delete = []
40
42
  @queued_for_write = {}
@@ -45,14 +47,17 @@ module Paperclip
45
47
  normalize_style_definition
46
48
  initialize_storage
47
49
 
48
- logger.info("[paperclip] Paperclip attachment #{name} on #{instance.class} initialized.")
50
+ log("Paperclip attachment #{name} on #{instance.class} initialized.")
49
51
  end
50
52
 
51
- # What gets called when you call instance.attachment = File. It clears errors,
52
- # assigns attributes, processes the file, and runs validations. It also queues up
53
- # the previous file for deletion, to be flushed away on #save of its host.
54
- # In addition to form uploads, you can also assign another Paperclip attachment:
53
+ # What gets called when you call instance.attachment = File. It clears
54
+ # errors, assigns attributes, processes the file, and runs validations. It
55
+ # also queues up the previous file for deletion, to be flushed away on
56
+ # #save of its host. In addition to form uploads, you can also assign
57
+ # another Paperclip attachment:
55
58
  # new_user.avatar = old_user.avatar
59
+ # If the file that is assigned is not valid, the processing (i.e.
60
+ # thumbnailing, etc) will NOT be run.
56
61
  def assign uploaded_file
57
62
  %w(file_name).each do |field|
58
63
  unless @instance.class.column_names.include?("#{name}_#{field}")
@@ -65,7 +70,7 @@ module Paperclip
65
70
  end
66
71
 
67
72
  return nil unless valid_assignment?(uploaded_file)
68
- logger.info("[paperclip] Assigning #{uploaded_file.inspect} to #{name}")
73
+ log("Assigning #{uploaded_file.inspect} to #{name}")
69
74
 
70
75
  uploaded_file.binmode if uploaded_file.respond_to? :binmode
71
76
  queue_existing_for_delete
@@ -74,7 +79,7 @@ module Paperclip
74
79
 
75
80
  return nil if uploaded_file.nil?
76
81
 
77
- logger.info("[paperclip] Writing attributes for #{name}")
82
+ log("Writing attributes for #{name}")
78
83
  @queued_for_write[:original] = uploaded_file.to_tempfile
79
84
  instance_write(:file_name, uploaded_file.original_filename.strip.gsub(/[^\w\d\.\-]+/, '_'))
80
85
  instance_write(:content_type, uploaded_file.content_type.to_s.strip)
@@ -83,7 +88,7 @@ module Paperclip
83
88
 
84
89
  @dirty = true
85
90
 
86
- post_process
91
+ post_process if valid?
87
92
 
88
93
  # Reset the file size if the original file was reprocessed.
89
94
  instance_write(:file_size, uploaded_file.size.to_i)
@@ -91,20 +96,22 @@ module Paperclip
91
96
  validate
92
97
  end
93
98
 
94
- # Returns the public URL of the attachment, with a given style. Note that this
95
- # does not necessarily need to point to a file that your web server can access
96
- # and can point to an action in your app, if you need fine grained security.
97
- # This is not recommended if you don't need the security, however, for
98
- # performance reasons.
99
- def url style = default_style
99
+ # Returns the public URL of the attachment, with a given style. Note that
100
+ # this does not necessarily need to point to a file that your web server
101
+ # can access and can point to an action in your app, if you need fine
102
+ # grained security. This is not recommended if you don't need the
103
+ # security, however, for performance reasons. set
104
+ # include_updated_timestamp to false if you want to stop the attachment
105
+ # update time appended to the url
106
+ def url style = default_style, include_updated_timestamp = true
100
107
  url = original_filename.nil? ? interpolate(@default_url, style) : interpolate(@url, style)
101
- updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
108
+ include_updated_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
102
109
  end
103
110
 
104
111
  # Returns the path of the attachment as defined by the :path option. If the
105
- # file is stored in the filesystem the path refers to the path of the file on
106
- # disk. If the file is stored in S3, the path is the "key" part of the URL,
107
- # and the :bucket option refers to the S3 bucket.
112
+ # file is stored in the filesystem the path refers to the path of the file
113
+ # on disk. If the file is stored in S3, the path is the "key" part of the
114
+ # URL, and the :bucket option refers to the S3 bucket.
108
115
  def path style = nil #:nodoc:
109
116
  original_filename.nil? ? nil : interpolate(@path, style)
110
117
  end
@@ -134,32 +141,38 @@ module Paperclip
134
141
  # the instance's errors and returns false, cancelling the save.
135
142
  def save
136
143
  if valid?
137
- logger.info("[paperclip] Saving files for #{name}")
144
+ log("Saving files for #{name}")
138
145
  flush_deletes
139
146
  flush_writes
140
147
  @dirty = false
141
148
  true
142
149
  else
143
- logger.info("[paperclip] Errors on #{name}. Not saving.")
150
+ log("Errors on #{name}. Not saving.")
144
151
  flush_errors
145
152
  false
146
153
  end
147
154
  end
148
155
 
149
- # Returns the name of the file as originally assigned, and as lives in the
156
+ # Returns the name of the file as originally assigned, and lives in the
150
157
  # <attachment>_file_name attribute of the model.
151
158
  def original_filename
152
159
  instance_read(:file_name)
153
160
  end
154
161
 
162
+ # Returns the size of the file as originally assigned, and lives in the
163
+ # <attachment>_file_size attribute of the model.
155
164
  def size
156
165
  instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
157
166
  end
158
167
 
168
+ # Returns the content_type of the file as originally assigned, and lives
169
+ # in the <attachment>_content_type attribute of the model.
159
170
  def content_type
160
171
  instance_read(:content_type)
161
172
  end
162
173
 
174
+ # Returns the last modified time of the file as originally assigned, and
175
+ # lives in the <attachment>_updated_at attribute of the model.
163
176
  def updated_at
164
177
  time = instance_read(:updated_at)
165
178
  time && time.to_i
@@ -181,7 +194,7 @@ module Paperclip
181
194
  attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "")
182
195
  end,
183
196
  :extension => lambda do |attachment,style|
184
- ((style = attachment.styles[style]) && style.last) ||
197
+ ((style = attachment.styles[style]) && style[:format]) ||
185
198
  File.extname(attachment.original_filename).gsub(/^\.+/, "")
186
199
  end,
187
200
  :id => lambda{|attachment,style| attachment.instance.id },
@@ -193,10 +206,10 @@ module Paperclip
193
206
  }
194
207
  end
195
208
 
196
- # This method really shouldn't be called that often. It's expected use is in the
197
- # paperclip:refresh rake task and that's it. It will regenerate all thumbnails
198
- # forcefully, by reobtaining the original file and going through the post-process
199
- # again.
209
+ # This method really shouldn't be called that often. It's expected use is
210
+ # in the paperclip:refresh rake task and that's it. It will regenerate all
211
+ # thumbnails forcefully, by reobtaining the original file and going through
212
+ # the post-process again.
200
213
  def reprocess!
201
214
  new_original = Tempfile.new("paperclip-reprocess")
202
215
  if old_original = to_file(:original)
@@ -214,16 +227,22 @@ module Paperclip
214
227
  end
215
228
  end
216
229
 
230
+ # Returns true if a file has been assigned.
217
231
  def file?
218
232
  !original_filename.blank?
219
233
  end
220
234
 
235
+ # Writes the attachment-specific attribute on the instance. For example,
236
+ # instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's
237
+ # "avatar_file_name" field (assuming the attachment is called avatar).
221
238
  def instance_write(attr, value)
222
239
  setter = :"#{name}_#{attr}="
223
240
  responds = instance.respond_to?(setter)
224
241
  instance.send(setter, value) if responds || attr.to_s == "file_name"
225
242
  end
226
243
 
244
+ # Reads the attachment-specific attribute on the instance. See instance_write
245
+ # for more details.
227
246
  def instance_read(attr)
228
247
  getter = :"#{name}_#{attr}"
229
248
  responds = instance.respond_to?(getter)
@@ -236,6 +255,10 @@ module Paperclip
236
255
  instance.logger
237
256
  end
238
257
 
258
+ def log message
259
+ logger.info("[paperclip] #{message}")
260
+ end
261
+
239
262
  def valid_assignment? file #:nodoc:
240
263
  file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
241
264
  end
@@ -255,9 +278,23 @@ module Paperclip
255
278
 
256
279
  def normalize_style_definition
257
280
  @styles.each do |name, args|
258
- dimensions, format = [args, nil].flatten[0..1]
259
- format = nil if format == ""
260
- @styles[name] = [dimensions, format]
281
+ unless args.is_a? Hash
282
+ dimensions, format = [args, nil].flatten[0..1]
283
+ format = nil if format.blank?
284
+ @styles[name] = {
285
+ :processors => @processors,
286
+ :geometry => dimensions,
287
+ :format => format,
288
+ :whiny => @whiny,
289
+ :convert_options => extra_options_for(name)
290
+ }
291
+ else
292
+ @styles[name] = {
293
+ :processors => @processors,
294
+ :whiny => @whiny,
295
+ :convert_options => extra_options_for(name)
296
+ }.merge(@styles[name])
297
+ end
261
298
  end
262
299
  end
263
300
 
@@ -272,20 +309,25 @@ module Paperclip
272
309
 
273
310
  def post_process #:nodoc:
274
311
  return if @queued_for_write[:original].nil?
275
- logger.info("[paperclip] Post-processing #{name}")
312
+ return if callback(:before_post_process) == false
313
+ return if callback(:"before_#{name}_post_process") == false
314
+ log("Post-processing #{name}")
276
315
  @styles.each do |name, args|
277
316
  begin
278
- dimensions, format = args
279
- dimensions = dimensions.call(instance) if dimensions.respond_to? :call
280
- @queued_for_write[name] = Thumbnail.make(@queued_for_write[:original],
281
- dimensions,
282
- format,
283
- extra_options_for(name),
284
- @whiny_thumbnails)
317
+ @queued_for_write[name] = @queued_for_write[:original]
318
+ args[:processors].each do |processor|
319
+ @queued_for_write[name] = Paperclip.processor(processor).make(@queued_for_write[name], args)
320
+ end
285
321
  rescue PaperclipError => e
286
- (@errors[:processing] ||= []) << e.message if @whiny_thumbnails
322
+ (@errors[:processing] ||= []) << e.message if @whiny
287
323
  end
288
324
  end
325
+ callback(:"after_#{name}_post_process")
326
+ callback(:after_post_process)
327
+ end
328
+
329
+ def callback which
330
+ instance.run_callbacks(which, @queued_for_write){|result, obj| result == false }
289
331
  end
290
332
 
291
333
  def interpolate pattern, style = default_style #:nodoc:
@@ -300,7 +342,7 @@ module Paperclip
300
342
 
301
343
  def queue_existing_for_delete #:nodoc:
302
344
  return unless file?
303
- logger.info("[paperclip] Queueing the existing files for #{name} for deletion.")
345
+ log("Queueing the existing files for #{name} for deletion.")
304
346
  @queued_for_delete += [:original, *@styles.keys].uniq.map do |style|
305
347
  path(style) if exists?(style)
306
348
  end.compact