paperclip 2.3.8 → 2.3.9

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.

Files changed (59) hide show
  1. data/{README.rdoc → README.md} +104 -54
  2. data/lib/paperclip.rb +2 -0
  3. data/lib/paperclip/attachment.rb +67 -38
  4. data/lib/paperclip/command_line.rb +7 -1
  5. data/lib/paperclip/interpolations.rb +17 -1
  6. data/lib/paperclip/storage.rb +1 -0
  7. data/lib/paperclip/storage/fog.rb +98 -0
  8. data/lib/paperclip/storage/s3.rb +5 -5
  9. data/lib/paperclip/version.rb +1 -1
  10. data/test/attachment_test.rb +117 -0
  11. data/test/command_line_test.rb +5 -0
  12. data/test/fixtures/uppercase.PNG +0 -0
  13. data/test/fog_test.rb +107 -0
  14. data/test/helper.rb +1 -1
  15. data/test/integration_test.rb +88 -0
  16. data/test/interpolations_test.rb +17 -1
  17. data/test/paperclip_test.rb +30 -0
  18. data/test/storage_test.rb +23 -0
  19. metadata +47 -80
  20. data/lib/paperclip.rbc +0 -5202
  21. data/lib/paperclip/attachment.rbc +0 -6393
  22. data/lib/paperclip/callback_compatability.rbc +0 -1649
  23. data/lib/paperclip/command_line.rbc +0 -1952
  24. data/lib/paperclip/geometry.rbc +0 -2141
  25. data/lib/paperclip/glue.rbc +0 -685
  26. data/lib/paperclip/interpolations.rbc +0 -2205
  27. data/lib/paperclip/iostream.rbc +0 -905
  28. data/lib/paperclip/matchers.rbc +0 -267
  29. data/lib/paperclip/matchers/have_attached_file_matcher.rbc +0 -1258
  30. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rbc +0 -1660
  31. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rbc +0 -1304
  32. data/lib/paperclip/matchers/validate_attachment_size_matcher.rbc +0 -2160
  33. data/lib/paperclip/processor.rbc +0 -911
  34. data/lib/paperclip/railtie.rbc +0 -680
  35. data/lib/paperclip/storage.rbc +0 -69
  36. data/lib/paperclip/storage/filesystem.rbc +0 -1402
  37. data/lib/paperclip/storage/s3.rbc +0 -3489
  38. data/lib/paperclip/style.rbc +0 -1571
  39. data/lib/paperclip/thumbnail.rbc +0 -1648
  40. data/lib/paperclip/upfile.rbc +0 -1619
  41. data/lib/paperclip/version.rbc +0 -176
  42. data/shoulda_macros/paperclip.rbc +0 -2415
  43. data/test/attachment_test.rbc +0 -22952
  44. data/test/command_line_test.rbc +0 -4307
  45. data/test/geometry_test.rbc +0 -5181
  46. data/test/helper.rbc +0 -3971
  47. data/test/integration_test.rbc +0 -13137
  48. data/test/interpolations_test.rbc +0 -3432
  49. data/test/iostream_test.rbc +0 -1889
  50. data/test/matchers/have_attached_file_matcher_test.rbc +0 -622
  51. data/test/matchers/validate_attachment_content_type_matcher_test.rbc +0 -1174
  52. data/test/matchers/validate_attachment_presence_matcher_test.rbc +0 -622
  53. data/test/matchers/validate_attachment_size_matcher_test.rbc +0 -1658
  54. data/test/paperclip_test.rbc +0 -7407
  55. data/test/processor_test.rbc +0 -314
  56. data/test/storage_test.rbc +0 -10294
  57. data/test/style_test.rbc +0 -3752
  58. data/test/thumbnail_test.rbc +0 -6803
  59. data/test/upfile_test.rbc +0 -1635
@@ -1,4 +1,5 @@
1
- =Paperclip
1
+ Paperclip
2
+ =========
2
3
 
