cloudfuji_paperclip 2.4.6 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/.gitignore +2 -0
  2. data/.travis.yml +9 -6
  3. data/Appraisals +6 -6
  4. data/CONTRIBUTING.md +34 -2
  5. data/Gemfile +2 -0
  6. data/NEWS +90 -0
  7. data/README.md +62 -29
  8. data/RUNNING_TESTS.md +4 -0
  9. data/Rakefile +7 -2
  10. data/UPGRADING +14 -0
  11. data/features/basic_integration.feature +11 -7
  12. data/features/rake_tasks.feature +1 -1
  13. data/features/step_definitions/attachment_steps.rb +11 -2
  14. data/features/step_definitions/rails_steps.rb +17 -79
  15. data/features/support/env.rb +3 -0
  16. data/features/support/fakeweb.rb +7 -0
  17. data/features/support/file_helpers.rb +24 -0
  18. data/features/support/rails.rb +3 -3
  19. data/gemfiles/{rails3_1.gemfile → 3.0.gemfile} +3 -1
  20. data/gemfiles/{rails2.gemfile → 3.1.gemfile} +3 -1
  21. data/gemfiles/{rails3.gemfile → 3.2.gemfile} +3 -1
  22. data/images.rake +21 -0
  23. data/lib/cloudfuji_paperclip.rb +1 -0
  24. data/lib/generators/paperclip/paperclip_generator.rb +1 -2
  25. data/lib/paperclip.rb +54 -319
  26. data/lib/paperclip/attachment.rb +86 -107
  27. data/lib/paperclip/attachment_options.rb +9 -0
  28. data/lib/paperclip/callbacks.rb +30 -0
  29. data/lib/paperclip/errors.rb +27 -0
  30. data/lib/paperclip/geometry.rb +6 -4
  31. data/lib/paperclip/glue.rb +23 -0
  32. data/lib/paperclip/helpers.rb +71 -0
  33. data/lib/paperclip/instance_methods.rb +35 -0
  34. data/lib/paperclip/interpolations.rb +4 -4
  35. data/lib/paperclip/io_adapters/attachment_adapter.rb +69 -0
  36. data/lib/paperclip/io_adapters/file_adapter.rb +81 -0
  37. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
  38. data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
  39. data/lib/paperclip/io_adapters/registry.rb +32 -0
  40. data/lib/paperclip/io_adapters/stringio_adapter.rb +64 -0
  41. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +63 -0
  42. data/lib/paperclip/locales/en.yml +17 -0
  43. data/lib/paperclip/logger.rb +21 -0
  44. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +5 -5
  45. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +7 -7
  46. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +11 -11
  47. data/lib/paperclip/missing_attachment_styles.rb +6 -9
  48. data/lib/paperclip/processor.rb +32 -17
  49. data/lib/paperclip/railtie.rb +13 -17
  50. data/lib/paperclip/storage/filesystem.rb +4 -13
  51. data/lib/paperclip/storage/fog.rb +33 -24
  52. data/lib/paperclip/storage/s3.rb +36 -28
  53. data/lib/paperclip/tempfile.rb +41 -0
  54. data/lib/paperclip/thumbnail.rb +2 -3
  55. data/lib/paperclip/validators.rb +45 -0
  56. data/lib/paperclip/validators/attachment_content_type_validator.rb +54 -0
  57. data/lib/paperclip/validators/attachment_presence_validator.rb +26 -0
  58. data/lib/paperclip/validators/attachment_size_validator.rb +102 -0
  59. data/lib/paperclip/version.rb +1 -1
  60. data/lib/tasks/paperclip.rake +4 -12
  61. data/paperclip.gemspec +15 -5
  62. data/test/adapter_registry_test.rb +32 -0
  63. data/test/attachment_adapter_test.rb +51 -0
  64. data/test/attachment_options_test.rb +27 -0
  65. data/test/attachment_test.rb +130 -46
  66. data/test/file_adapter_test.rb +88 -0
  67. data/test/generator_test.rb +78 -0
  68. data/test/geometry_test.rb +5 -5
  69. data/test/helper.rb +21 -22
  70. data/test/identity_adapter_test.rb +8 -0
  71. data/test/integration_test.rb +55 -102
  72. data/test/interpolations_test.rb +15 -5
  73. data/test/matchers/validate_attachment_content_type_matcher_test.rb +23 -0
  74. data/test/matchers/validate_attachment_presence_matcher_test.rb +21 -0
  75. data/test/matchers/validate_attachment_size_matcher_test.rb +37 -2
  76. data/test/nil_adapter_test.rb +25 -0
  77. data/test/paperclip_missing_attachment_styles_test.rb +16 -0
  78. data/test/paperclip_test.rb +34 -183
  79. data/test/storage/filesystem_test.rb +27 -27
  80. data/test/storage/fog_test.rb +68 -12
  81. data/test/storage/s3_live_test.rb +79 -38
  82. data/test/storage/s3_test.rb +204 -34
  83. data/test/stringio_adapter_test.rb +42 -0
  84. data/test/thumbnail_test.rb +29 -8
  85. data/test/uploaded_file_adapter_test.rb +98 -0
  86. data/test/url_generator_test.rb +8 -8
  87. data/test/validators/attachment_content_type_validator_test.rb +192 -0
  88. data/test/validators/attachment_presence_validator_test.rb +85 -0
  89. data/test/validators/attachment_size_validator_test.rb +207 -0
  90. data/test/validators_test.rb +25 -0
  91. metadata +166 -59
  92. data/generators/paperclip/USAGE +0 -5
  93. data/generators/paperclip/paperclip_generator.rb +0 -27
  94. data/generators/paperclip/templates/paperclip_migration.rb.erb +0 -19
  95. data/init.rb +0 -4
  96. data/lib/paperclip/callback_compatibility.rb +0 -61
  97. data/lib/paperclip/iostream.rb +0 -45
  98. data/lib/paperclip/upfile.rb +0 -62
  99. data/rails/init.rb +0 -2
  100. data/test/.gitignore +0 -1
  101. data/test/fixtures/question?mark.png +0 -0
  102. data/test/iostream_test.rb +0 -71
  103. data/test/upfile_test.rb +0 -53
