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.

@@ -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 defaul file store.
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 extendible selection of post-processors. When you define
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 free to ignore it.
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 (and processors) will only be
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["README*",
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
@@ -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.8"
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
- # * whiny_thumbnails: Will raise an error if Paperclip cannot process thumbnails of
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
- :whiny_thumbnails => true,
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::Attachment.interpolations[key] = block
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/:basename.:extension". See
168
+ # "/system/:attachment/:id/:style/:filename". See
145
169
  # Paperclip::Attachment#interpolate for more information on variable interpolaton.
146
- # :url => "/:class/:attachment/:id/:style_:basename.:extension"
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
- # * +whiny_thumbnails+: Will raise an error if Paperclip cannot post_process an uploaded file due
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 => {}}.merge(options)
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] = lambda do |attachment, instance|
230
- if attachment.file? && !range.include?(attachment.size.to_i)
231
- message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
232
- end
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] = lambda do |attachment, instance|
245
- message unless attachment.file?
246
- end
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] = lambda do |attachment, instance|
264
- valid_types = [options[:content_type]].flatten
265
-
266
- unless attachment.original_filename.blank?
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
@@ -6,17 +6,18 @@ module Paperclip
6
6
 
7
7
  def self.default_options
8
8
  @default_options ||= {
9
- :url => "/system/:attachment/:id/:style/:basename.:extension",
10
- :path => ":rails_root/public/system/:attachment/:id/:style/:basename.:extension",
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
- %w(file_name).each do |field|
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 = nil #:nodoc:
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
- # A hash of procs that are run during the interpolation of a path or url.
196
- # A variable of the format :name will be replaced with the return value of
197
- # the proc named ":name". Each lambda takes the attachment and the current
198
- # style as arguments. This hash can be added to with your own proc if
199
- # necessary.
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
- @interpolations ||= {
202
- :rails_root => lambda{|attachment,style| RAILS_ROOT },
203
- :rails_env => lambda{|attachment,style| RAILS_ENV },
204
- :class => lambda do |attachment,style|
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 logger #:nodoc:
273
- instance.logger
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
- logger.info("[paperclip] #{message}") if logging?
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, block = validation
292
- errors[name] = block.call(self, instance) if block
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
- interpolations = self.class.interpolations.sort{|a,b| a.first.to_s <=> b.first.to_s }
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:
@@ -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
@@ -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 are
9
- # only two methods you must implement to properly be a subclass:
10
- # #initialize and #make. Initialize's arguments are the file that will
11
- # be operated on (which is an instance of File), and a hash of options
12
- # that were defined in has_attached_file's style hash.
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 do is return an instance of File (Tempfile is
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
@@ -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
- logger.info("[paperclip] saving #{path(style)}")
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
- logger.info("[paperclip] deleting #{path}")
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
- logger.info("[paperclip] There was an unexpected error while deleting directories: #{e.class}")
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
- base.class.interpolations[:s3_alias_url] = lambda do |attachment, style|
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
- base.class.interpolations[:s3_path_url] = lambda do |attachment, style|
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
- base.class.interpolations[:s3_domain_url] = lambda do |attachment, style|
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[ENV['RAILS_ENV']] || creds).symbolize_keys
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
- logger.info("[paperclip] saving #{path(style)}")
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
- logger.info("[paperclip] deleting #{path}")
211
+ log("deleting #{path}")
210
212
  if file = s3_bucket.key(path)
211
213
  file.delete
212
214
  end
@@ -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 => nil, :convert_options => ""})
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 => nil, :convert_options => ""})
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 { @file.close }
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)
@@ -0,0 +1,4 @@
1
+ development:
2
+ key: 54321
3
+ production:
4
+ key: 12345
@@ -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}")
@@ -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 = defined?(RAILS_ENV) ? RAILS_ENV : nil
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
@@ -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
@@ -10,29 +10,29 @@ class StorageTest < Test::Unit::TestCase
10
10
  @dummy = Dummy.new
11
11
  @avatar = @dummy.avatar
12
12
 
13
- @current_env = ENV['RAILS_ENV']
13
+ @current_env = RAILS_ENV
14
14
  end
15
15
 
16
16
  teardown do
17
- ENV['RAILS_ENV'] = @current_env
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
- ENV['RAILS_ENV'] = 'production'
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
- ENV['RAILS_ENV'] = 'development'
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
- ENV['RAILS_ENV'] = "not really an env"
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
- should "get the right bucket in production", :before => lambda{ ENV.expects(:[]).returns('production') } do
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", :before => lambda{ ENV.expects(:[]).returns('development') } do
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.8
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-04-02 00:00:00 -04:00
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
File without changes
File without changes
File without changes