3
4
  Paperclip is intended as an easy file attachment library for ActiveRecord. The
4
5
  intent behind it was to keep setup as easy as possible and to treat files as
@@ -12,64 +13,92 @@ packages). Attached files are saved to the filesystem and referenced in the
12
13
  browser by an easily understandable specification, which has sensible and
13
14
  useful defaults.
14
15
 
15
- See the documentation for +has_attached_file+ in Paperclip::ClassMethods for
16
+ See the documentation for `has_attached_file` in Paperclip::ClassMethods for
16
17
  more detailed options.
17
18
 
18
- The complete RDoc[http://rdoc.info/projects/thoughtbot/paperclip] is online.
19
+ The complete [RDoc](http://rdoc.info/gems/paperclip) is online.
19
20
 
20
- ==Quick Start
21
+ Requirements
22
+ ------------
21
23
 
22
- In your model:
24
+ ImageMagick must be installed and Paperclip must have access to it. To ensure
25
+ that it does, on your command line, run `which convert` (one of the ImageMagick
26
+ utilities). This will give you the path where that utility is installed. For
27
+ example, it might return `/usr/local/bin/convert`.
23
28
 
24
- class User < ActiveRecord::Base
25
- has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
26
- end
29
+ Then, in your environment config file, let Paperclip know to look there by adding that
30
+ directory to its path.
27
31
 
28
- In your migrations:
32
+ In development mode, you might add this line to `config/environments/development.rb)`:
33
+
34
+ Paperclip.options[:command_path] = "/usr/local/bin/"
35
+
36
+ Installation
37
+ ------------
38
+
39
+ Include the gem in your Gemfile:
40
+
41
+ gem "paperclip", "~> 2.3"
42
+
43
+ Or as a plugin:
44
+
45
+ ruby script/plugin install git://github.com/thoughtbot/paperclip.git
46
+
47
+ Quick Start
48
+ -----------
49
+
50
+ In your model:
29
51
 
30
- class AddAvatarColumnsToUser < ActiveRecord::Migration
31
- def self.up
32
- add_column :users, :avatar_file_name, :string
33
- add_column :users, :avatar_content_type, :string
34
- add_column :users, :avatar_file_size, :integer
35
- add_column :users, :avatar_updated_at, :datetime
52
+ class User < ActiveRecord::Base
53
+ has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
36
54
  end
37
55
 
38
- def self.down
39
- remove_column :users, :avatar_file_name
40
- remove_column :users, :avatar_content_type
41
- remove_column :users, :avatar_file_size
42
- remove_column :users, :avatar_updated_at
56
+ In your migrations:
57
+
58
+ class AddAvatarColumnsToUser < ActiveRecord::Migration
59
+ def self.up
60
+ add_column :users, :avatar_file_name, :string
61
+ add_column :users, :avatar_content_type, :string
62
+ add_column :users, :avatar_file_size, :integer
63
+ add_column :users, :avatar_updated_at, :datetime
64
+ end
65
+
66
+ def self.down
67
+ remove_column :users, :avatar_file_name
68
+ remove_column :users, :avatar_content_type
69
+ remove_column :users, :avatar_file_size
70
+ remove_column :users, :avatar_updated_at
71
+ end
43
72
  end
44
- end
45
73
 
46
74
  In your edit and new views:
47
75
 
48
- <% form_for :user, @user, :url => user_path, :html => { :multipart => true } do |form| %>
49
- <%= form.file_field :avatar %>
50
- <% end %>
76
+ <% form_for :user, @user, :url => user_path, :html => { :multipart => true } do |form| %>
77
+ <%= form.file_field :avatar %>
78
+ <% end %>
51
79
 
52
80
  In your controller:
53
81
 
54
- def create
55
- @user = User.create( params[:user] )
56
- end
82
+ def create
83
+ @user = User.create( params[:user] )
84
+ end
57
85
 
58
86
  In your show view:
59
87
 
60
- <%= image_tag @user.avatar.url %>
61
- <%= image_tag @user.avatar.url(:medium) %>
62
- <%= image_tag @user.avatar.url(:thumb) %>
88
+ <%= image_tag @user.avatar.url %>
89
+ <%= image_tag @user.avatar.url(:medium) %>
90
+ <%= image_tag @user.avatar.url(:thumb) %>
63
91
 
64
- ==Usage
92
+ Usage
93
+ -----
65
94
 
66
95
  The basics of paperclip are quite simple: Declare that your model has an
67
96
  attachment with the has_attached_file method, and give it a name. Paperclip
68
97
  will wrap up up to four attributes (all prefixed with that attachment's name,
69
98
  so you can have multiple attachments per model if you wish) and give the a
70
- friendly front end. The attributes are <attachment>_file_name,
71
- <attachment>_file_size, <attachment>_content_type, and <attachment>_updated_at.
72
- Only <attachment>_file_name is required for paperclip to operate. More
99
+ friendly front end. The attributes are `<attachment>_file_name`,
100
+ `<attachment>_file_size`, `<attachment>_content_type`, and `<attachment>_updated_at`.
101
+ Only `<attachment>_file_name` is required for paperclip to operate. More
73
102
  information about the options to has_attached_file is available in the
74
103
  documentation of Paperclip::ClassMethods.
75
104
 
@@ -77,7 +106,8 @@ Attachments can be validated with Paperclip's validation methods,
77
106
  validates_attachment_presence, validates_attachment_content_type, and
78
107
  validates_attachment_size.
79
108
 
80
- ==Storage
109
+ Storage
110
+ -------
81
111
 
82
112
  The files that are assigned as attachments are, by default, placed in the
83
113
  directory specified by the :path option to has_attached_file. By default, this
@@ -87,10 +117,10 @@ public/system directory is symlinked to the app's shared directory, meaning it
87
117
  will survive between deployments. For example, using that :path, you may have a
88
118
  file at
89
119
 
90
- /data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png
120
+ /data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png
91
121
 
92
- NOTE: This is a change from previous versions of Paperclip, but is overall a
93
- safer choice for the default file store.
122
+ _NOTE: This is a change from previous versions of Paperclip, but is overall a
123
+ safer choice for the default file store._
94
124
 
95
125
  You may also choose to store your files using Amazon's S3 service. You can find
96
126
  more information about S3 storage at the description for
@@ -103,7 +133,8 @@ both the :path and :url options in order to make sure the files are unavailable
103
133
  to the public. Both :path and :url allow the same set of interpolated
104
134
  variables.
105
135
 
106
- ==Post Processing
136
+ Post Processing
137
+ ---------------
107
138
 
108
139
  Paperclip supports an extensible selection of post-processors. When you define
109
140
  a set of styles for an attachment, by default it is expected that those
@@ -114,8 +145,8 @@ your Rails app's lib/paperclip_processors directory is automatically loaded by
114
145
  paperclip, allowing you to easily define custom processors. You can specify a
115
146
  processor with the :processors option to has_attached_file:
116
147
 
117
- has_attached_file :scan, :styles => { :text => { :quality => :better } },
118
- :processors => [:ocr]
148
+ has_attached_file :scan, :styles => { :text => { :quality => :better } },
149
+ :processors => [:ocr]
119
150
 
120
151
  This would load the hypothetical class Paperclip::Ocr, which would have the
121
152
  hash "{ :quality => :better }" passed to it along with the uploaded file. For
@@ -125,7 +156,7 @@ The default processor is Paperclip::Thumbnail. For backwards compatability
125
156
  reasons, you can pass a single geometry string or an array containing a
126
157
  geometry and a format, which the file will be converted to, like so:
127
158
 
128
- has_attached_file :avatar, :styles => { :thumb => ["32x32#", :png] }
159
+ has_attached_file :avatar, :styles => { :thumb => ["32x32#", :png] }
129
160
 
130
161
  This will convert the "thumb" style to a 32x32 square in png format, regardless
131
162
  of what was uploaded. If the format is not specified, it is kept the same (i.e.
@@ -137,39 +168,42 @@ be given the result of the previous processor's execution. All processors will
137
168
  receive the same parameters, which are what you define in the :styles hash.
138
169
  For example, assuming we had this definition:
139
170
 
140
- has_attached_file :scan, :styles => { :text => { :quality => :better } },
141
- :processors => [:rotator, :ocr]
171
+ has_attached_file :scan, :styles => { :text => { :quality => :better } },
172
+ :processors => [:rotator, :ocr]
142
173
 
143
174
  then both the :rotator processor and the :ocr processor would receive the
144
175
  options "{ :quality => :better }". This parameter may not mean anything to one
145
176
  or more or the processors, and they are expected to ignore it.
146
177
 
147
- NOTE: Because processors operate by turning the original attachment into the
148
- styles, no processors will be run if there are no styles defined.
178
+ _NOTE: Because processors operate by turning the original attachment into the
179
+ styles, no processors will be run if there are no styles defined._
149
180
 
150
- ==Events
181
+ Events
182
+ ------
151
183
 
152
184
  Before and after the Post Processing step, Paperclip calls back to the model
153
185
  with a few callbacks, allowing the model to change or cancel the processing
154
- step. The callbacks are "before_post_process" and "after_post_process" (which
186
+ step. The callbacks are `before_post_process` and `after_post_process` (which
155
187
  are called before and after the processing of each attachment), and the
156
- attachment-specific "before_<attachment>_post_process" and
157
- "after_<attachment>_post_process". The callbacks are intended to be as close to
188
+ attachment-specific `before_<attachment>_post_process` and
189
+ `after_<attachment>_post_process`. The callbacks are intended to be as close to
158
190
  normal ActiveRecord callbacks as possible, so if you return false (specifically
159
191
  - returning nil is not the same) in a before_ filter, the post processing step
160
192
  will halt. Returning false in an after_ filter will not halt anything, but you
161
193
  can access the model and the attachment if necessary.
162
194
 
163
- NOTE: Post processing will not even *start* if the attachment is not valid
195
+ _NOTE: Post processing will not even *start* if the attachment is not valid
164
196
  according to the validations. Your callbacks and processors will *only* be
165
- called with valid attachments.
197
+ called with valid attachments._
166
198
 
167
- ==Testing
199
+ Testing
200
+ -------
168
201
 
169
202
  Paperclip provides rspec-compatible matchers for testing attachments. See the
170
203
  documentation on Paperclip::Shoulda::Matchers for more information.
171
204
 
172
- ==Contributing
205
+ Contributing
206
+ ------------
173
207
 
174
208
  If you'd like to contribute a feature or bugfix: Thanks! To make sure your
175
209
  fix/feature has a high chance of being included, please read the following
@@ -180,3 +214,19 @@ guidelines:
180
214
  2. Make sure there are tests! We will not accept any patch that is not tested.
181
215
  It's a rare time when explicit tests aren't needed. If you have questions
182
216
  about writing tests for paperclip, please ask the mailing list.
217
+
218
+ Credits
219
+ -------
220
+
221
+ ![thoughtbot](http://thoughtbot.com/images/tm/logo.png)
222
+
223
+ Paperclip is maintained and funded by [thoughtbot, inc](http://thoughtbot.com/community)
224
+
225
+ Thank you to all [the contributors](https://github.com/thoughtbot/paperclip/contributors)!
226
+
227
+ The names and logos for thoughtbot are trademarks of thoughtbot, inc.
228
+
229
+ License
230
+ -------
231
+
232
+ Paperclip is Copyright © 2008-2011 thoughtbot. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
@@ -276,6 +276,7 @@ module Paperclip
276
276
  max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
277
277
  range = (min..max)
278
278
  message = options[:message] || "file size must be between :min and :max bytes."
279
+ message = message.call if message.respond_to?(:call)
279
280
  message = message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
280
281
 
281
282
  validates_inclusion_of :"#{name}_file_size",
@@ -330,6 +331,7 @@ module Paperclip
330
331
  if !allowed_types.any?{|t| t === value } && !(value.nil? || value.blank?)
331
332
  if record.errors.method(:add).arity == -2
332
333
  message = options[:message] || "is not one of #{allowed_types.join(", ")}"
334
+ message = message.call if message.respond_to?(:call)
333
335
  record.errors.add(:"#{name}_content_type", message)
334
336
  else
335
337
  record.errors.add(:"#{name}_content_type", :inclusion, :default => options[:message], :value => value)
@@ -8,20 +8,24 @@ module Paperclip
8
8
 
9
9
  def self.default_options
10
10
  @default_options ||= {
11
- :url => "/system/:attachment/:id/:style/:filename",
12
- :path => ":rails_root/public:url",
13
- :styles => {},
14
- :processors => [:thumbnail],
15
- :convert_options => {},
16
- :default_url => "/:attachment/:style/missing.png",
17
- :default_style => :original,
18
- :storage => :filesystem,
19
- :use_timestamp => true,
20
- :whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails]
11
+ :url => "/system/:attachment/:id/:style/:filename",
12
+ :path => ":rails_root/public:url",
13
+ :styles => {},
14
+ :processors => [:thumbnail],
15
+ :convert_options => {},
16
+ :default_url => "/:attachment/:style/missing.png",
17
+ :default_style => :original,
18
+ :storage => :filesystem,
19
+ :use_timestamp => true,
20
+ :whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails],
21
+ :use_default_time_zone => true,
22
+ :hash_digest => "SHA1",
23
+ :hash_data => ":class/:attachment/:id/:style/:updated_at"
21
24
  }
22
25
  end
23
26
 
24
27
  attr_reader :name, :instance, :default_style, :convert_options, :queued_for_write, :whiny, :options
28
+ attr_accessor :post_processing
25
29
 
26
30
  # Creates an Attachment object. +name+ is the name of the attachment,
27
31
  # +instance+ is the ActiveRecord object instance it's attached to, and
@@ -32,24 +36,29 @@ module Paperclip
32
36
 
33
37
  options = self.class.default_options.merge(options)
34
38
 
35
- @url = options[:url]
36
- @url = @url.call(self) if @url.is_a?(Proc)
37
- @path = options[:path]
38
- @path = @path.call(self) if @path.is_a?(Proc)
39
- @styles = options[:styles]
40
- @normalized_styles = nil
41
- @default_url = options[:default_url]
42
- @default_style = options[:default_style]
43
- @storage = options[:storage]
44
- @use_timestamp = options[:use_timestamp]
45
- @whiny = options[:whiny_thumbnails] || options[:whiny]
46
- @convert_options = options[:convert_options]
47
- @processors = options[:processors]
48
- @options = options
49
- @queued_for_delete = []
50
- @queued_for_write = {}
51
- @errors = {}
52
- @dirty = false
39
+ @url = options[:url]
40
+ @url = @url.call(self) if @url.is_a?(Proc)
41
+ @path = options[:path]
42
+ @path = @path.call(self) if @path.is_a?(Proc)
43
+ @styles = options[:styles]
44
+ @normalized_styles = nil
45
+ @default_url = options[:default_url]
46
+ @default_style = options[:default_style]
47
+ @storage = options[:storage]
48
+ @use_timestamp = options[:use_timestamp]
49
+ @whiny = options[:whiny_thumbnails] || options[:whiny]
50
+ @use_default_time_zone = options[:use_default_time_zone]
51
+ @hash_digest = options[:hash_digest]
52
+ @hash_data = options[:hash_data]
53
+ @hash_secret = options[:hash_secret]
54
+ @convert_options = options[:convert_options]
55
+ @processors = options[:processors]
56
+ @options = options
57
+ @post_processing = true
58
+ @queued_for_delete = []
59
+ @queued_for_write = {}
60
+ @errors = {}
61
+ @dirty = false
53
62
 
54
63
  initialize_storage
55
64
  end
@@ -98,7 +107,7 @@ module Paperclip
98
107
 
99
108
  @dirty = true
100
109
 
101
- post_process
110
+ post_process if @post_processing
102
111
 
103
112
  # Reset the file size if the original file was reprocessed.
104
113
  instance_write(:file_size, @queued_for_write[:original].size.to_i)
@@ -127,7 +136,7 @@ module Paperclip
127
136
  end
128
137
 
129
138
  # Alias to +url+
130
- def to_s style_name = nil
139
+ def to_s style_name = default_style
131
140
  url(style_name)
132
141
  end
133
142
 
@@ -197,6 +206,21 @@ module Paperclip
197
206
  time && time.to_f.to_i
198
207
  end
199
208
 
209
+ # The time zone to use for timestamp interpolation. Using the default
210
+ # time zone ensures that results are consistent across all threads.
211
+ def time_zone
212
+ @use_default_time_zone ? Time.zone_default : Time.zone
213
+ end
214
+
215
+ # Returns a unique hash suitable for obfuscating the URL of an otherwise
216
+ # publicly viewable attachment.
217
+ def hash(style_name = default_style)
218
+ raise ArgumentError, "Unable to generate hash without :hash_secret" unless @hash_secret
219
+ require 'openssl' unless defined?(OpenSSL)
220
+ data = interpolate(@hash_data, style_name)
221
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@hash_digest).new, @hash_secret, data)
222
+ end
223
+
200
224
  def generate_fingerprint(source)
201
225
  data = source.read
202
226
  source.rewind if source.respond_to?(:rewind)
@@ -219,7 +243,7 @@ module Paperclip
219
243
  # in the paperclip:refresh rake task and that's it. It will regenerate all
220
244
  # thumbnails forcefully, by reobtaining the original file and going through
221
245
  # the post-process again.
222
- def reprocess!
246
+ def reprocess!(*style_args)
223
247
  new_original = Tempfile.new("paperclip-reprocess")
224
248
  new_original.binmode
225
249
  if old_original = to_file(:original)
@@ -227,7 +251,7 @@ module Paperclip
227
251
  new_original.rewind
228
252
 
229
253
  @queued_for_write = { :original => new_original }
230
- post_process
254
+ post_process(*style_args)
231
255
 
232
256
  old_original.close if old_original.respond_to?(:close)
233
257
 
@@ -235,6 +259,9 @@ module Paperclip
235
259
  else
236
260
  true
237
261
  end
262
+ rescue Errno::EACCES => e
263
+ warn "#{e} - skipping file"
264
+ false
238
265
  end
239
266
 
240
267
  # Returns true if a file has been assigned.
@@ -299,21 +326,23 @@ module Paperclip
299
326
  [ style_options, all_options ].compact.join(" ")
300
327
  end
301
328
 
302
- def post_process #:nodoc:
329
+ def post_process(*style_args) #:nodoc:
303
330
  return if @queued_for_write[:original].nil?
304
331
  instance.run_paperclip_callbacks(:post_process) do
305
332
  instance.run_paperclip_callbacks(:"#{name}_post_process") do
306
- post_process_styles
333
+ post_process_styles(*style_args)
307
334
  end
308
335
  end
309
336
  end
310
337
 
311
- def post_process_styles #:nodoc:
338
+ def post_process_styles(*style_args) #:nodoc:
312
339
  styles.each do |name, style|
313
340
  begin
314
- raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
315
- @queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
316
- Paperclip.processor(processor).make(file, style.processor_options, self)
341
+ if style_args.empty? || style_args.include?(name)
342
+ raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
343
+ @queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
344
+ Paperclip.processor(processor).make(file, style.processor_options, self)
345
+ end
317
346
  end
318
347
  rescue PaperclipError => e
319
348
  log("An error was received while processing: #{e.inspect}")