@@ -7,33 +7,32 @@ module Paperclip
7
7
  # when the model saves, deletes when the model is destroyed, and processes
8
8
  # the file upon assignment.
9
9
  class Attachment
10
- include IOStream
11
-
12
10
  def self.default_options
13
11
  @default_options ||= {
14
- :url => "/system/:attachment/:id/:style/:filename",
15
- :path => ":rails_root/public:url",
16
- :styles => {},
12
+ :convert_options => {},
13
+ :default_style => :original,
14
+ :default_url => "/:attachment/:style/missing.png",
15
+ :restricted_characters => /[&$+,\/:;=?@<>\[\]\{\}\|\\\^~%# ]/,
16
+ :hash_data => ":class/:attachment/:id/:style/:updated_at",
17
+ :hash_digest => "SHA1",
18
+ :interpolator => Paperclip::Interpolations,
17
19
  :only_process => [],
20
+ :path => ":rails_root/public:url",
21
+ :preserve_files => false,
18
22
  :processors => [:thumbnail],
19
- :convert_options => {},
20
23
  :source_file_options => {},
21
- :default_url => "/:attachment/:style/missing.png",
22
- :default_style => :original,
23
24
  :storage => :filesystem,
24
- :use_timestamp => true,
25
- :whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails],
25
+ :styles => {},
26
+ :url => "/system/:class/:attachment/:id_partition/:style/:filename",
27
+ :url_generator => Paperclip::UrlGenerator,
26
28
  :use_default_time_zone => true,
27
- :hash_digest => "SHA1",
28
- :hash_data => ":class/:attachment/:id/:style/:updated_at",
29
- :preserve_files => false,
30
- :interpolator => Paperclip::Interpolations,
31
- :url_generator => Paperclip::UrlGenerator
29
+ :use_timestamp => true,
30
+ :whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails]
32
31
  }
