paperclip 2.2.8 → 2.2.9.1
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 +9 -7
- data/Rakefile +29 -7
- data/lib/paperclip.rb +62 -30
- data/lib/paperclip/attachment.rb +64 -55
- data/lib/paperclip/geometry.rb +1 -1
- data/lib/paperclip/interpolations.rb +105 -0
- data/lib/paperclip/processor.rb +7 -6
- data/lib/paperclip/storage.rb +11 -9
- data/test/attachment_test.rb +29 -3
- data/test/fixtures/s3.yml +4 -0
- data/test/geometry_test.rb +9 -0
- data/test/helper.rb +21 -3
- data/test/interpolations_test.rb +120 -0
- data/test/paperclip_test.rb +58 -0
- data/test/storage_test.rb +12 -7
- metadata +4 -15
- data/test/debug.log +0 -0
- data/test/s3.yml +0 -0
- data/test/tmp/storage.txt +0 -0
data/README.rdoc
CHANGED
@@ -79,8 +79,7 @@ validates_attachment_size.
|
|
79
79
|
|
80
80
|
The files that are assigned as attachments are, by default, placed in the
|
81
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
|
82
|
+
location is ":rails_root/public/system/:attachment/:id/:style/:filename". This
|
84
83
|
location was chosen because on standard Capistrano deployments, the
|
85
84
|
public/system directory is symlinked to the app's shared directory, meaning it
|
86
85
|
will survive between deployments. For example, using that :path, you may have a
|
@@ -89,7 +88,7 @@ file at
|
|
89
88
|
/data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png
|
90
89
|
|
91
90
|
NOTE: This is a change from previous versions of Paperclip, but is overall a
|
92
|
-
safer choice for the
|
91
|
+
safer choice for the default file store.
|
93
92
|
|
94
93
|
You may also choose to store your files using Amazon's S3 service. You can find
|
95
94
|
more information about S3 storage at the description for
|
@@ -104,9 +103,9 @@ variables.
|
|
104
103
|
|
105
104
|
==Post Processing
|
106
105
|
|
107
|
-
Paperclip supports an
|
106
|
+
Paperclip supports an extensible selection of post-processors. When you define
|
108
107
|
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
|
108
|
+
"styles" are actually "thumbnails". However, you can do much more than just
|
110
109
|
thumbnail images. By defining a subclass of Paperclip::Processor, you can
|
111
110
|
perform any processing you want on the files that are attached. Any file in
|
112
111
|
your Rails app's lib/paperclip_processors directory is automatically loaded by
|
@@ -141,7 +140,10 @@ For example, assuming we had this definition:
|
|
141
140
|
|
142
141
|
then both the :rotator processor and the :ocr processor would receive the
|
143
142
|
options "{ :quality => :better }". This parameter may not mean anything to one
|
144
|
-
or more or the processors, and they are
|
143
|
+
or more or the processors, and they are expected to ignore it.
|
144
|
+
|
145
|
+
NOTE: Because processors operate by turning the original attachment into the
|
146
|
+
styles, no processors will be run if there are no styles defined.
|
145
147
|
|
146
148
|
==Events
|
147
149
|
|
@@ -157,7 +159,7 @@ will halt. Returning false in an after_ filter will not halt anything, but you
|
|
157
159
|
can access the model and the attachment if necessary.
|
158
160
|
|
159
161
|
NOTE: Post processing will not even *start* if the attachment is not valid
|
160
|
-
according to the validations. Your callbacks
|
162
|
+
according to the validations. Your callbacks and processors will *only* be
|
161
163
|
called with valid attachments.
|
162
164
|
|
163
165
|
==Contributing
|
data/Rakefile
CHANGED
@@ -42,8 +42,23 @@ task :clean do |t|
|
|
42
42
|
FileUtils.rm_rf "pkg"
|
43
43
|
FileUtils.rm "test/debug.log" rescue nil
|
44
44
|
FileUtils.rm "test/paperclip.db" rescue nil
|
45
|
+
Dir.glob("paperclip-*.gem").each{|f| FileUtils.rm f }
|
45
46
|
end
|
46
47
|
|
48
|
+
include_file_globs = ["README*",
|
49
|
+
"LICENSE",
|
50
|
+
"Rakefile",
|
51
|
+
"init.rb",
|
52
|
+
"{generators,lib,tasks,test,shoulda_macros}/**/*"]
|
53
|
+
exclude_file_globs = ["test/s3.yml",
|
54
|
+
"test/debug.log",
|
55
|
+
"test/paperclip.db",
|
56
|
+
"test/doc",
|
57
|
+
"test/doc/*",
|
58
|
+
"test/pkg",
|
59
|
+
"test/pkg/*",
|
60
|
+
"test/tmp",
|
61
|
+
"test/tmp/*"]
|
47
62
|
spec = Gem::Specification.new do |s|
|
48
63
|
s.name = "paperclip"
|
49
64
|
s.version = Paperclip::VERSION
|
@@ -52,11 +67,7 @@ spec = Gem::Specification.new do |s|
|
|
52
67
|
s.homepage = "http://www.thoughtbot.com/projects/paperclip"
|
53
68
|
s.platform = Gem::Platform::RUBY
|
54
69
|
s.summary = "File attachments as attributes for ActiveRecord"
|
55
|
-
s.files = FileList[
|
56
|
-
"LICENSE",
|
57
|
-
"Rakefile",
|
58
|
-
"init.rb",
|
59
|
-
"{generators,lib,tasks,test,shoulda_macros}/**/*"].to_a
|
70
|
+
s.files = FileList[include_file_globs].to_a - FileList[exclude_file_globs].to_a
|
60
71
|
s.require_path = "lib"
|
61
72
|
s.test_files = FileList["test/**/test_*.rb"].to_a
|
62
73
|
s.rubyforge_project = "paperclip"
|
@@ -64,14 +75,25 @@ spec = Gem::Specification.new do |s|
|
|
64
75
|
s.extra_rdoc_files = FileList["README*"].to_a
|
65
76
|
s.rdoc_options << '--line-numbers' << '--inline-source'
|
66
77
|
s.requirements << "ImageMagick"
|
67
|
-
s.add_runtime_dependency 'right_aws'
|
68
78
|
s.add_development_dependency 'thoughtbot-shoulda'
|
69
79
|
s.add_development_dependency 'mocha'
|
70
80
|
end
|
81
|
+
|
82
|
+
desc "Print a list of the files to be put into the gem"
|
83
|
+
task :manifest => :clean do
|
84
|
+
spec.files.each do |file|
|
85
|
+
puts file
|
86
|
+
end
|
87
|
+
end
|
71
88
|
|
72
89
|
desc "Generate a gemspec file for GitHub"
|
73
|
-
task :gemspec do
|
90
|
+
task :gemspec => :clean do
|
74
91
|
File.open("#{spec.name}.gemspec", 'w') do |f|
|
75
92
|
f.write spec.to_ruby
|
76
93
|
end
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "Build the gem into the current directory"
|
97
|
+
task :gem => :gemspec do
|
98
|
+
`gem build #{spec.name}.gemspec`
|
77
99
|
end
|
data/lib/paperclip.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# columns to your table.
|
6
6
|
#
|
7
7
|
# Author:: Jon Yurek
|
8
|
-
# Copyright:: Copyright (c) 2008 thoughtbot, inc.
|
8
|
+
# Copyright:: Copyright (c) 2008-2009 thoughtbot, inc.
|
9
9
|
# License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
10
10
|
#
|
11
11
|
# Paperclip defines an attachment as any file, though it makes special considerations
|
@@ -32,6 +32,7 @@ require 'paperclip/geometry'
|
|
32
32
|
require 'paperclip/processor'
|
33
33
|
require 'paperclip/thumbnail'
|
34
34
|
require 'paperclip/storage'
|
35
|
+
require 'paperclip/interpolations'
|
35
36
|
require 'paperclip/attachment'
|
36
37
|
if defined? RAILS_ROOT
|
37
38
|
Dir.glob(File.join(File.expand_path(RAILS_ROOT), "lib", "paperclip_processors", "*.rb")).each do |processor|
|
@@ -43,11 +44,11 @@ end
|
|
43
44
|
# documentation for Paperclip::ClassMethods for more useful information.
|
44
45
|
module Paperclip
|
45
46
|
|
46
|
-
VERSION = "2.2.
|
47
|
+
VERSION = "2.2.9.1"
|
47
48
|
|
48
49
|
class << self
|
49
50
|
# Provides configurability to Paperclip. There are a number of options available, such as:
|
50
|
-
# *
|
51
|
+
# * whiny: Will raise an error if Paperclip cannot process thumbnails of
|
51
52
|
# an uploaded image. Defaults to true.
|
52
53
|
# * log: Logs progress to the Rails log. Uses ActiveRecord's logger, so honors
|
53
54
|
# log levels, etc. Defaults to true.
|
@@ -57,10 +58,11 @@ module Paperclip
|
|
57
58
|
# * image_magick_path: Deprecated alias of command_path.
|
58
59
|
def options
|
59
60
|
@options ||= {
|
60
|
-
:
|
61
|
+
:whiny => true,
|
61
62
|
:image_magick_path => nil,
|
62
63
|
:command_path => nil,
|
63
64
|
:log => true,
|
65
|
+
:log_command => false,
|
64
66
|
:swallow_stderr => true
|
65
67
|
}
|
66
68
|
end
|
@@ -74,7 +76,7 @@ module Paperclip
|
|
74
76
|
end
|
75
77
|
|
76
78
|
def interpolates key, &block
|
77
|
-
Paperclip::
|
79
|
+
Paperclip::Interpolations[key] = block
|
78
80
|
end
|
79
81
|
|
80
82
|
# The run method takes a command to execute and a string of parameters
|
@@ -86,9 +88,14 @@ module Paperclip
|
|
86
88
|
# If the command returns with a result code that is not one of the
|
87
89
|
# expected_outcodes, a PaperclipCommandLineError will be raised. Generally
|
88
90
|
# a code of 0 is expected, but a list of codes may be passed if necessary.
|
91
|
+
#
|
92
|
+
# This method can log the command being run when
|
93
|
+
# Paperclip.options[:log_command] is set to true (defaults to false). This
|
94
|
+
# will only log if logging in general is set to true as well.
|
89
95
|
def run cmd, params = "", expected_outcodes = 0
|
90
96
|
command = %Q<#{%Q[#{path_for_command(cmd)} #{params}].gsub(/\s+/, " ")}>
|
91
97
|
command = "#{command} 2>#{bit_bucket}" if Paperclip.options[:swallow_stderr]
|
98
|
+
Paperclip.log(command) if Paperclip.options[:log_command]
|
92
99
|
output = `#{command}`
|
93
100
|
unless [expected_outcodes].flatten.include?($?.exitstatus)
|
94
101
|
raise PaperclipCommandLineError, "Error while running #{cmd}"
|
@@ -115,6 +122,20 @@ module Paperclip
|
|
115
122
|
end
|
116
123
|
processor
|
117
124
|
end
|
125
|
+
|
126
|
+
# Log a paperclip-specific line. Uses ActiveRecord::Base.logger
|
127
|
+
# by default. Set Paperclip.options[:log] to false to turn off.
|
128
|
+
def log message
|
129
|
+
logger.info("[paperclip] #{message}") if logging?
|
130
|
+
end
|
131
|
+
|
132
|
+
def logger #:nodoc:
|
133
|
+
ActiveRecord::Base.logger
|
134
|
+
end
|
135
|
+
|
136
|
+
def logging? #:nodoc:
|
137
|
+
options[:log]
|
138
|
+
end
|
118
139
|
end
|
119
140
|
|
120
141
|
class PaperclipError < StandardError #:nodoc:
|
@@ -125,6 +146,9 @@ module Paperclip
|
|
125
146
|
|
126
147
|
class NotIdentifiedByImageMagickError < PaperclipError #:nodoc:
|
127
148
|
end
|
149
|
+
|
150
|
+
class InfiniteInterpolationError < PaperclipError #:nodoc:
|
151
|
+
end
|
128
152
|
|
129
153
|
module ClassMethods
|
130
154
|
# +has_attached_file+ gives the class it is called on an attribute that maps to a file. This
|
@@ -141,9 +165,9 @@ module Paperclip
|
|
141
165
|
# that can control permissions. You can specify the full domain and path, but usually
|
142
166
|
# just an absolute path is sufficient. The leading slash *must* be included manually for
|
143
167
|
# absolute paths. The default value is
|
144
|
-
# "/system/:attachment/:id/:style/:
|
168
|
+
# "/system/:attachment/:id/:style/:filename". See
|
145
169
|
# Paperclip::Attachment#interpolate for more information on variable interpolaton.
|
146
|
-
# :url => "/:class/:attachment/:id/:style_:
|
170
|
+
# :url => "/:class/:attachment/:id/:style_:filename"
|
147
171
|
# :url => "http://some.other.host/stuff/:class/:id_:extension"
|
148
172
|
# * +default_url+: The URL that will be returned if there is no attachment assigned.
|
149
173
|
# This field is interpolated just as the url is. The default value is
|
@@ -161,9 +185,10 @@ module Paperclip
|
|
161
185
|
# has_attached_file :avatar, :styles => { :normal => "100x100#" },
|
162
186
|
# :default_style => :normal
|
163
187
|
# user.avatar.url # => "/avatars/23/normal_me.png"
|
164
|
-
# * +
|
188
|
+
# * +whiny+: Will raise an error if Paperclip cannot post_process an uploaded file due
|
165
189
|
# to a command line error. This will override the global setting for this attachment.
|
166
|
-
# Defaults to true.
|
190
|
+
# Defaults to true. This option used to be called :whiny_thumbanils, but this is
|
191
|
+
# deprecated.
|
167
192
|
# * +convert_options+: When creating thumbnails, use this free-form options
|
168
193
|
# field to pass in various convert command options. Typical options are "-strip" to
|
169
194
|
# remove all Exif data from the image (save space for thumbnails and avatars) or
|
@@ -179,6 +204,9 @@ module Paperclip
|
|
179
204
|
# :all => "-strip",
|
180
205
|
# :negative => "-negate"
|
181
206
|
# }
|
207
|
+
# NOTE: While not deprecated yet, it is not recommended to specify options this way.
|
208
|
+
# It is recommended that :convert_options option be included in the hash passed to each
|
209
|
+
# :styles for compatability with future versions.
|
182
210
|
# * +storage+: Chooses the storage backend where the files will be stored. The current
|
183
211
|
# choices are :filesystem and :s3. The default is :filesystem. Make sure you read the
|
184
212
|
# documentation for Paperclip::Storage::Filesystem and Paperclip::Storage::S3
|
@@ -187,7 +215,7 @@ module Paperclip
|
|
187
215
|
include InstanceMethods
|
188
216
|
|
189
217
|
write_inheritable_attribute(:attachment_definitions, {}) if attachment_definitions.nil?
|
190
|
-
attachment_definitions[name] = {:validations =>
|
218
|
+
attachment_definitions[name] = {:validations => []}.merge(options)
|
191
219
|
|
192
220
|
after_save :save_attached_files
|
193
221
|
before_destroy :destroy_attached_files
|
@@ -220,30 +248,39 @@ module Paperclip
|
|
220
248
|
# * +less_than+: equivalent to :in => 0..options[:less_than]
|
221
249
|
# * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
|
222
250
|
# * +message+: error message to display, use :min and :max as replacements
|
251
|
+
# * +if+: A lambda or name of a method on the instance. Validation will only
|
252
|
+
# be run is this lambda or method returns true.
|
253
|
+
# * +unless+: Same as +if+ but validates if lambda or method returns false.
|
223
254
|
def validates_attachment_size name, options = {}
|
224
255
|
min = options[:greater_than] || (options[:in] && options[:in].first) || 0
|
225
256
|
max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
|
226
257
|
range = (min..max)
|
227
258
|
message = options[:message] || "file size must be between :min and :max bytes."
|
228
259
|
|
229
|
-
attachment_definitions[name][:validations][:size
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
end
|
260
|
+
attachment_definitions[name][:validations] << [:size, {:range => range,
|
261
|
+
:message => message,
|
262
|
+
:if => options[:if],
|
263
|
+
:unless => options[:unless]}]
|
234
264
|
end
|
235
265
|
|
236
266
|
# Adds errors if thumbnail creation fails. The same as specifying :whiny_thumbnails => true.
|
237
267
|
def validates_attachment_thumbnails name, options = {}
|
268
|
+
warn('[DEPRECATION] validates_attachment_thumbnail is deprecated. ' +
|
269
|
+
'This validation is on by default and will be removed from future versions. ' +
|
270
|
+
'If you wish to turn it off, supply :whiny => false in your definition.')
|
238
271
|
attachment_definitions[name][:whiny_thumbnails] = true
|
239
272
|
end
|
240
273
|
|
241
274
|
# Places ActiveRecord-style validations on the presence of a file.
|
275
|
+
# Options:
|
276
|
+
# * +if+: A lambda or name of a method on the instance. Validation will only
|
277
|
+
# be run is this lambda or method returns true.
|
278
|
+
# * +unless+: Same as +if+ but validates if lambda or method returns false.
|
242
279
|
def validates_attachment_presence name, options = {}
|
243
280
|
message = options[:message] || "must be set."
|
244
|
-
attachment_definitions[name][:validations][:presence
|
245
|
-
|
246
|
-
|
281
|
+
attachment_definitions[name][:validations] << [:presence, {:message => message,
|
282
|
+
:if => options[:if],
|
283
|
+
:unless => options[:unless]}]
|
247
284
|
end
|
248
285
|
|
249
286
|
# Places ActiveRecord-style validations on the content type of the file
|
@@ -256,22 +293,17 @@ module Paperclip
|
|
256
293
|
# match. Allows all by default.
|
257
294
|
# * +message+: The message to display when the uploaded file has an invalid
|
258
295
|
# content type.
|
296
|
+
# * +if+: A lambda or name of a method on the instance. Validation will only
|
297
|
+
# be run is this lambda or method returns true.
|
298
|
+
# * +unless+: Same as +if+ but validates if lambda or method returns false.
|
259
299
|
# NOTE: If you do not specify an [attachment]_content_type field on your
|
260
300
|
# model, content_type validation will work _ONLY upon assignment_ and
|
261
301
|
# re-validation after the instance has been reloaded will always succeed.
|
262
302
|
def validates_attachment_content_type name, options = {}
|
263
|
-
attachment_definitions[name][:validations][:content_type
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
unless valid_types.blank?
|
268
|
-
content_type = attachment.instance_read(:content_type)
|
269
|
-
unless valid_types.any?{|t| content_type.nil? || t === content_type }
|
270
|
-
options[:message] || "is not one of the allowed file types."
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
303
|
+
attachment_definitions[name][:validations] << [:content_type, {:content_type => options[:content_type],
|
304
|
+
:message => options[:message],
|
305
|
+
:if => options[:if],
|
306
|
+
:unless => options[:unless]}]
|
275
307
|
end
|
276
308
|
|
277
309
|
# Returns the attachment definitions defined by each call to
|
data/lib/paperclip/attachment.rb
CHANGED
@@ -6,17 +6,18 @@ module Paperclip
|
|
6
6
|
|
7
7
|
def self.default_options
|
8
8
|
@default_options ||= {
|
9
|
-
:url => "/system/:attachment/:id/:style/:
|
10
|
-
:path => ":rails_root/public
|
9
|
+
:url => "/system/:attachment/:id/:style/:filename",
|
10
|
+
:path => ":rails_root/public:url",
|
11
11
|
:styles => {},
|
12
12
|
:default_url => "/:attachment/:style/missing.png",
|
13
13
|
:default_style => :original,
|
14
|
-
:validations =>
|
15
|
-
:storage => :filesystem
|
14
|
+
:validations => [],
|
15
|
+
:storage => :filesystem,
|
16
|
+
:whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails]
|
16
17
|
}
|
17
18
|
end
|
18
19
|
|
19
|
-
attr_reader :name, :instance, :styles, :default_style, :convert_options, :queued_for_write
|
20
|
+
attr_reader :name, :instance, :styles, :default_style, :convert_options, :queued_for_write, :options
|
20
21
|
|
21
22
|
# Creates an Attachment object. +name+ is the name of the attachment,
|
22
23
|
# +instance+ is the ActiveRecord object instance it's attached to, and
|
@@ -37,7 +38,7 @@ module Paperclip
|
|
37
38
|
@validations = options[:validations]
|
38
39
|
@default_style = options[:default_style]
|
39
40
|
@storage = options[:storage]
|
40
|
-
@whiny = options[:whiny_thumbnails]
|
41
|
+
@whiny = options[:whiny_thumbnails] || options[:whiny]
|
41
42
|
@convert_options = options[:convert_options] || {}
|
42
43
|
@processors = options[:processors] || [:thumbnail]
|
43
44
|
@options = options
|
@@ -60,11 +61,7 @@ module Paperclip
|
|
60
61
|
# If the file that is assigned is not valid, the processing (i.e.
|
61
62
|
# thumbnailing, etc) will NOT be run.
|
62
63
|
def assign uploaded_file
|
63
|
-
|
64
|
-
unless @instance.class.column_names.include?("#{name}_#{field}")
|
65
|
-
raise PaperclipError.new("#{@instance.class} model does not have required column '#{name}_#{field}'")
|
66
|
-
end
|
67
|
-
end
|
64
|
+
ensure_required_accessors!
|
68
65
|
|
69
66
|
if uploaded_file.is_a?(Paperclip::Attachment)
|
70
67
|
uploaded_file = uploaded_file.to_file(:original)
|
@@ -111,7 +108,7 @@ module Paperclip
|
|
111
108
|
# file is stored in the filesystem the path refers to the path of the file
|
112
109
|
# on disk. If the file is stored in S3, the path is the "key" part of the
|
113
110
|
# URL, and the :bucket option refers to the S3 bucket.
|
114
|
-
def path style =
|
111
|
+
def path style = default_style
|
115
112
|
original_filename.nil? ? nil : interpolate(@path, style)
|
116
113
|
end
|
117
114
|
|
@@ -192,32 +189,16 @@ module Paperclip
|
|
192
189
|
time && time.to_i
|
193
190
|
end
|
194
191
|
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
#
|
192
|
+
# Paths and URLs can have a number of variables interpolated into them
|
193
|
+
# to vary the storage location based on name, id, style, class, etc.
|
194
|
+
# This method is a deprecated access into supplying and retrieving these
|
195
|
+
# interpolations. Future access should use either Paperclip.interpolates
|
196
|
+
# or extend the Paperclip::Interpolations module directly.
|
200
197
|
def self.interpolations
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
attachment.instance.class.name.underscore.pluralize
|
206
|
-
end,
|
207
|
-
:basename => lambda do |attachment,style|
|
208
|
-
attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "")
|
209
|
-
end,
|
210
|
-
:extension => lambda do |attachment,style|
|
211
|
-
((style = attachment.styles[style]) && style[:format]) ||
|
212
|
-
File.extname(attachment.original_filename).gsub(/^\.+/, "")
|
213
|
-
end,
|
214
|
-
:id => lambda{|attachment,style| attachment.instance.id },
|
215
|
-
:id_partition => lambda do |attachment, style|
|
216
|
-
("%09d" % attachment.instance.id).scan(/\d{3}/).join("/")
|
217
|
-
end,
|
218
|
-
:attachment => lambda{|attachment,style| attachment.name.to_s.downcase.pluralize },
|
219
|
-
:style => lambda{|attachment,style| style || attachment.default_style },
|
220
|
-
}
|
198
|
+
warn('[DEPRECATION] Paperclip::Attachment.interpolations is deprecated ' +
|
199
|
+
'and will be removed from future versions. ' +
|
200
|
+
'Use Paperclip.interpolates instead')
|
201
|
+
Paperclip::Interpolations
|
221
202
|
end
|
222
203
|
|
223
204
|
# This method really shouldn't be called that often. It's expected use is
|
@@ -269,16 +250,16 @@ module Paperclip
|
|
269
250
|
|
270
251
|
private
|
271
252
|
|
272
|
-
def
|
273
|
-
|
253
|
+
def ensure_required_accessors! #:nodoc:
|
254
|
+
%w(file_name).each do |field|
|
255
|
+
unless @instance.respond_to?("#{name}_#{field}") && @instance.respond_to?("#{name}_#{field}=")
|
256
|
+
raise PaperclipError.new("#{@instance.class} model missing required attr_accessor for '#{name}_#{field}'")
|
257
|
+
end
|
258
|
+
end
|
274
259
|
end
|
275
260
|
|
276
261
|
def log message #:nodoc:
|
277
|
-
|
278
|
-
end
|
279
|
-
|
280
|
-
def logging? #:nodoc:
|
281
|
-
Paperclip.options[:log]
|
262
|
+
Paperclip.log(message)
|
282
263
|
end
|
283
264
|
|
284
265
|
def valid_assignment? file #:nodoc:
|
@@ -288,8 +269,8 @@ module Paperclip
|
|
288
269
|
def validate #:nodoc:
|
289
270
|
unless @validation_errors
|
290
271
|
@validation_errors = @validations.inject({}) do |errors, validation|
|
291
|
-
name,
|
292
|
-
errors[name] =
|
272
|
+
name, options = validation
|
273
|
+
errors[name] = send(:"validate_#{name}", options) if allow_validation?(options)
|
293
274
|
errors
|
294
275
|
end
|
295
276
|
@validation_errors.reject!{|k,v| v == nil }
|
@@ -298,6 +279,40 @@ module Paperclip
|
|
298
279
|
@validation_errors
|
299
280
|
end
|
300
281
|
|
282
|
+
def allow_validation? options #:nodoc:
|
283
|
+
(options[:if].nil? || check_guard(options[:if])) && (options[:unless].nil? || !check_guard(options[:unless]))
|
284
|
+
end
|
285
|
+
|
286
|
+
def check_guard guard #:nodoc:
|
287
|
+
if guard.respond_to? :call
|
288
|
+
guard.call(instance)
|
289
|
+
elsif ! guard.blank?
|
290
|
+
instance.send(guard.to_s)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def validate_size options #:nodoc:
|
295
|
+
if file? && !options[:range].include?(size.to_i)
|
296
|
+
options[:message].gsub(/:min/, options[:min].to_s).gsub(/:max/, options[:max].to_s)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def validate_presence options #:nodoc:
|
301
|
+
options[:message] unless file?
|
302
|
+
end
|
303
|
+
|
304
|
+
def validate_content_type options #:nodoc:
|
305
|
+
valid_types = [options[:content_type]].flatten
|
306
|
+
unless original_filename.blank?
|
307
|
+
unless valid_types.blank?
|
308
|
+
content_type = instance_read(:content_type)
|
309
|
+
unless valid_types.any?{|t| content_type.nil? || t === content_type }
|
310
|
+
options[:message] || "is not one of the allowed file types."
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
301
316
|
def normalize_style_definition #:nodoc:
|
302
317
|
@styles.each do |name, args|
|
303
318
|
unless args.is_a? Hash
|
@@ -349,7 +364,7 @@ module Paperclip
|
|
349
364
|
return if fire_events(:after)
|
350
365
|
end
|
351
366
|
|
352
|
-
def fire_events(which)
|
367
|
+
def fire_events(which) #:nodoc:
|
353
368
|
return true if callback(:"#{which}_post_process") == false
|
354
369
|
return true if callback(:"#{which}_#{name}_post_process") == false
|
355
370
|
end
|
@@ -358,7 +373,7 @@ module Paperclip
|
|
358
373
|
instance.run_callbacks(which, @queued_for_write){|result, obj| result == false }
|
359
374
|
end
|
360
375
|
|
361
|
-
def post_process_styles
|
376
|
+
def post_process_styles #:nodoc:
|
362
377
|
@styles.each do |name, args|
|
363
378
|
begin
|
364
379
|
raise RuntimeError.new("Style #{name} has no processors defined.") if args[:processors].blank?
|
@@ -373,13 +388,7 @@ module Paperclip
|
|
373
388
|
end
|
374
389
|
|
375
390
|
def interpolate pattern, style = default_style #:nodoc:
|
376
|
-
|
377
|
-
interpolations.reverse.inject( pattern.dup ) do |result, interpolation|
|
378
|
-
tag, blk = interpolation
|
379
|
-
result.gsub(/:#{tag}/) do |match|
|
380
|
-
blk.call( self, style )
|
381
|
-
end
|
382
|
-
end
|
391
|
+
Paperclip::Interpolations.interpolate(pattern, self, style)
|
383
392
|
end
|
384
393
|
|
385
394
|
def queue_existing_for_delete #:nodoc:
|
data/lib/paperclip/geometry.rb
CHANGED
@@ -26,7 +26,7 @@ module Paperclip
|
|
26
26
|
|
27
27
|
# Parses a "WxH" formatted string, where W is the width and H is the height.
|
28
28
|
def self.parse string
|
29
|
-
if match = (string && string.match(/\b(\d*)x?(\d*)\b([\>\<\#\@\%^!])?/))
|
29
|
+
if match = (string && string.match(/\b(\d*)x?(\d*)\b([\>\<\#\@\%^!])?/i))
|
30
30
|
Geometry.new(*match[1,3])
|
31
31
|
end
|
32
32
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Paperclip
|
2
|
+
# This module contains all the methods that are available for interpolation
|
3
|
+
# in paths and urls. To add your own (or override an existing one), you
|
4
|
+
# can either open this module and define it, or call the
|
5
|
+
# Paperclip.interpolates method.
|
6
|
+
module Interpolations
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# Hash assignment of interpolations. Included only for compatability,
|
10
|
+
# and is not intended for normal use.
|
11
|
+
def self.[]= name, block
|
12
|
+
define_method(name, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Hash access of interpolations. Included only for compatability,
|
16
|
+
# and is not intended for normal use.
|
17
|
+
def self.[] name
|
18
|
+
method(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns a sorted list of all interpolations.
|
22
|
+
def self.all
|
23
|
+
self.instance_methods(false).sort
|
24
|
+
end
|
25
|
+
|
26
|
+
# Perform the actual interpolation. Takes the pattern to interpolate
|
27
|
+
# and the arguments to pass, which are the attachment and style name.
|
28
|
+
def self.interpolate pattern, *args
|
29
|
+
all.reverse.inject( pattern.dup ) do |result, tag|
|
30
|
+
result.gsub(/:#{tag}/) do |match|
|
31
|
+
send( tag, *args )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the filename, the same way as ":basename.:extension" would.
|
37
|
+
def filename attachment, style
|
38
|
+
"#{basename(attachment, style)}.#{extension(attachment, style)}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the interpolated URL. Will raise an error if the url itself
|
42
|
+
# contains ":url" to prevent infinite recursion. This interpolation
|
43
|
+
# is used in the default :path to ease default specifications.
|
44
|
+
def url attachment, style
|
45
|
+
raise InfiniteInterpolationError if attachment.options[:url].include?(":url")
|
46
|
+
attachment.url(style, false)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the timestamp as defined by the <attachment>_updated_at field
|
50
|
+
def timestamp attachment, style
|
51
|
+
attachment.instance_read(:updated_at).to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the RAILS_ROOT constant.
|
55
|
+
def rails_root attachment, style
|
56
|
+
RAILS_ROOT
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the RAILS_ENV constant.
|
60
|
+
def rails_env attachment, style
|
61
|
+
RAILS_ENV
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the underscored, pluralized version of the class name.
|
65
|
+
# e.g. "users" for the User class.
|
66
|
+
def class attachment, style
|
67
|
+
attachment.instance.class.to_s.underscore.pluralize
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the basename of the file. e.g. "file" for "file.jpg"
|
71
|
+
def basename attachment, style
|
72
|
+
attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the extension of the file. e.g. "jpg" for "file.jpg"
|
76
|
+
# If the style has a format defined, it will return the format instead
|
77
|
+
# of the actual extension.
|
78
|
+
def extension attachment, style
|
79
|
+
((style = attachment.styles[style]) && style[:format]) ||
|
80
|
+
File.extname(attachment.original_filename).gsub(/^\.+/, "")
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the id of the instance.
|
84
|
+
def id attachment, style
|
85
|
+
attachment.instance.id
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the id of the instance in a split path form. e.g. returns
|
89
|
+
# 000/001/234 for an id of 1234.
|
90
|
+
def id_partition attachment, style
|
91
|
+
("%09d" % attachment.instance.id).scan(/\d{3}/).join("/")
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the pluralized form of the attachment name. e.g.
|
95
|
+
# "avatars" for an attachment of :avatar
|
96
|
+
def attachment attachment, style
|
97
|
+
attachment.name.to_s.downcase.pluralize
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the style, or the default style if nil is supplied.
|
101
|
+
def style attachment, style
|
102
|
+
style || attachment.default_style
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/paperclip/processor.rb
CHANGED
@@ -5,13 +5,14 @@ module Paperclip
|
|
5
5
|
# are not required to follow suit.
|
6
6
|
#
|
7
7
|
# Processors are required to be defined inside the Paperclip module and
|
8
|
-
# are also required to be a subclass of Paperclip::Processor. There
|
9
|
-
# only
|
10
|
-
# #
|
11
|
-
# be operated on (which is an instance of
|
12
|
-
# that were defined in has_attached_file's
|
8
|
+
# are also required to be a subclass of Paperclip::Processor. There is
|
9
|
+
# only one method you *must* implement to properly be a subclass:
|
10
|
+
# #make, but #initialize may also be of use. Both methods accept 3
|
11
|
+
# arguments: the file that will be operated on (which is an instance of
|
12
|
+
# File), a hash of options that were defined in has_attached_file's
|
13
|
+
# style hash, and the Paperclip::Attachment itself.
|
13
14
|
#
|
14
|
-
# All #make needs to
|
15
|
+
# All #make needs to return is an instance of File (Tempfile is
|
15
16
|
# acceptable) which contains the results of the processing.
|
16
17
|
#
|
17
18
|
# See Paperclip.run for more information about using command-line
|
data/lib/paperclip/storage.rb
CHANGED
@@ -39,7 +39,7 @@ module Paperclip
|
|
39
39
|
@queued_for_write.each do |style, file|
|
40
40
|
file.close
|
41
41
|
FileUtils.mkdir_p(File.dirname(path(style)))
|
42
|
-
|
42
|
+
log("saving #{path(style)}")
|
43
43
|
FileUtils.mv(file.path, path(style))
|
44
44
|
FileUtils.chmod(0644, path(style))
|
45
45
|
end
|
@@ -49,7 +49,7 @@ module Paperclip
|
|
49
49
|
def flush_deletes #:nodoc:
|
50
50
|
@queued_for_delete.each do |path|
|
51
51
|
begin
|
52
|
-
|
52
|
+
log("deleting #{path}")
|
53
53
|
FileUtils.rm(path) if File.exist?(path)
|
54
54
|
rescue Errno::ENOENT => e
|
55
55
|
# ignore file-not-found, let everything else pass
|
@@ -62,7 +62,7 @@ module Paperclip
|
|
62
62
|
rescue Errno::EEXIST, Errno::ENOTEMPTY, Errno::ENOENT, Errno::EINVAL, Errno::ENOTDIR
|
63
63
|
# Stop trying to remove parent directories
|
64
64
|
rescue SystemCallError => e
|
65
|
-
|
65
|
+
log("There was an unexpected error while deleting directories: #{e.class}")
|
66
66
|
# Ignore it
|
67
67
|
end
|
68
68
|
end
|
@@ -128,6 +128,8 @@ module Paperclip
|
|
128
128
|
# separate parts of your file name.
|
129
129
|
module S3
|
130
130
|
def self.extended base
|
131
|
+
warn('[DEPRECATION] S3 support through RightAWS is deprecated. S3 support will ' +
|
132
|
+
'be changed to AWS::S3 in a future version.')
|
131
133
|
require 'right_aws'
|
132
134
|
base.instance_eval do
|
133
135
|
@s3_credentials = parse_credentials(@options[:s3_credentials])
|
@@ -140,13 +142,13 @@ module Paperclip
|
|
140
142
|
@s3_host_alias = @options[:s3_host_alias]
|
141
143
|
@url = ":s3_path_url" unless @url.to_s.match(/^:s3.*url$/)
|
142
144
|
end
|
143
|
-
|
145
|
+
Paperclip.interpolates(:s3_alias_url) do |attachment, style|
|
144
146
|
"#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
145
147
|
end
|
146
|
-
|
148
|
+
Paperclip.interpolates(:s3_path_url) do |attachment, style|
|
147
149
|
"#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
148
150
|
end
|
149
|
-
|
151
|
+
Paperclip.interpolates(:s3_domain_url) do |attachment, style|
|
150
152
|
"#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
|
151
153
|
end
|
152
154
|
end
|
@@ -171,7 +173,7 @@ module Paperclip
|
|
171
173
|
|
172
174
|
def parse_credentials creds
|
173
175
|
creds = find_credentials(creds).stringify_keys
|
174
|
-
(creds[
|
176
|
+
(creds[RAILS_ENV] || creds).symbolize_keys
|
175
177
|
end
|
176
178
|
|
177
179
|
def exists?(style = default_style)
|
@@ -192,7 +194,7 @@ module Paperclip
|
|
192
194
|
def flush_writes #:nodoc:
|
193
195
|
@queued_for_write.each do |style, file|
|
194
196
|
begin
|
195
|
-
|
197
|
+
log("saving #{path(style)}")
|
196
198
|
key = s3_bucket.key(path(style))
|
197
199
|
key.data = file
|
198
200
|
key.put(nil, @s3_permissions, {'Content-type' => instance_read(:content_type)}.merge(@s3_headers))
|
@@ -206,7 +208,7 @@ module Paperclip
|
|
206
208
|
def flush_deletes #:nodoc:
|
207
209
|
@queued_for_delete.each do |path|
|
208
210
|
begin
|
209
|
-
|
211
|
+
log("deleting #{path}")
|
210
212
|
if file = s3_bucket.key(path)
|
211
213
|
file.delete
|
212
214
|
end
|
data/test/attachment_test.rb
CHANGED
@@ -5,6 +5,14 @@ class Dummy
|
|
5
5
|
end
|
6
6
|
|
7
7
|
class AttachmentTest < Test::Unit::TestCase
|
8
|
+
should "return the path based on the url by default" do
|
9
|
+
@attachment = attachment :url => "/:class/:id/:basename"
|
10
|
+
@model = @attachment.instance
|
11
|
+
@model.id = 1234
|
12
|
+
@model.avatar_file_name = "fake.jpg"
|
13
|
+
assert_equal "#{RAILS_ROOT}/public/fake_models/1234/fake", @attachment.path
|
14
|
+
end
|
15
|
+
|
8
16
|
context "Attachment default_options" do
|
9
17
|
setup do
|
10
18
|
rebuild_model
|
@@ -106,6 +114,20 @@ class AttachmentTest < Test::Unit::TestCase
|
|
106
114
|
end
|
107
115
|
end
|
108
116
|
|
117
|
+
context "An attachment with a default style and an extension interpolation" do
|
118
|
+
setup do
|
119
|
+
@attachment = attachment :path => ":basename.:extension",
|
120
|
+
:styles => { :default => ["100x100", :png] },
|
121
|
+
:default_style => :default
|
122
|
+
@file = StringIO.new("...")
|
123
|
+
@file.expects(:original_filename).returns("file.jpg")
|
124
|
+
end
|
125
|
+
should "return the right extension for the path" do
|
126
|
+
@attachment.assign(@file)
|
127
|
+
assert_equal "file.png", @attachment.path
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
109
131
|
context "An attachment with :convert_options" do
|
110
132
|
setup do
|
111
133
|
rebuild_model :styles => {
|
@@ -319,13 +341,13 @@ class AttachmentTest < Test::Unit::TestCase
|
|
319
341
|
setup { @dummy.avatar = @file }
|
320
342
|
|
321
343
|
before_should "call #make on all specified processors" do
|
322
|
-
expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny =>
|
344
|
+
expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
|
323
345
|
Paperclip::Thumbnail.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
|
324
346
|
Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
|
325
347
|
end
|
326
348
|
|
327
349
|
before_should "call #make with attachment passed as third argument" do
|
328
|
-
expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny =>
|
350
|
+
expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
|
329
351
|
Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
|
330
352
|
end
|
331
353
|
end
|
@@ -456,6 +478,7 @@ class AttachmentTest < Test::Unit::TestCase
|
|
456
478
|
|
457
479
|
context "An attachment" do
|
458
480
|
setup do
|
481
|
+
@old_defaults = Paperclip::Attachment.default_options.dup
|
459
482
|
Paperclip::Attachment.default_options.merge!({
|
460
483
|
:path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
|
461
484
|
})
|
@@ -468,7 +491,10 @@ class AttachmentTest < Test::Unit::TestCase
|
|
468
491
|
"5k.png"), 'rb')
|
469
492
|
end
|
470
493
|
|
471
|
-
teardown
|
494
|
+
teardown do
|
495
|
+
@file.close
|
496
|
+
Paperclip::Attachment.default_options.merge!(@old_defaults)
|
497
|
+
end
|
472
498
|
|
473
499
|
should "raise if there are not the correct columns when you try to assign" do
|
474
500
|
@other_attachment = Paperclip::Attachment.new(:not_here, @instance)
|
data/test/fixtures/s3.yml
CHANGED
data/test/geometry_test.rb
CHANGED
@@ -49,6 +49,15 @@ class GeometryTest < Test::Unit::TestCase
|
|
49
49
|
assert_nil @geo.modifier
|
50
50
|
end
|
51
51
|
|
52
|
+
should "treat x and X the same in geometries" do
|
53
|
+
@lower = Paperclip::Geometry.parse("123x456")
|
54
|
+
@upper = Paperclip::Geometry.parse("123X456")
|
55
|
+
assert_equal 123, @lower.width
|
56
|
+
assert_equal 123, @upper.width
|
57
|
+
assert_equal 456, @lower.height
|
58
|
+
assert_equal 456, @upper.height
|
59
|
+
end
|
60
|
+
|
52
61
|
['>', '<', '#', '@', '%', '^', '!', nil].each do |mod|
|
53
62
|
should "ensure the modifier #{mod.inspect} is preserved" do
|
54
63
|
assert @geo = Paperclip::Geometry.parse("123x456#{mod}")
|
data/test/helper.rb
CHANGED
@@ -17,6 +17,7 @@ end
|
|
17
17
|
|
18
18
|
ROOT = File.join(File.dirname(__FILE__), '..')
|
19
19
|
RAILS_ROOT = ROOT
|
20
|
+
RAILS_ENV = "test"
|
20
21
|
|
21
22
|
$LOAD_PATH << File.join(ROOT, 'lib')
|
22
23
|
$LOAD_PATH << File.join(ROOT, 'lib', 'paperclip')
|
@@ -25,8 +26,6 @@ require File.join(ROOT, 'lib', 'paperclip.rb')
|
|
25
26
|
|
26
27
|
require 'shoulda_macros/paperclip'
|
27
28
|
|
28
|
-
ENV['RAILS_ENV'] ||= 'test'
|
29
|
-
|
30
29
|
FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
|
31
30
|
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
32
31
|
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
@@ -71,7 +70,7 @@ def rebuild_class options = {}
|
|
71
70
|
end
|
72
71
|
|
73
72
|
def temporary_rails_env(new_env)
|
74
|
-
old_env =
|
73
|
+
old_env = Object.const_defined?("RAILS_ENV") ? RAILS_ENV : nil
|
75
74
|
silence_warnings do
|
76
75
|
Object.const_set("RAILS_ENV", new_env)
|
77
76
|
end
|
@@ -80,3 +79,22 @@ def temporary_rails_env(new_env)
|
|
80
79
|
Object.const_set("RAILS_ENV", old_env)
|
81
80
|
end
|
82
81
|
end
|
82
|
+
|
83
|
+
class FakeModel
|
84
|
+
attr_accessor :avatar_file_name,
|
85
|
+
:avatar_file_size,
|
86
|
+
:avatar_last_updated,
|
87
|
+
:avatar_content_type,
|
88
|
+
:id
|
89
|
+
|
90
|
+
def errors
|
91
|
+
@errors ||= []
|
92
|
+
end
|
93
|
+
|
94
|
+
def run_callbacks name, *args
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def attachment options
|
99
|
+
Paperclip::Attachment.new(:avatar, FakeModel.new, options)
|
100
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'test/helper'
|
2
|
+
|
3
|
+
class InterpolationsTest < Test::Unit::TestCase
|
4
|
+
should "return all methods but the infrastructure when sent #all" do
|
5
|
+
methods = Paperclip::Interpolations.all
|
6
|
+
assert ! methods.include?(:[])
|
7
|
+
assert ! methods.include?(:[]=)
|
8
|
+
assert ! methods.include?(:all)
|
9
|
+
methods.each do |m|
|
10
|
+
assert Paperclip::Interpolations.respond_to? m
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
should "return the RAILS_ROOT" do
|
15
|
+
assert_equal RAILS_ROOT, Paperclip::Interpolations.rails_root(:attachment, :style)
|
16
|
+
end
|
17
|
+
|
18
|
+
should "return the RAILS_ENV" do
|
19
|
+
assert_equal RAILS_ENV, Paperclip::Interpolations.rails_env(:attachment, :style)
|
20
|
+
end
|
21
|
+
|
22
|
+
should "return the class of the instance" do
|
23
|
+
attachment = mock
|
24
|
+
attachment.expects(:instance).returns(attachment)
|
25
|
+
attachment.expects(:class).returns("Thing")
|
26
|
+
assert_equal "things", Paperclip::Interpolations.class(attachment, :style)
|
27
|
+
end
|
28
|
+
|
29
|
+
should "return the basename of the file" do
|
30
|
+
attachment = mock
|
31
|
+
attachment.expects(:original_filename).returns("one.jpg").times(2)
|
32
|
+
assert_equal "one", Paperclip::Interpolations.basename(attachment, :style)
|
33
|
+
end
|
34
|
+
|
35
|
+
should "return the extension of the file" do
|
36
|
+
attachment = mock
|
37
|
+
attachment.expects(:original_filename).returns("one.jpg")
|
38
|
+
attachment.expects(:styles).returns({})
|
39
|
+
assert_equal "jpg", Paperclip::Interpolations.extension(attachment, :style)
|
40
|
+
end
|
41
|
+
|
42
|
+
should "return the extension of the file as the format if defined in the style" do
|
43
|
+
attachment = mock
|
44
|
+
attachment.expects(:original_filename).never
|
45
|
+
attachment.expects(:styles).returns({:style => {:format => "png"}})
|
46
|
+
assert_equal "png", Paperclip::Interpolations.extension(attachment, :style)
|
47
|
+
end
|
48
|
+
|
49
|
+
should "return the id of the attachment" do
|
50
|
+
attachment = mock
|
51
|
+
attachment.expects(:id).returns(23)
|
52
|
+
attachment.expects(:instance).returns(attachment)
|
53
|
+
assert_equal 23, Paperclip::Interpolations.id(attachment, :style)
|
54
|
+
end
|
55
|
+
|
56
|
+
should "return the partitioned id of the attachment" do
|
57
|
+
attachment = mock
|
58
|
+
attachment.expects(:id).returns(23)
|
59
|
+
attachment.expects(:instance).returns(attachment)
|
60
|
+
assert_equal "000/000/023", Paperclip::Interpolations.id_partition(attachment, :style)
|
61
|
+
end
|
62
|
+
|
63
|
+
should "return the name of the attachment" do
|
64
|
+
attachment = mock
|
65
|
+
attachment.expects(:name).returns("file")
|
66
|
+
assert_equal "files", Paperclip::Interpolations.attachment(attachment, :style)
|
67
|
+
end
|
68
|
+
|
69
|
+
should "return the style" do
|
70
|
+
assert_equal :style, Paperclip::Interpolations.style(:attachment, :style)
|
71
|
+
end
|
72
|
+
|
73
|
+
should "return the default style" do
|
74
|
+
attachment = mock
|
75
|
+
attachment.expects(:default_style).returns(:default_style)
|
76
|
+
assert_equal :default_style, Paperclip::Interpolations.style(attachment, nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
should "reinterpolate :url" do
|
80
|
+
attachment = mock
|
81
|
+
attachment.expects(:options).returns({:url => ":id"})
|
82
|
+
attachment.expects(:url).with(:style, false).returns("1234")
|
83
|
+
assert_equal "1234", Paperclip::Interpolations.url(attachment, :style)
|
84
|
+
end
|
85
|
+
|
86
|
+
should "raise if infinite loop detcted reinterpolating :url" do
|
87
|
+
attachment = mock
|
88
|
+
attachment.expects(:options).returns({:url => ":url"})
|
89
|
+
assert_raises(Paperclip::InfiniteInterpolationError){ Paperclip::Interpolations.url(attachment, :style) }
|
90
|
+
end
|
91
|
+
|
92
|
+
should "return the filename as basename.extension" do
|
93
|
+
attachment = mock
|
94
|
+
attachment.expects(:styles).returns({})
|
95
|
+
attachment.expects(:original_filename).returns("one.jpg").times(3)
|
96
|
+
assert_equal "one.jpg", Paperclip::Interpolations.filename(attachment, :style)
|
97
|
+
end
|
98
|
+
|
99
|
+
should "return the filename as basename.extension when format supplied" do
|
100
|
+
attachment = mock
|
101
|
+
attachment.expects(:styles).returns({:style => {:format => :png}})
|
102
|
+
attachment.expects(:original_filename).returns("one.jpg").times(2)
|
103
|
+
assert_equal "one.png", Paperclip::Interpolations.filename(attachment, :style)
|
104
|
+
end
|
105
|
+
|
106
|
+
should "return the timestamp" do
|
107
|
+
now = Time.now
|
108
|
+
attachment = mock
|
109
|
+
attachment.expects(:instance_read).with(:updated_at).returns(now)
|
110
|
+
assert_equal now.to_s, Paperclip::Interpolations.timestamp(attachment, :style)
|
111
|
+
end
|
112
|
+
|
113
|
+
should "call all expected interpolations with the given arguments" do
|
114
|
+
Paperclip::Interpolations.expects(:id).with(:attachment, :style).returns(1234)
|
115
|
+
Paperclip::Interpolations.expects(:attachment).with(:attachment, :style).returns("attachments")
|
116
|
+
Paperclip::Interpolations.expects(:notreal).never
|
117
|
+
value = Paperclip::Interpolations.interpolate(":notreal/:id/:attachment", :attachment, :style)
|
118
|
+
assert_equal ":notreal/1234/attachments", value
|
119
|
+
end
|
120
|
+
end
|
data/test/paperclip_test.rb
CHANGED
@@ -30,6 +30,14 @@ class PaperclipTest < Test::Unit::TestCase
|
|
30
30
|
Paperclip.expects(:"`").with("convert one.jpg two.jpg 2>/dev/null")
|
31
31
|
Paperclip.run("convert", "one.jpg two.jpg")
|
32
32
|
end
|
33
|
+
|
34
|
+
should "log the command when :log_command is set" do
|
35
|
+
Paperclip.options[:log_command] = true
|
36
|
+
Paperclip.expects(:bit_bucket).returns("/dev/null")
|
37
|
+
Paperclip.expects(:log).with("this is the command 2>/dev/null")
|
38
|
+
Paperclip.expects(:"`").with("this is the command 2>/dev/null")
|
39
|
+
Paperclip.run("this","is the command")
|
40
|
+
end
|
33
41
|
end
|
34
42
|
|
35
43
|
should "raise when sent #processor and the name of a class that exists but isn't a subclass of Processor" do
|
@@ -44,6 +52,18 @@ class PaperclipTest < Test::Unit::TestCase
|
|
44
52
|
assert_equal ::Paperclip::Thumbnail, Paperclip.processor(:thumbnail)
|
45
53
|
end
|
46
54
|
|
55
|
+
should "call a proc sent to check_guard" do
|
56
|
+
@dummy = Dummy.new
|
57
|
+
@dummy.expects(:one).returns(:one)
|
58
|
+
assert_equal :one, @dummy.avatar.send(:check_guard, lambda{|x| x.one })
|
59
|
+
end
|
60
|
+
|
61
|
+
should "call a method name sent to check_guard" do
|
62
|
+
@dummy = Dummy.new
|
63
|
+
@dummy.expects(:one).returns(:one)
|
64
|
+
assert_equal :one, @dummy.avatar.send(:check_guard, :one)
|
65
|
+
end
|
66
|
+
|
47
67
|
context "Paperclip.bit_bucket" do
|
48
68
|
context "on systems without /dev/null" do
|
49
69
|
setup do
|
@@ -167,6 +187,44 @@ class PaperclipTest < Test::Unit::TestCase
|
|
167
187
|
end
|
168
188
|
end
|
169
189
|
|
190
|
+
context "a validation with an if guard clause" do
|
191
|
+
setup do
|
192
|
+
Dummy.send(:"validates_attachment_presence", :avatar, :if => lambda{|i| i.foo })
|
193
|
+
@dummy = Dummy.new
|
194
|
+
end
|
195
|
+
|
196
|
+
should "attempt validation if the guard returns true" do
|
197
|
+
@dummy.expects(:foo).returns(true)
|
198
|
+
@dummy.avatar.expects(:validate_presence).returns(nil)
|
199
|
+
@dummy.valid?
|
200
|
+
end
|
201
|
+
|
202
|
+
should "not attempt validation if the guard returns false" do
|
203
|
+
@dummy.expects(:foo).returns(false)
|
204
|
+
@dummy.avatar.expects(:validate_presence).never
|
205
|
+
@dummy.valid?
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "a validation with an unless guard clause" do
|
210
|
+
setup do
|
211
|
+
Dummy.send(:"validates_attachment_presence", :avatar, :unless => lambda{|i| i.foo })
|
212
|
+
@dummy = Dummy.new
|
213
|
+
end
|
214
|
+
|
215
|
+
should "attempt validation if the guard returns true" do
|
216
|
+
@dummy.expects(:foo).returns(false)
|
217
|
+
@dummy.avatar.expects(:validate_presence).returns(nil)
|
218
|
+
@dummy.valid?
|
219
|
+
end
|
220
|
+
|
221
|
+
should "not attempt validation if the guard returns false" do
|
222
|
+
@dummy.expects(:foo).returns(true)
|
223
|
+
@dummy.avatar.expects(:validate_presence).never
|
224
|
+
@dummy.valid?
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
170
228
|
def self.should_validate validation, options, valid_file, invalid_file
|
171
229
|
context "with #{validation} validation and #{options.inspect} options" do
|
172
230
|
setup do
|
data/test/storage_test.rb
CHANGED
@@ -10,29 +10,29 @@ class StorageTest < Test::Unit::TestCase
|
|
10
10
|
@dummy = Dummy.new
|
11
11
|
@avatar = @dummy.avatar
|
12
12
|
|
13
|
-
@current_env =
|
13
|
+
@current_env = RAILS_ENV
|
14
14
|
end
|
15
15
|
|
16
16
|
teardown do
|
17
|
-
|
17
|
+
Object.const_set("RAILS_ENV", @current_env)
|
18
18
|
end
|
19
19
|
|
20
20
|
should "get the correct credentials when RAILS_ENV is production" do
|
21
|
-
|
21
|
+
Object.const_set('RAILS_ENV', "production")
|
22
22
|
assert_equal({:key => "12345"},
|
23
23
|
@avatar.parse_credentials('production' => {:key => '12345'},
|
24
24
|
:development => {:key => "54321"}))
|
25
25
|
end
|
26
26
|
|
27
27
|
should "get the correct credentials when RAILS_ENV is development" do
|
28
|
-
|
28
|
+
Object.const_set('RAILS_ENV', "development")
|
29
29
|
assert_equal({:key => "54321"},
|
30
30
|
@avatar.parse_credentials('production' => {:key => '12345'},
|
31
31
|
:development => {:key => "54321"}))
|
32
32
|
end
|
33
33
|
|
34
34
|
should "return the argument if the key does not exist" do
|
35
|
-
|
35
|
+
Object.const_set('RAILS_ENV', "not really an env")
|
36
36
|
assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345"))
|
37
37
|
end
|
38
38
|
end
|
@@ -94,13 +94,18 @@ class StorageTest < Test::Unit::TestCase
|
|
94
94
|
:development => { :bucket => "dev_bucket" }
|
95
95
|
}
|
96
96
|
@dummy = Dummy.new
|
97
|
+
@old_env = RAILS_ENV
|
97
98
|
end
|
98
99
|
|
99
|
-
|
100
|
+
teardown{ Object.const_set("RAILS_ENV", @old_env) }
|
101
|
+
|
102
|
+
should "get the right bucket in production" do
|
103
|
+
Object.const_set("RAILS_ENV", "production")
|
100
104
|
assert_equal "prod_bucket", @dummy.avatar.bucket_name
|
101
105
|
end
|
102
106
|
|
103
|
-
should "get the right bucket in development"
|
107
|
+
should "get the right bucket in development" do
|
108
|
+
Object.const_set("RAILS_ENV", "development")
|
104
109
|
assert_equal "dev_bucket", @dummy.avatar.bucket_name
|
105
110
|
end
|
106
111
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paperclip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Yurek
|
@@ -9,19 +9,9 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-15 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
name: right_aws
|
17
|
-
type: :runtime
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: "0"
|
24
|
-
version:
|
25
15
|
- !ruby/object:Gem::Dependency
|
26
16
|
name: thoughtbot-shoulda
|
27
17
|
type: :development
|
@@ -61,6 +51,7 @@ files:
|
|
61
51
|
- lib/paperclip/attachment.rb
|
62
52
|
- lib/paperclip/callback_compatability.rb
|
63
53
|
- lib/paperclip/geometry.rb
|
54
|
+
- lib/paperclip/interpolations.rb
|
64
55
|
- lib/paperclip/iostream.rb
|
65
56
|
- lib/paperclip/matchers/have_attached_file_matcher.rb
|
66
57
|
- lib/paperclip/matchers/validate_attachment_content_type_matcher.rb
|
@@ -75,7 +66,6 @@ files:
|
|
75
66
|
- tasks/paperclip_tasks.rake
|
76
67
|
- test/attachment_test.rb
|
77
68
|
- test/database.yml
|
78
|
-
- test/debug.log
|
79
69
|
- test/fixtures/12k.png
|
80
70
|
- test/fixtures/50x50.png
|
81
71
|
- test/fixtures/5k.png
|
@@ -86,6 +76,7 @@ files:
|
|
86
76
|
- test/geometry_test.rb
|
87
77
|
- test/helper.rb
|
88
78
|
- test/integration_test.rb
|
79
|
+
- test/interpolations_test.rb
|
89
80
|
- test/iostream_test.rb
|
90
81
|
- test/matchers/have_attached_file_matcher_test.rb
|
91
82
|
- test/matchers/validate_attachment_content_type_matcher_test.rb
|
@@ -93,10 +84,8 @@ files:
|
|
93
84
|
- test/matchers/validate_attachment_size_matcher_test.rb
|
94
85
|
- test/paperclip_test.rb
|
95
86
|
- test/processor_test.rb
|
96
|
-
- test/s3.yml
|
97
87
|
- test/storage_test.rb
|
98
88
|
- test/thumbnail_test.rb
|
99
|
-
- test/tmp/storage.txt
|
100
89
|
- shoulda_macros/paperclip.rb
|
101
90
|
has_rdoc: true
|
102
91
|
homepage: http://www.thoughtbot.com/projects/paperclip
|
data/test/debug.log
DELETED
File without changes
|
data/test/s3.yml
DELETED
File without changes
|
data/test/tmp/storage.txt
DELETED
File without changes
|