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.
- data/README.rdoc +123 -10
- data/Rakefile +1 -5
- data/lib/paperclip.rb +63 -24
- data/lib/paperclip/attachment.rb +90 -48
- data/lib/paperclip/callback_compatability.rb +33 -0
- data/lib/paperclip/iostream.rb +1 -1
- data/lib/paperclip/processor.rb +47 -0
- data/lib/paperclip/storage.rb +11 -1
- data/lib/paperclip/thumbnail.rb +13 -31
- data/shoulda_macros/paperclip.rb +131 -10
- data/test/attachment_test.rb +126 -32
- data/test/helper.rb +2 -0
- data/test/integration_test.rb +64 -1
- data/test/iostream_test.rb +2 -0
- data/test/paperclip_test.rb +39 -3
- data/test/processor_test.rb +10 -0
- data/test/storage_test.rb +48 -0
- data/test/thumbnail_test.rb +12 -6
- metadata +5 -2
data/README.rdoc
CHANGED
@@ -1,10 +1,21 @@
|
|
1
1
|
=Paperclip
|
2
2
|
|
3
|
-
Paperclip is intended as an easy file attachment library for ActiveRecord. The
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
54
|
-
has a high chance of being
|
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!
|
58
|
-
It's a rare time when explicit tests aren't needed. If you have questions
|
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'
|
data/lib/paperclip.rb
CHANGED
@@ -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.
|
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
|
-
# *
|
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
|
-
|
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
|
-
# "/:
|
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
|
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
|
-
# "/:
|
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
|
123
|
-
#
|
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
|
204
|
-
# possible options are:
|
205
|
-
# * +content_type+: Allowed content types. Can be a single content type
|
206
|
-
# Each type can be a String or a Regexp. It should be
|
207
|
-
#
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
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
|
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
|
-
@
|
236
|
-
@
|
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
|
data/lib/paperclip/attachment.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
module Paperclip
|
2
|
-
# The Attachment class manages the files for a given attachment. It saves
|
3
|
-
# deletes when the model is destroyed, and processes
|
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,
|
21
|
-
# ActiveRecord object instance it's attached to, and
|
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
|
-
@
|
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
|
-
|
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
|
52
|
-
# assigns attributes, processes the file, and runs validations. It
|
53
|
-
# the previous file for deletion, to be flushed away on
|
54
|
-
# In addition to form uploads, you can also assign
|
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
|
-
|
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
|
-
|
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
|
95
|
-
# does not necessarily need to point to a file that your web server
|
96
|
-
# and can point to an action in your app, if you need fine
|
97
|
-
# This is not recommended if you don't need the
|
98
|
-
# performance reasons.
|
99
|
-
|
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
|
106
|
-
# disk. If the file is stored in S3, the path is the "key" part of the
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
197
|
-
# paperclip:refresh rake task and that's it. It will regenerate all
|
198
|
-
# forcefully, by reobtaining the original file and going through
|
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
|
-
|
259
|
-
|
260
|
-
|
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
|
-
|
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
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
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 @
|
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
|
-
|
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
|