33
32
  end
34
33
 
35
- attr_reader :name, :instance, :default_style, :convert_options, :queued_for_write, :whiny, :options, :interpolator
36
- attr_reader :source_file_options, :whiny
34
+ attr_reader :name, :instance, :default_style, :convert_options, :queued_for_write, :whiny,
35
+ :options, :interpolator, :source_file_options, :whiny
37
36
  attr_accessor :post_processing
38
37
 
39
38
  # Creates an Attachment object. +name+ is the name of the attachment,
@@ -89,28 +88,16 @@ module Paperclip
89
88
  # new_user.avatar = old_user.avatar
90
89
  def assign uploaded_file
91
90
  ensure_required_accessors!
91
+ file = Paperclip.io_adapters.for(uploaded_file)
92
92
 
93
- if uploaded_file.is_a?(Paperclip::Attachment)
94
- uploaded_filename = uploaded_file.original_filename
95
- uploaded_file = uploaded_file.to_file(:original)
96
- close_uploaded_file = uploaded_file.respond_to?(:close)
97
- else
98
- instance_write(:uploaded_file, uploaded_file) if uploaded_file
99
- end
100
-
101
- return nil unless valid_assignment?(uploaded_file)
93
+ self.clear(*@options[:only_process])
94
+ return nil if file.nil?
102
95
 
103
- uploaded_file.binmode if uploaded_file.respond_to? :binmode
104
- self.clear
105
-
106
- return nil if uploaded_file.nil?
107
-
108
- uploaded_filename ||= uploaded_file.original_filename
109
- @queued_for_write[:original] = to_tempfile(uploaded_file)
110
- instance_write(:file_name, uploaded_filename.strip)
111
- instance_write(:content_type, uploaded_file.content_type.to_s.strip)
112
- instance_write(:file_size, uploaded_file.size.to_i)
113
- instance_write(:fingerprint, generate_fingerprint(uploaded_file))
96
+ @queued_for_write[:original] = file
97
+ instance_write(:file_name, cleanup_filename(file.original_filename))
98
+ instance_write(:content_type, file.content_type.to_s.strip)
99
+ instance_write(:file_size, file.size)
100
+ instance_write(:fingerprint, file.fingerprint) if instance_respond_to?(:fingerprint)
114
101
  instance_write(:updated_at, Time.now)
115
102
 
116
103
  @dirty = true
@@ -118,10 +105,8 @@ module Paperclip
118
105
  post_process(*@options[:only_process]) if post_processing
119
106
 
120
107
  # Reset the file size if the original file was reprocessed.
121
- instance_write(:file_size, @queued_for_write[:original].size.to_i)
122
- instance_write(:fingerprint, generate_fingerprint(@queued_for_write[:original]))
123
- ensure
124
- uploaded_file.close if close_uploaded_file
108
+ instance_write(:file_size, @queued_for_write[:original].size)
109
+ instance_write(:fingerprint, @queued_for_write[:original].fingerprint) if instance_respond_to?(:fingerprint)
125
110
  end
126
111
 
127
112
  # Returns the public URL of the attachment with a given style. This does
@@ -217,10 +202,14 @@ module Paperclip
217
202
  # Clears out the attachment. Has the same effect as previously assigning
218
203
  # nil to the attachment. Does NOT save. If you wish to clear AND save,
219
204
  # use #destroy.
220
- def clear
221
- queue_existing_for_delete
222
- @queued_for_write = {}
223
- @errors = {}
205
+ def clear(*styles_to_clear)
206
+ if styles_to_clear.any?
207
+ queue_some_for_delete(*styles_to_clear)
208
+ else
209
+ queue_all_for_delete
210
+ @queued_for_write = {}
211
+ @errors = {}
212
+ end
224
213
  end
225
214
 
226
215
  # Destroys the attachment. Has the same effect as previously assigning
@@ -250,10 +239,10 @@ module Paperclip
250
239
  instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
251
240
  end
252
241
 
253
- # Returns the hash of the file as originally assigned, and lives in the
254
- # <attachment>_fingerprint attribute of the model.
242
+ # Returns the fingerprint of the file, if one's defined. The fingerprint is
243
+ # stored in the <attachment>_fingerpring attribute of the model.
255
244
  def fingerprint
256
- instance_read(:fingerprint) || (@queued_for_write[:original] && generate_fingerprint(@queued_for_write[:original]))
245
+ instance_read(:fingerprint)
257
246
  end
258
247
 
259
248
  # Returns the content_type of the file as originally assigned, and lives
@@ -277,60 +266,28 @@ module Paperclip
277
266
 
278
267
  # Returns a unique hash suitable for obfuscating the URL of an otherwise
279
268
  # publicly viewable attachment.
280
- def hash(style_name = default_style)
269
+ def hash_key(style_name = default_style)
281
270
  raise ArgumentError, "Unable to generate hash without :hash_secret" unless @options[:hash_secret]
282
271
  require 'openssl' unless defined?(OpenSSL)
283
272
  data = interpolate(@options[:hash_data], style_name)
284
273
  OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@options[:hash_digest]).new, @options[:hash_secret], data)
285
274
  end
286
275
 
287
- def generate_fingerprint(source)
288
- if source.respond_to?(:path) && source.path && !source.path.blank?
289
- Digest::MD5.file(source.path).to_s
290
- else
291
- data = source.read
292
- source.rewind if source.respond_to?(:rewind)
293
- Digest::MD5.hexdigest(data)
294
- end
295
- end
296
-
297
- # Paths and URLs can have a number of variables interpolated into them
298
- # to vary the storage location based on name, id, style, class, etc.
299
- # This method is a deprecated access into supplying and retrieving these
300
- # interpolations. Future access should use either Paperclip.interpolates
301
- # or extend the Paperclip::Interpolations module directly.
302
- def self.interpolations
303
- warn('[DEPRECATION] Paperclip::Attachment.interpolations is deprecated ' +
304
- 'and will be removed from future versions. ' +
305
- 'Use Paperclip.interpolates instead')
306
- Paperclip::Interpolations
307
- end
308
-
309
276
  # This method really shouldn't be called that often. It's expected use is
310
277
  # in the paperclip:refresh rake task and that's it. It will regenerate all
311
278
  # thumbnails forcefully, by reobtaining the original file and going through
312
279
  # the post-process again.
313
280
  def reprocess!(*style_args)
314
- new_original = Tempfile.new("paperclip-reprocess")
315
- new_original.binmode
316
- if old_original = to_file(:original)
317
- new_original.write( old_original.respond_to?(:get) ? old_original.get : old_original.read )
318
- new_original.rewind
319
-
320
- @queued_for_write = { :original => new_original }
321
- instance_write(:updated_at, Time.now)
322
- post_process(*style_args)
323
-
324
- old_original.close if old_original.respond_to?(:close)
325
- old_original.unlink if old_original.respond_to?(:unlink)
326
-
281
+ saved_only_process, @options[:only_process] = @options[:only_process], style_args
282
+ begin
283
+ assign(self)
327
284
  save
328
- else
329
- true
285
+ rescue Errno::EACCES => e
286
+ warn "#{e} - skipping file."
287
+ false
288
+ ensure
289
+ @options[:only_process] = saved_only_process
330
290
  end
331
- rescue Errno::EACCES => e
332
- warn "#{e} - skipping file"
333
- false
334
291
  end
335
292
 
336
293
  # Returns true if a file has been assigned.
@@ -340,6 +297,12 @@ module Paperclip
340
297
 
341
298
  alias :present? :file?
342
299
 
300
+ # Determines whether the instance responds to this attribute. Used to prevent
301
+ # calculations on fields we won't even store.
302
+ def instance_respond_to?(attr)
303
+ instance.respond_to?(:"#{name}_#{attr}")
304
+ end
305
+
343
306
  # Writes the attachment-specific attribute on the instance. For example,
344
307
  # instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's
345
308
  # "avatar_file_name" field (assuming the attachment is called avatar).
@@ -369,7 +332,7 @@ module Paperclip
369
332
  def ensure_required_accessors! #:nodoc:
370
333
  %w(file_name).each do |field|
371
334
  unless @instance.respond_to?("#{name}_#{field}") && @instance.respond_to?("#{name}_#{field}=")
372
- raise PaperclipError.new("#{@instance.class} model missing required attr_accessor for '#{name}_#{field}'")
335
+ raise Paperclip::Error.new("#{@instance.class} model missing required attr_accessor for '#{name}_#{field}'")
373
336
  end
374
337
  end
375
338
  end
@@ -387,7 +350,7 @@ module Paperclip
387
350
  begin
388
351
  storage_module = Paperclip::Storage.const_get(storage_class_name)
389
352
  rescue NameError
390
- raise StorageMethodNotFound, "Cannot load storage module '#{storage_class_name}'"
353
+ raise Errors::StorageMethodNotFound, "Cannot load storage module '#{storage_class_name}'"
391
354
  end
392
355
  self.extend(storage_module)
393
356
  end
@@ -420,26 +383,40 @@ module Paperclip
420
383
  end
421
384
 
422
385
  def post_process_styles(*style_args) #:nodoc:
423
- styles.each do |name, style|
424
- begin
425
- if style_args.empty? || style_args.include?(name)
426
- raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
427
- @queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
428
- Paperclip.processor(processor).make(file, style.processor_options, self)
429
- end
430
- end
431
- rescue PaperclipError => e
432
- log("An error was received while processing: #{e.inspect}")
433
- (@errors[:processing] ||= []) << e.message if @options[:whiny]
386
+ post_process_style(:original, styles[:original]) if styles.include?(:original) && process_style?(:original, style_args)
387
+ styles.reject{ |name, style| name == :original }.each do |name, style|
388
+ post_process_style(name, style) if process_style?(name, style_args)
389
+ end
390
+ end
391
+
392
+ def post_process_style(name, style) #:nodoc:
393
+ begin
394
+ raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
395
+ @queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
396
+ Paperclip.processor(processor).make(file, style.processor_options, self)
434
397
  end
398
+ @queued_for_write[name] = Paperclip.io_adapters.for(@queued_for_write[name])
399
+ rescue Paperclip::Error => e
400
+ log("An error was received while processing: #{e.inspect}")
401
+ (@errors[:processing] ||= []) << e.message if @options[:whiny]
435
402
  end
436
403
  end
437
404
 
405
+ def process_style?(style_name, style_args) #:nodoc:
406
+ style_args.empty? || style_args.include?(style_name)
407
+ end
408
+
438
409
  def interpolate(pattern, style_name = default_style) #:nodoc:
439
410
  interpolator.interpolate(pattern, self, style_name)
440
411
  end
441
412
 
442
- def queue_existing_for_delete #:nodoc:
413
+ def queue_some_for_delete(*styles)
414
+ @queued_for_delete += styles.uniq.map do |style|
415
+ path(style) if exists?(style)
416
+ end.compact
417
+ end
418
+
419
+ def queue_all_for_delete #:nodoc:
443
420
  return if @options[:preserve_files] || !file?
444
421
  @queued_for_delete += [:original, *styles.keys].uniq.map do |style|
445
422
  path(style) if exists?(style)
@@ -447,6 +424,7 @@ module Paperclip
447
424
  instance_write(:file_name, nil)
448
425
  instance_write(:content_type, nil)
449
426
  instance_write(:file_size, nil)
427
+ instance_write(:fingerprint, nil)
450
428
  instance_write(:updated_at, nil)
451
429
  end
452
430
 
@@ -458,11 +436,12 @@ module Paperclip
458
436
 
459
437
  # called by storage after the writes are flushed and before @queued_for_writes is cleared
460
438
  def after_flush_writes
461
- @queued_for_write.each do |style, file|
462
- file.close unless file.closed?
463
- file.unlink if file.respond_to?(:unlink) && file.path.present? && File.exist?(file.path)
464
- end
465
439
  end
466
440
 
441
+ def cleanup_filename(filename)
442
+ if @options[:restricted_characters]
443
+ filename.gsub(@options[:restricted_characters], '_')
444
+ end
445
+ end
467
446
  end
468
447
  end
@@ -0,0 +1,9 @@
1
+ module Paperclip
2
+ class AttachmentOptions < Hash
3
+ def initialize(options)
4
+ options.each do |k, v|
5
+ self.[]=(k, v)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ module Paperclip
2
+ module Callbacks
3
+ def self.included(base)
4
+ base.extend(Defining)
5
+ base.send(:include, Running)
6
+ end
7
+
8
+ module Defining
9
+ def define_paperclip_callbacks(*callbacks)
10
+ define_callbacks *[callbacks, {:terminator => "result == false"}].flatten
11
+ callbacks.each do |callback|
12
+ eval <<-end_callbacks
13
+ def before_#{callback}(*args, &blk)
14
+ set_callback(:#{callback}, :before, *args, &blk)
15
+ end
16
+ def after_#{callback}(*args, &blk)
17
+ set_callback(:#{callback}, :after, *args, &blk)
18
+ end
19
+ end_callbacks
20
+ end
21
+ end
22
+ end
23
+
24
+ module Running
25
+ def run_paperclip_callbacks(callback, opts = nil, &block)
26
+ run_callbacks(callback, opts, &block)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ module Paperclip
2
+ # A base error class for Paperclip. Most of the error that will be thrown
3
+ # from Paperclip will inherits from this class.
4
+ class Error < StandardError
5
+ end
6
+
7
+ module Errors
8
+ # Will be thrown when a storage method is not found.
9
+ class StorageMethodNotFound < Paperclip::Error
10
+ end
11
+
12
+ # Will be thrown when a command or executable is not found.
13
+ class CommandNotFoundError < Paperclip::Error
14
+ end
15
+
16
+ # Will be thrown when ImageMagic cannot determine the uploaded file's
17
+ # metadata, usually this would mean the file is not an image.
18
+ class NotIdentifiedByImageMagickError < Paperclip::Error
19
+ end
20
+
21
+ # Will be thrown if the interpolation is creating an infinite loop. If you
22
+ # are creating an interpolator which might cause an infinite loop, you
23
+ # should be throwing this error upon the infinite loop as well.
24
+ class InfiniteInterpolationError < Paperclip::Error
25
+ end
26
+ end
27
+ end
@@ -17,16 +17,18 @@ module Paperclip
17
17
  # a Tempfile object, which would be eligible for file deletion when no longer referenced.
18
18
  def self.from_file file
19
19
  file_path = file.respond_to?(:path) ? file.path : file
20
- raise(Paperclip::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name")) if file_path.blank?
20
+ raise(Errors::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name")) if file_path.blank?
21
21
  geometry = begin
22
- Paperclip.run("identify", "-format %wx%h :file", :file => "#{file_path}[0]")
22
+ silence_stream(STDERR) do
23
+ Paperclip.run("identify", "-format %wx%h :file", :file => "#{file_path}[0]")
24
+ end
23
25
  rescue Cocaine::ExitStatusError
24
26
  ""
25
27
  rescue Cocaine::CommandNotFoundError => e
26
- raise Paperclip::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
28
+ raise Errors::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
27
29
  end
28
30
  parse(geometry) ||
29
- raise(NotIdentifiedByImageMagickError.new("#{file_path} is not recognized by the 'identify' command."))
31
+ raise(Errors::NotIdentifiedByImageMagickError.new("#{file_path} is not recognized by the 'identify' command."))
30
32
  end
31
33
 
32
34
  # Parses a "WxH" formatted string, where W is the width and H is the height.
@@ -0,0 +1,23 @@
1
+ require 'paperclip/callbacks'
2
+ require 'paperclip/validators'
3
+ require 'paperclip/schema'
4
+
5
+ module Paperclip
6
+ module Glue
7
+ def self.included base #:nodoc:
8
+ base.extend ClassMethods
9
+ base.send :include, Callbacks
10
+ base.send :include, Validators
11
+ base.class_attribute :attachment_definitions
12
+
13
+ if defined?(ActiveRecord)
14
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, Paperclip::Schema)
15
+ ActiveRecord::ConnectionAdapters::Table.send(:include, Paperclip::Schema)
16
+ ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Paperclip::Schema)
17
+ end
18
+
19
+ locale_path = Dir.glob(File.dirname(__FILE__) + "/locales/*.{rb,yml}")
20
+ I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,71 @@
1
+ module Paperclip
2
+ module Helpers
3
+ def configure
4
+ yield(self) if block_given?
5
+ end
6
+
7
+ def interpolates key, &block
8
+ Paperclip::Interpolations[key] = block
9
+ end
10
+
11
+ # The run method takes the name of a binary to run, the arguments to that binary
12
+ # and some options:
13
+ #
14
+ # :command_path -> A $PATH-like variable that defines where to look for the binary
15
+ # on the filesystem. Colon-separated, just like $PATH.
16
+ #
17
+ # :expected_outcodes -> An array of integers that defines the expected exit codes
18
+ # of the binary. Defaults to [0].
19
+ #
20
+ # :log_command -> Log the command being run when set to true (defaults to false).
21
+ # This will only log if logging in general is set to true as well.
22
+ #
23
+ # :swallow_stderr -> Set to true if you don't care what happens on STDERR.
24
+ #
25
+ def run(cmd, arguments = "", local_options = {})
26
+ command_path = options[:command_path]
27
+ Cocaine::CommandLine.path = ( Cocaine::CommandLine.path ? [Cocaine::CommandLine.path].flatten | [command_path] : command_path )
28
+ local_options = local_options.merge(:logger => logger) if logging? && (options[:log_command] || local_options[:log_command])
29
+ Cocaine::CommandLine.new(cmd, arguments, local_options).run
30
+ end
31
+
32
+ # Find all instances of the given Active Record model +klass+ with attachment +name+.
33
+ # This method is used by the refresh rake tasks.
34
+ def each_instance_with_attachment(klass, name)
35
+ unscope_method = class_for(klass).respond_to?(:unscoped) ? :unscoped : :with_exclusive_scope
36
+ class_for(klass).send(unscope_method) do
37
+ class_for(klass).find_each(:conditions => "#{name}_file_name is not null") do |instance|
38
+ yield(instance)
39
+ end
40
+ end
41
+ end
42
+
43
+ def class_for(class_name)
44
+ # Ruby 1.9 introduces an inherit argument for Module#const_get and
45
+ # #const_defined? and changes their default behavior.
46
+ # https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L89
47
+ if Module.method(:const_get).arity == 1
48
+ class_name.split('::').inject(Object) do |klass, partial_class_name|
49
+ klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name) : klass.const_missing(partial_class_name)
50
+ end
51
+ else
52
+ class_name.split('::').inject(Object) do |klass, partial_class_name|
53
+ klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name, false) : klass.const_missing(partial_class_name)
54
+ end
55
+ end
56
+ end
57
+
58
+ def check_for_url_clash(name,url,klass)
59
+ @names_url ||= {}
60
+ default_url = url || Attachment.default_options[:url]
61
+ if @names_url[name] && @names_url[name][:url] == default_url && @names_url[name][:class] != klass && @names_url[name][:url] !~ /:class/
62
+ log("Duplicate URL for #{name} with #{default_url}. This will clash with attachment defined in #{@names_url[name][:class]} class")
63
+ end
64
+ @names_url[name] = {:url => default_url, :class => klass}
65
+ end
66
+
67
+ def reset_duplicate_clash_check!
68
+ @names_url = nil
69
+ end
70
+ end
71
+ end