paperclip 2.1.2 → 2.1.5
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 → README.rdoc} +14 -3
- data/Rakefile +15 -5
- data/generators/paperclip/USAGE +1 -1
- data/generators/paperclip/paperclip_generator.rb +2 -2
- data/generators/paperclip/templates/{paperclip_migration.rb → paperclip_migration.rb.erb} +2 -0
- data/lib/paperclip.rb +64 -41
- data/lib/paperclip/attachment.rb +113 -35
- data/lib/paperclip/geometry.rb +17 -11
- data/lib/paperclip/iostream.rb +16 -1
- data/lib/paperclip/storage.rb +86 -11
- data/lib/paperclip/thumbnail.rb +19 -11
- data/lib/paperclip/upfile.rb +20 -5
- data/shoulda_macros/paperclip.rb +32 -0
- data/tasks/paperclip_tasks.rake +55 -14
- data/test/attachment_test.rb +455 -0
- data/test/database.yml +0 -1
- data/test/debug.log +0 -1745
- data/test/{test_geometry.rb → geometry_test.rb} +44 -18
- data/test/helper.rb +16 -0
- data/test/{test_integration.rb → integration_test.rb} +103 -25
- data/test/iostream_test.rb +68 -0
- data/test/paperclip_test.rb +186 -0
- data/test/s3.yml +0 -2
- data/test/{test_storage.rb → storage_test.rb} +41 -10
- data/test/{test_thumbnail.rb → thumbnail_test.rb} +45 -14
- metadata +50 -28
- data/test/test_attachment.rb +0 -230
- data/test/test_iostream.rb +0 -60
- data/test/test_paperclip.rb +0 -123
data/{README → README.rdoc}
RENAMED
@@ -14,17 +14,19 @@ In your model:
|
|
14
14
|
|
15
15
|
In your migrations:
|
16
16
|
|
17
|
-
class
|
17
|
+
class AddAvatarColumnsToUser < ActiveRecord::Migration
|
18
18
|
def self.up
|
19
|
-
add_column :users, :avatar_file_name,
|
19
|
+
add_column :users, :avatar_file_name, :string
|
20
20
|
add_column :users, :avatar_content_type, :string
|
21
|
-
add_column :users, :avatar_file_size,
|
21
|
+
add_column :users, :avatar_file_size, :integer
|
22
|
+
add_column :users, :avatar_updated_at, :datetime
|
22
23
|
end
|
23
24
|
|
24
25
|
def self.down
|
25
26
|
remove_column :users, :avatar_file_name
|
26
27
|
remove_column :users, :avatar_content_type
|
27
28
|
remove_column :users, :avatar_file_size
|
29
|
+
remove_column :users, :avatar_updated_at
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
@@ -46,3 +48,12 @@ In your show view:
|
|
46
48
|
<%= image_tag @user.avatar.url(:medium) %>
|
47
49
|
<%= image_tag @user.avatar.url(:thumb) %>
|
48
50
|
|
51
|
+
==Contributing
|
52
|
+
|
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:
|
55
|
+
|
56
|
+
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.
|
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ task :default => [:clean, :test]
|
|
12
12
|
desc 'Test the paperclip plugin.'
|
13
13
|
Rake::TestTask.new(:test) do |t|
|
14
14
|
t.libs << 'lib' << 'profile'
|
15
|
-
t.pattern = 'test
|
15
|
+
t.pattern = 'test/**/*_test.rb'
|
16
16
|
t.verbose = true
|
17
17
|
end
|
18
18
|
|
@@ -50,21 +50,24 @@ spec = Gem::Specification.new do |s|
|
|
50
50
|
s.version = Paperclip::VERSION
|
51
51
|
s.author = "Jon Yurek"
|
52
52
|
s.email = "jyurek@thoughtbot.com"
|
53
|
-
s.homepage = "http://www.thoughtbot.com/"
|
53
|
+
s.homepage = "http://www.thoughtbot.com/projects/paperclip"
|
54
54
|
s.platform = Gem::Platform::RUBY
|
55
55
|
s.summary = "File attachments as attributes for ActiveRecord"
|
56
|
-
s.files = FileList["README",
|
56
|
+
s.files = FileList["README*",
|
57
57
|
"LICENSE",
|
58
58
|
"Rakefile",
|
59
59
|
"init.rb",
|
60
|
-
"{generators,lib,tasks,test}/**/*"].to_a
|
60
|
+
"{generators,lib,tasks,test,shoulda_macros}/**/*"].to_a
|
61
61
|
s.require_path = "lib"
|
62
62
|
s.test_files = FileList["test/**/test_*.rb"].to_a
|
63
63
|
s.rubyforge_project = "paperclip"
|
64
64
|
s.has_rdoc = true
|
65
|
-
s.extra_rdoc_files = ["README"]
|
65
|
+
s.extra_rdoc_files = FileList["README*"].to_a
|
66
66
|
s.rdoc_options << '--line-numbers' << '--inline-source'
|
67
67
|
s.requirements << "ImageMagick"
|
68
|
+
s.add_runtime_dependency 'right_aws'
|
69
|
+
s.add_development_dependency 'thoughtbot-shoulda'
|
70
|
+
s.add_development_dependency 'mocha'
|
68
71
|
end
|
69
72
|
|
70
73
|
Rake::GemPackageTask.new(spec) do |pkg|
|
@@ -82,3 +85,10 @@ task :release => [:test, :sync_docs, :gem] do
|
|
82
85
|
spec.version,
|
83
86
|
File.join("pkg", "#{spec.name}-#{spec.version}.gem")
|
84
87
|
end
|
88
|
+
|
89
|
+
desc "Generate a gemspec file for GitHub"
|
90
|
+
task :gemspec do
|
91
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
92
|
+
f.write spec.to_ruby
|
93
|
+
end
|
94
|
+
end
|
data/generators/paperclip/USAGE
CHANGED
@@ -10,7 +10,7 @@ class PaperclipGenerator < Rails::Generator::NamedBase
|
|
10
10
|
file_name = generate_file_name
|
11
11
|
@migration_name = file_name.camelize
|
12
12
|
record do |m|
|
13
|
-
m.migration_template "paperclip_migration.rb",
|
13
|
+
m.migration_template "paperclip_migration.rb.erb",
|
14
14
|
File.join('db', 'migrate'),
|
15
15
|
:migration_file_name => file_name
|
16
16
|
end
|
@@ -24,4 +24,4 @@ class PaperclipGenerator < Rails::Generator::NamedBase
|
|
24
24
|
"add_attachments_#{names.join("_")}_to_#{@class_name.underscore}"
|
25
25
|
end
|
26
26
|
|
27
|
-
end
|
27
|
+
end
|
@@ -4,6 +4,7 @@ class <%= migration_name %> < ActiveRecord::Migration
|
|
4
4
|
add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name, :string
|
5
5
|
add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type, :string
|
6
6
|
add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_size, :integer
|
7
|
+
add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at, :datetime
|
7
8
|
<% end -%>
|
8
9
|
end
|
9
10
|
|
@@ -12,6 +13,7 @@ class <%= migration_name %> < ActiveRecord::Migration
|
|
12
13
|
remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name
|
13
14
|
remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type
|
14
15
|
remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_size
|
16
|
+
remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at
|
15
17
|
<% end -%>
|
16
18
|
end
|
17
19
|
end
|
data/lib/paperclip.rb
CHANGED
@@ -33,10 +33,11 @@ require 'paperclip/thumbnail'
|
|
33
33
|
require 'paperclip/storage'
|
34
34
|
require 'paperclip/attachment'
|
35
35
|
|
36
|
-
# The base module that gets included in ActiveRecord::Base.
|
36
|
+
# The base module that gets included in ActiveRecord::Base. See the
|
37
|
+
# documentation for Paperclip::ClassMethods for more useful information.
|
37
38
|
module Paperclip
|
38
39
|
|
39
|
-
VERSION = "2.1.
|
40
|
+
VERSION = "2.1.5"
|
40
41
|
|
41
42
|
class << self
|
42
43
|
# Provides configurability to Paperclip. There are a number of options available, such as:
|
@@ -57,6 +58,18 @@ module Paperclip
|
|
57
58
|
File.join(*path)
|
58
59
|
end
|
59
60
|
|
61
|
+
def run cmd, params = "", expected_outcodes = 0
|
62
|
+
output = `#{%Q[#{path_for_command(cmd)} #{params} 2>#{bit_bucket}].gsub(/\s+/, " ")}`
|
63
|
+
unless [expected_outcodes].flatten.include?($?.exitstatus)
|
64
|
+
raise PaperclipCommandLineError, "Error while running #{cmd}"
|
65
|
+
end
|
66
|
+
output
|
67
|
+
end
|
68
|
+
|
69
|
+
def bit_bucket
|
70
|
+
File.exists?("/dev/null") ? "/dev/null" : "NUL"
|
71
|
+
end
|
72
|
+
|
60
73
|
def included base #:nodoc:
|
61
74
|
base.extend ClassMethods
|
62
75
|
end
|
@@ -65,6 +78,9 @@ module Paperclip
|
|
65
78
|
class PaperclipError < StandardError #:nodoc:
|
66
79
|
end
|
67
80
|
|
81
|
+
class PaperclipCommandLineError < StandardError #:nodoc:
|
82
|
+
end
|
83
|
+
|
68
84
|
class NotIdentifiedByImageMagickError < PaperclipError #:nodoc:
|
69
85
|
end
|
70
86
|
|
@@ -82,7 +98,8 @@ module Paperclip
|
|
82
98
|
# as easily point to a directory served directly through Apache as it can to an action
|
83
99
|
# that can control permissions. You can specify the full domain and path, but usually
|
84
100
|
# just an absolute path is sufficient. The leading slash must be included manually for
|
85
|
-
# absolute paths. The default value is
|
101
|
+
# absolute paths. The default value is
|
102
|
+
# "/:class/:attachment/:id/:style_:basename.:extension". See
|
86
103
|
# Paperclip::Attachment#interpolate for more information on variable interpolaton.
|
87
104
|
# :url => "/:attachment/:id/:style_:basename:extension"
|
88
105
|
# :url => "http://some.other.host/stuff/:class/:id_:extension"
|
@@ -102,24 +119,33 @@ module Paperclip
|
|
102
119
|
# has_attached_file :avatar, :styles => { :normal => "100x100#" },
|
103
120
|
# :default_style => :normal
|
104
121
|
# user.avatar.url # => "/avatars/23/normal_me.png"
|
105
|
-
# * +path+: The location of the repository of attachments on disk. This can be coordinated
|
106
|
-
# with the value of the +url+ option to allow files to be saved into a place where Apache
|
107
|
-
# can serve them without hitting your app. Defaults to
|
108
|
-
# ":rails_root/public/:class/:attachment/:id/:style_:filename".
|
109
|
-
# By default this places the files in the app's public directory which can be served
|
110
|
-
# directly. If you are using capistrano for deployment, a good idea would be to
|
111
|
-
# make a symlink to the capistrano-created system directory from inside your app's
|
112
|
-
# public directory.
|
113
|
-
# See Paperclip::Attachment#interpolate for more information on variable interpolaton.
|
114
|
-
# :path => "/var/app/attachments/:class/:id/:style/:filename"
|
115
122
|
# * +whiny_thumbnails+: Will raise an error if Paperclip cannot process thumbnails of an
|
116
123
|
# uploaded image. This will ovrride the global setting for this attachment.
|
117
124
|
# Defaults to true.
|
125
|
+
# * +convert_options+: When creating thumbnails, use this free-form options
|
126
|
+
# field to pass in various convert command options. Typical options are "-strip" to
|
127
|
+
# remove all Exif data from the image (save space for thumbnails and avatars) or
|
128
|
+
# "-depth 8" to specify the bit depth of the resulting conversion. See ImageMagick
|
129
|
+
# convert documentation for more options: (http://www.imagemagick.org/script/convert.php)
|
130
|
+
# Note that this option takes a hash of options, each of which correspond to the style
|
131
|
+
# of thumbnail being generated. You can also specify :all as a key, which will apply
|
132
|
+
# to all of the thumbnails being generated. If you specify options for the :original,
|
133
|
+
# it would be best if you did not specify destructive options, as the intent of keeping
|
134
|
+
# the original around is to regenerate all the thumbnails when requirements change.
|
135
|
+
# has_attached_file :avatar, :styles => { :large => "300x300", :negative => "100x100" }
|
136
|
+
# :convert_options => {
|
137
|
+
# :all => "-strip",
|
138
|
+
# :negative => "-negate"
|
139
|
+
# }
|
140
|
+
# * +storage+: Chooses the storage backend where the files will be stored. The current
|
141
|
+
# choices are :filesystem and :s3. The default is :filesystem. Make sure you read the
|
142
|
+
# documentation for Paperclip::Storage::Filesystem and Paperclip::Storage::S3
|
143
|
+
# for backend-specific options.
|
118
144
|
def has_attached_file name, options = {}
|
119
145
|
include InstanceMethods
|
120
146
|
|
121
147
|
write_inheritable_attribute(:attachment_definitions, {}) if attachment_definitions.nil?
|
122
|
-
attachment_definitions[name] = {:validations =>
|
148
|
+
attachment_definitions[name] = {:validations => {}}.merge(options)
|
123
149
|
|
124
150
|
after_save :save_attached_files
|
125
151
|
before_destroy :destroy_attached_files
|
@@ -134,11 +160,11 @@ module Paperclip
|
|
134
160
|
end
|
135
161
|
|
136
162
|
define_method "#{name}?" do
|
137
|
-
|
163
|
+
attachment_for(name).file?
|
138
164
|
end
|
139
165
|
|
140
166
|
validates_each(name) do |record, attr, value|
|
141
|
-
value.send(:flush_errors)
|
167
|
+
value.send(:flush_errors) unless value.valid?
|
142
168
|
end
|
143
169
|
end
|
144
170
|
|
@@ -149,22 +175,14 @@ module Paperclip
|
|
149
175
|
# * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
|
150
176
|
# * +message+: error message to display, use :min and :max as replacements
|
151
177
|
def validates_attachment_size name, options = {}
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
min = options[:in].first
|
161
|
-
max = options[:in].last
|
162
|
-
|
163
|
-
if options[:message]
|
164
|
-
options[:message].gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
|
165
|
-
else
|
166
|
-
"file size is not between #{min} and #{max} bytes."
|
167
|
-
end
|
178
|
+
min = options[:greater_than] || (options[:in] && options[:in].first) || 0
|
179
|
+
max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
|
180
|
+
range = (min..max)
|
181
|
+
message = options[:message] || "file size must be between :min and :max bytes."
|
182
|
+
|
183
|
+
attachment_definitions[name][:validations][:size] = lambda do |attachment, instance|
|
184
|
+
if attachment.file? && !range.include?(attachment.size.to_i)
|
185
|
+
message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
|
168
186
|
end
|
169
187
|
end
|
170
188
|
end
|
@@ -176,26 +194,29 @@ module Paperclip
|
|
176
194
|
|
177
195
|
# Places ActiveRecord-style validations on the presence of a file.
|
178
196
|
def validates_attachment_presence name, options = {}
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
end
|
197
|
+
message = options[:message] || "must be set."
|
198
|
+
attachment_definitions[name][:validations][:presence] = lambda do |attachment, instance|
|
199
|
+
message unless attachment.file?
|
183
200
|
end
|
184
201
|
end
|
185
202
|
|
186
203
|
# Places ActiveRecord-style validations on the content type of the file assigned. The
|
187
204
|
# possible options are:
|
188
|
-
# * +content_type+: Allowed content types. Can be a single content type or an array.
|
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.
|
189
210
|
# * +message+: The message to display when the uploaded file has an invalid content type.
|
190
211
|
def validates_attachment_content_type name, options = {}
|
191
|
-
attachment_definitions[name][:validations]
|
212
|
+
attachment_definitions[name][:validations][:content_type] = lambda do |attachment, instance|
|
192
213
|
valid_types = [options[:content_type]].flatten
|
193
214
|
|
194
|
-
unless attachment.original_filename.
|
215
|
+
unless attachment.original_filename.blank?
|
195
216
|
unless options[:content_type].blank?
|
196
|
-
content_type =
|
217
|
+
content_type = attachment.instance_read(:content_type)
|
197
218
|
unless valid_types.any?{|t| t === content_type }
|
198
|
-
options[:message] ||
|
219
|
+
options[:message] || "is not one of the allowed file types."
|
199
220
|
end
|
200
221
|
end
|
201
222
|
end
|
@@ -222,12 +243,14 @@ module Paperclip
|
|
222
243
|
end
|
223
244
|
|
224
245
|
def save_attached_files
|
246
|
+
logger.info("[paperclip] Saving attachments.")
|
225
247
|
each_attachment do |name, attachment|
|
226
248
|
attachment.send(:save)
|
227
249
|
end
|
228
250
|
end
|
229
251
|
|
230
252
|
def destroy_attached_files
|
253
|
+
logger.info("[paperclip] Deleting attachments.")
|
231
254
|
each_attachment do |name, attachment|
|
232
255
|
attachment.send(:queue_existing_for_delete)
|
233
256
|
attachment.send(:flush_deletes)
|
data/lib/paperclip/attachment.rb
CHANGED
@@ -10,12 +10,12 @@ module Paperclip
|
|
10
10
|
:styles => {},
|
11
11
|
:default_url => "/:attachment/:style/missing.png",
|
12
12
|
:default_style => :original,
|
13
|
-
:validations =>
|
13
|
+
:validations => {},
|
14
14
|
:storage => :filesystem
|
15
15
|
}
|
16
16
|
end
|
17
17
|
|
18
|
-
attr_reader :name, :instance, :styles, :default_style
|
18
|
+
attr_reader :name, :instance, :styles, :default_style, :convert_options
|
19
19
|
|
20
20
|
# Creates an Attachment object. +name+ is the name of the attachment, +instance+ is the
|
21
21
|
# ActiveRecord object instance it's attached to, and +options+ is the same as the hash
|
@@ -34,37 +34,59 @@ module Paperclip
|
|
34
34
|
@default_style = options[:default_style]
|
35
35
|
@storage = options[:storage]
|
36
36
|
@whiny_thumbnails = options[:whiny_thumbnails]
|
37
|
+
@convert_options = options[:convert_options] || {}
|
37
38
|
@options = options
|
38
39
|
@queued_for_delete = []
|
39
40
|
@queued_for_write = {}
|
40
|
-
@errors =
|
41
|
+
@errors = {}
|
41
42
|
@validation_errors = nil
|
42
43
|
@dirty = false
|
43
44
|
|
44
45
|
normalize_style_definition
|
45
46
|
initialize_storage
|
47
|
+
|
48
|
+
logger.info("[paperclip] Paperclip attachment #{name} on #{instance.class} initialized.")
|
46
49
|
end
|
47
50
|
|
48
51
|
# What gets called when you call instance.attachment = File. It clears errors,
|
49
52
|
# assigns attributes, processes the file, and runs validations. It also queues up
|
50
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:
|
55
|
+
# new_user.avatar = old_user.avatar
|
51
56
|
def assign uploaded_file
|
57
|
+
%w(file_name).each do |field|
|
58
|
+
unless @instance.class.column_names.include?("#{name}_#{field}")
|
59
|
+
raise PaperclipError.new("#{@instance.class} model does not have required column '#{name}_#{field}'")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if uploaded_file.is_a?(Paperclip::Attachment)
|
64
|
+
uploaded_file = uploaded_file.to_file(:original)
|
65
|
+
end
|
66
|
+
|
52
67
|
return nil unless valid_assignment?(uploaded_file)
|
68
|
+
logger.info("[paperclip] Assigning #{uploaded_file.inspect} to #{name}")
|
53
69
|
|
70
|
+
uploaded_file.binmode if uploaded_file.respond_to? :binmode
|
54
71
|
queue_existing_for_delete
|
55
|
-
@errors =
|
72
|
+
@errors = {}
|
56
73
|
@validation_errors = nil
|
57
74
|
|
58
75
|
return nil if uploaded_file.nil?
|
59
76
|
|
60
|
-
|
61
|
-
@
|
62
|
-
|
63
|
-
|
77
|
+
logger.info("[paperclip] Writing attributes for #{name}")
|
78
|
+
@queued_for_write[:original] = uploaded_file.to_tempfile
|
79
|
+
instance_write(:file_name, uploaded_file.original_filename.strip.gsub(/[^\w\d\.\-]+/, '_'))
|
80
|
+
instance_write(:content_type, uploaded_file.content_type.to_s.strip)
|
81
|
+
instance_write(:file_size, uploaded_file.size.to_i)
|
82
|
+
instance_write(:updated_at, Time.now)
|
64
83
|
|
65
84
|
@dirty = true
|
66
85
|
|
67
86
|
post_process
|
87
|
+
|
88
|
+
# Reset the file size if the original file was reprocessed.
|
89
|
+
instance_write(:file_size, uploaded_file.size.to_i)
|
68
90
|
ensure
|
69
91
|
validate
|
70
92
|
end
|
@@ -75,15 +97,16 @@ module Paperclip
|
|
75
97
|
# This is not recommended if you don't need the security, however, for
|
76
98
|
# performance reasons.
|
77
99
|
def url style = default_style
|
78
|
-
original_filename.nil? ? interpolate(@default_url, style) : interpolate(@url, style)
|
100
|
+
url = original_filename.nil? ? interpolate(@default_url, style) : interpolate(@url, style)
|
101
|
+
updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
|
79
102
|
end
|
80
103
|
|
81
|
-
# Returns the path of the attachment as defined by the :path
|
104
|
+
# Returns the path of the attachment as defined by the :path option. If the
|
82
105
|
# file is stored in the filesystem the path refers to the path of the file on
|
83
|
-
# disk. If the file is stored in S3, the path is the "key" part of
|
106
|
+
# disk. If the file is stored in S3, the path is the "key" part of the URL,
|
84
107
|
# and the :bucket option refers to the S3 bucket.
|
85
108
|
def path style = nil #:nodoc:
|
86
|
-
interpolate(@path, style)
|
109
|
+
original_filename.nil? ? nil : interpolate(@path, style)
|
87
110
|
end
|
88
111
|
|
89
112
|
# Alias to +url+
|
@@ -91,14 +114,15 @@ module Paperclip
|
|
91
114
|
url(style)
|
92
115
|
end
|
93
116
|
|
94
|
-
# Returns true if there are
|
117
|
+
# Returns true if there are no errors on this attachment.
|
95
118
|
def valid?
|
96
|
-
|
119
|
+
validate
|
120
|
+
errors.empty?
|
97
121
|
end
|
98
122
|
|
99
123
|
# Returns an array containing the errors on this attachment.
|
100
124
|
def errors
|
101
|
-
@errors
|
125
|
+
@errors
|
102
126
|
end
|
103
127
|
|
104
128
|
# Returns true if there are changes that need to be saved.
|
@@ -110,11 +134,13 @@ module Paperclip
|
|
110
134
|
# the instance's errors and returns false, cancelling the save.
|
111
135
|
def save
|
112
136
|
if valid?
|
137
|
+
logger.info("[paperclip] Saving files for #{name}")
|
113
138
|
flush_deletes
|
114
139
|
flush_writes
|
115
140
|
@dirty = false
|
116
141
|
true
|
117
142
|
else
|
143
|
+
logger.info("[paperclip] Errors on #{name}. Not saving.")
|
118
144
|
flush_errors
|
119
145
|
false
|
120
146
|
end
|
@@ -123,7 +149,20 @@ module Paperclip
|
|
123
149
|
# Returns the name of the file as originally assigned, and as lives in the
|
124
150
|
# <attachment>_file_name attribute of the model.
|
125
151
|
def original_filename
|
126
|
-
|
152
|
+
instance_read(:file_name)
|
153
|
+
end
|
154
|
+
|
155
|
+
def size
|
156
|
+
instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
|
157
|
+
end
|
158
|
+
|
159
|
+
def content_type
|
160
|
+
instance_read(:content_type)
|
161
|
+
end
|
162
|
+
|
163
|
+
def updated_at
|
164
|
+
time = instance_read(:updated_at)
|
165
|
+
time && time.to_i
|
127
166
|
end
|
128
167
|
|
129
168
|
# A hash of procs that are run during the interpolation of a path or url.
|
@@ -134,11 +173,12 @@ module Paperclip
|
|
134
173
|
def self.interpolations
|
135
174
|
@interpolations ||= {
|
136
175
|
:rails_root => lambda{|attachment,style| RAILS_ROOT },
|
176
|
+
:rails_env => lambda{|attachment,style| RAILS_ENV },
|
137
177
|
:class => lambda do |attachment,style|
|
138
178
|
attachment.instance.class.name.underscore.pluralize
|
139
179
|
end,
|
140
180
|
:basename => lambda do |attachment,style|
|
141
|
-
attachment.original_filename.gsub(File.extname(attachment.original_filename)
|
181
|
+
attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "")
|
142
182
|
end,
|
143
183
|
:extension => lambda do |attachment,style|
|
144
184
|
((style = attachment.styles[style]) && style.last) ||
|
@@ -159,29 +199,58 @@ module Paperclip
|
|
159
199
|
# again.
|
160
200
|
def reprocess!
|
161
201
|
new_original = Tempfile.new("paperclip-reprocess")
|
162
|
-
old_original = to_file(:original)
|
163
|
-
|
164
|
-
|
202
|
+
if old_original = to_file(:original)
|
203
|
+
new_original.write( old_original.read )
|
204
|
+
new_original.rewind
|
165
205
|
|
166
|
-
|
167
|
-
|
206
|
+
@queued_for_write = { :original => new_original }
|
207
|
+
post_process
|
208
|
+
|
209
|
+
old_original.close if old_original.respond_to?(:close)
|
168
210
|
|
169
|
-
|
211
|
+
save
|
212
|
+
else
|
213
|
+
true
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def file?
|
218
|
+
!original_filename.blank?
|
219
|
+
end
|
220
|
+
|
221
|
+
def instance_write(attr, value)
|
222
|
+
setter = :"#{name}_#{attr}="
|
223
|
+
responds = instance.respond_to?(setter)
|
224
|
+
instance.send(setter, value) if responds || attr.to_s == "file_name"
|
225
|
+
end
|
226
|
+
|
227
|
+
def instance_read(attr)
|
228
|
+
getter = :"#{name}_#{attr}"
|
229
|
+
responds = instance.respond_to?(getter)
|
230
|
+
instance.send(getter) if responds || attr.to_s == "file_name"
|
170
231
|
end
|
171
232
|
|
172
233
|
private
|
173
234
|
|
235
|
+
def logger
|
236
|
+
instance.logger
|
237
|
+
end
|
238
|
+
|
174
239
|
def valid_assignment? file #:nodoc:
|
175
240
|
file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
|
176
241
|
end
|
177
242
|
|
178
243
|
def validate #:nodoc:
|
179
244
|
unless @validation_errors
|
180
|
-
@validation_errors = @validations.
|
181
|
-
|
182
|
-
|
183
|
-
|
245
|
+
@validation_errors = @validations.inject({}) do |errors, validation|
|
246
|
+
name, block = validation
|
247
|
+
errors[name] = block.call(self, instance) if block
|
248
|
+
errors
|
249
|
+
end
|
250
|
+
@validation_errors.reject!{|k,v| v == nil }
|
251
|
+
@errors.merge!(@validation_errors)
|
184
252
|
end
|
253
|
+
@validation_errors
|
185
254
|
end
|
186
255
|
|
187
256
|
def normalize_style_definition
|
@@ -197,17 +266,24 @@ module Paperclip
|
|
197
266
|
self.extend(@storage_module)
|
198
267
|
end
|
199
268
|
|
269
|
+
def extra_options_for(style) #:nodoc:
|
270
|
+
[ convert_options[style], convert_options[:all] ].compact.join(" ")
|
271
|
+
end
|
272
|
+
|
200
273
|
def post_process #:nodoc:
|
201
274
|
return if @queued_for_write[:original].nil?
|
275
|
+
logger.info("[paperclip] Post-processing #{name}")
|
202
276
|
@styles.each do |name, args|
|
203
277
|
begin
|
204
278
|
dimensions, format = args
|
279
|
+
dimensions = dimensions.call(instance) if dimensions.respond_to? :call
|
205
280
|
@queued_for_write[name] = Thumbnail.make(@queued_for_write[:original],
|
206
281
|
dimensions,
|
207
282
|
format,
|
208
|
-
|
283
|
+
extra_options_for(name),
|
284
|
+
@whiny_thumbnails)
|
209
285
|
rescue PaperclipError => e
|
210
|
-
@errors << e.message if @whiny_thumbnails
|
286
|
+
(@errors[:processing] ||= []) << e.message if @whiny_thumbnails
|
211
287
|
end
|
212
288
|
end
|
213
289
|
end
|
@@ -223,18 +299,20 @@ module Paperclip
|
|
223
299
|
end
|
224
300
|
|
225
301
|
def queue_existing_for_delete #:nodoc:
|
226
|
-
return
|
302
|
+
return unless file?
|
303
|
+
logger.info("[paperclip] Queueing the existing files for #{name} for deletion.")
|
227
304
|
@queued_for_delete += [:original, *@styles.keys].uniq.map do |style|
|
228
305
|
path(style) if exists?(style)
|
229
306
|
end.compact
|
230
|
-
|
231
|
-
|
232
|
-
|
307
|
+
instance_write(:file_name, nil)
|
308
|
+
instance_write(:content_type, nil)
|
309
|
+
instance_write(:file_size, nil)
|
310
|
+
instance_write(:updated_at, nil)
|
233
311
|
end
|
234
312
|
|
235
313
|
def flush_errors #:nodoc:
|
236
|
-
@errors.each do |error|
|
237
|
-
instance.errors.add(name,
|
314
|
+
@errors.each do |error, message|
|
315
|
+
instance.errors.add(name, message) if message
|
238
316
|
end
|
239
317
|
end
|
240
318
|
|