Commit 90c80bb9 by Alex Pounds

Support for readbyte in Paperclip attachments; better documentation of custom processors.

I recently implemented a custom processor that used the Exifr gem to
extract EXIF information from images uploaded to a Paperclip attachment.
Exifr's processor uses readbyte to parse the EXIF header, so it hit
errors when Paperclip's File-like object didn't have one. There's also a
test for this delegation.

I've also tidied up the README documentation for custom processors,
hopefully to be more clear than before. There was some duplicated
content between the "Post Processing" section and the "Custom Attachment
Processing" section, and those sections were separated in the file. I've
dedicated the "Post Processing" section to Paperclip's built in
thumbnailing processors, and made "Custom Attachment Processing" section
solely about writing & using your own custom processors. This should be
a more understandable progression, as built-in functionality is
discussed first & separately from extending Paperclip.k
parent e367efc2
...@@ -43,11 +43,11 @@ https://github.com/thoughtbot/paperclip/releases ...@@ -43,11 +43,11 @@ https://github.com/thoughtbot/paperclip/releases
- [Storage](#storage) - [Storage](#storage)
- [Understanding Storage](#understanding-storage) - [Understanding Storage](#understanding-storage)
- [Post Processing](#post-processing) - [Post Processing](#post-processing)
- [Custom Attachment Processors](#custom-attachment-processors)
- [Events](#events) - [Events](#events)
- [URI Obfuscation](#uri-obfuscation) - [URI Obfuscation](#uri-obfuscation)
- [MD5 Checksum / Fingerprint](#md5-checksum--fingerprint) - [MD5 Checksum / Fingerprint](#md5-checksum--fingerprint)
- [File Preservation for Soft-Delete](#file-preservation-for-soft-delete) - [File Preservation for Soft-Delete](#file-preservation-for-soft-delete)
- [Custom Attachment Processors](#custom-attachment-processors)
- [Dynamic Configuration](#dynamic-configuration) - [Dynamic Configuration](#dynamic-configuration)
- [Dynamic Styles:](#dynamic-styles) - [Dynamic Styles:](#dynamic-styles)
- [Dynamic Processors:](#dynamic-processors) - [Dynamic Processors:](#dynamic-processors)
...@@ -602,60 +602,72 @@ Post Processing ...@@ -602,60 +602,72 @@ Post Processing
Paperclip supports an extensible 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 a set of styles for an attachment, by default it is expected that those
"styles" are actually "thumbnails." However, you can do much more than just "styles" are actually "thumbnails." These are processed by
thumbnail images. By defining a subclass of Paperclip::Processor, you can `Paperclip::Thumbnail`. For backward compatibility reasons you can pass either
perform any processing you want on the files that are attached. Any file in a single geometry string, or an array containing a geometry and a format that
your Rails app's `lib/paperclip` and `lib/paperclip_processors` directories is the file will be converted to, like so:
automatically loaded by Paperclip, allowing you to easily define custom
processors. You can specify a processor with the `:processors` option to
`has_attached_file`:
```ruby ```ruby
has_attached_file :scan, styles: { text: { quality: :better } }, has_attached_file :avatar, styles: { thumb: ["32x32#", :png] }
processors: [:ocr]
``` ```
This would load the hypothetical class Paperclip::Ocr, which would have the This will convert the "thumb" style to a 32x32 square in PNG format, regardless
hash "{ quality: :better }" passed to it along with the uploaded file. For of what was uploaded. If the format is not specified, it is kept the same (e.g.
more information about defining processors, see JPGs will remain JPGs). `Paperclip::Thumbnail` uses ImageMagick to process
[Paperclip::Processor](https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/processor.rb). images; [ImageMagick's geometry documentation](http://www.imagemagick.org/script/command-line-processing.php#geometry)
has more information on the accepted style formats.
---
Custom Attachment Processors
-------
The default processor is Paperclip::Thumbnail. For backward compatibility You can write your own custom attachment processors to carry out tasks like
reasons, you can pass a single geometry string or an array containing a adding watermarks, compressing images, or encrypting files. Custom processors
geometry and a format that the file will be converted to, like so: must be defined within the `Paperclip` module, inherit from
`Paperclip::Processor` (see [`lib/paperclip/processor.rb`](https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/processor.rb)),
and implement a `make` method that returns a `File`. All files in your Rails
app's `lib/paperclip` and `lib/paperclip_processors` directories will be
automatically loaded by Paperclip. Processors are specified using the
`:processors` option to `has_attached_file`:
```ruby ```ruby
has_attached_file :avatar, styles: { thumb: ["32x32#", :png] } has_attached_file :scan, styles: { text: { quality: :better } },
processors: [:ocr]
``` ```
This will convert the "thumb" style to a 32x32 square in PNG format, regardless This would load the hypothetical class `Paperclip::Ocr`, and pass it the
of what was uploaded. If the format is not specified, it is kept the same (i.e. options hash `{ quality: :better }`, along with the uploaded file.
JPGs will remain JPGs). For more information on the accepted style formats, see
[here](http://www.imagemagick.org/script/command-line-processing.php#geometry).
Multiple processors can be specified, and they will be invoked in the order Multiple processors can be specified, and they will be invoked in the order
they are defined in the `:processors` array. Each successive processor will they are defined in the `:processors` array. Each successive processor is given
be given the result of the previous processor's execution. All processors will the result from the previous processor. All processors receive the same
receive the same parameters, which are defined in the `:styles` hash. parameters, which are defined in the `:styles` hash. For example, assuming we
For example, assuming we had this definition: had this definition:
```ruby ```ruby
has_attached_file :scan, styles: { text: { quality: :better } }, has_attached_file :scan, styles: { text: { quality: :better } },
processors: [:rotator, :ocr] processors: [:rotator, :ocr]
``` ```
then both the :rotator processor and the :ocr processor would receive the Both the `:rotator` processor and the `:ocr` processor would receive the
options `{ quality: :better }`. This parameter may not mean anything to one options `{ quality: :better }`. If a processor receives an option it doesn't
or more or the processors, and they are expected to ignore it. recognise, it's expected to ignore it.
_NOTE: Because processors operate by turning the original attachment into the _NOTE: Because processors operate by turning the original attachment into the
styles, no processors will be run if there are no styles defined._ styles, no processors will be run if there are no styles defined._
If you're interested in caching your thumbnail's width, height and size in the If you're interested in caching your thumbnail's width, height and size in the
database, take a look at the [paperclip-meta](https://github.com/teeparham/paperclip-meta) gem. database, take a look at the [paperclip-meta](https://github.com/teeparham/paperclip-meta)
gem.
Also, if you're interested in generating the thumbnail on-the-fly, you might want Also, if you're interested in generating the thumbnail on-the-fly, you might want
to look into the [attachment_on_the_fly](https://github.com/drpentode/Attachment-on-the-Fly) gem. to look into the [attachment_on_the_fly](https://github.com/drpentode/Attachment-on-the-Fly)
gem.
Paperclip's thumbnail generator (see [`lib/paperclip/thumbnail.rb`](lib/paperclip/thumbnail.rb))
is implemented as a processor, and may be a good reference for writing your own
processors.
--- ---
...@@ -748,25 +760,6 @@ This will prevent ```some_attachment``` from being wiped out when the model gets ...@@ -748,25 +760,6 @@ This will prevent ```some_attachment``` from being wiped out when the model gets
--- ---
Custom Attachment Processors
-------
Custom attachment processors can be implemented and their only requirement is
to inherit from `Paperclip::Processor` (see `lib/paperclip/processor.rb`).
For example, when `:styles` are specified for an image attachment, the
thumbnail processor (see `lib/paperclip/thumbnail.rb`) is loaded without having
to specify it as a `:processor` parameter to `has_attached_file`. When any
other processor is defined, it must be called out in the `:processors`
parameter if it is to be applied to the attachment. The thumbnail processor
uses the ImageMagick `convert` command to do the work of resizing image
thumbnails. It would be easy to create a custom processor that watermarks
an image using ImageMagick's `composite` command. Following the
implementation pattern of the thumbnail processor would be a way to implement a
watermark processor. All kinds of attachment processors can be created;
a few utility examples would be compression and encryption processors.
---
Dynamic Configuration Dynamic Configuration
--------------------- ---------------------
......
...@@ -5,7 +5,7 @@ module Paperclip ...@@ -5,7 +5,7 @@ module Paperclip
OS_RESTRICTED_CHARACTERS = %r{[/:]} OS_RESTRICTED_CHARACTERS = %r{[/:]}
attr_reader :content_type, :original_filename, :size attr_reader :content_type, :original_filename, :size
delegate :binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :rewind, :unlink, :to => :@tempfile delegate :binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :readbyte, :rewind, :unlink, :to => :@tempfile
alias :length :size alias :length :size
def fingerprint def fingerprint
......
...@@ -7,13 +7,14 @@ module Paperclip ...@@ -7,13 +7,14 @@ module Paperclip
# Processors are required to be defined inside the Paperclip module and # Processors are required to be defined inside the Paperclip module and
# are also required to be a subclass of Paperclip::Processor. There is # are also required to be a subclass of Paperclip::Processor. There is
# only one method you *must* implement to properly be a subclass: # only one method you *must* implement to properly be a subclass:
# #make, but #initialize may also be of use. Both methods accept 3 # #make, but #initialize may also be of use. #initialize accepts 3
# arguments: the file that will be operated on (which is an instance of # arguments: the file that will be operated on (which is an instance of
# File), a hash of options that were defined in has_attached_file's # File), a hash of options that were defined in has_attached_file's
# style hash, and the Paperclip::Attachment itself. # style hash, and the Paperclip::Attachment itself. These are set as
# instance variables that can be used within `#make`.
# #
# All #make needs to return is an instance of File (Tempfile is # #make must return an instance of File (Tempfile is acceptable) which
# acceptable) which contains the results of the processing. # contains the results of the processing.
# #
# See Paperclip.run for more information about using command-line # See Paperclip.run for more information about using command-line
# utilities from within Processors. # utilities from within Processors.
......
...@@ -34,7 +34,7 @@ describe Paperclip::AbstractAdapter do ...@@ -34,7 +34,7 @@ describe Paperclip::AbstractAdapter do
@adapter.tempfile = stub("Tempfile") @adapter.tempfile = stub("Tempfile")
end end
[:binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :rewind, :unlink].each do |method| [:binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :readbyte, :rewind, :unlink].each do |method|
it "delegates #{method} to @tempfile" do it "delegates #{method} to @tempfile" do
@adapter.tempfile.stubs(method) @adapter.tempfile.stubs(method)
@adapter.public_send(method) @adapter.public_send(method)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment