paperclip-cloudfiles 2.3.2 → 2.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/README.rdoc +10 -3
  2. data/Rakefile +8 -4
  3. data/generators/paperclip/USAGE +2 -2
  4. data/generators/paperclip/paperclip_generator.rb +8 -8
  5. data/lib/generators/paperclip/USAGE +8 -0
  6. data/lib/generators/paperclip/paperclip_generator.rb +31 -0
  7. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  8. data/lib/paperclip/attachment.rb +38 -18
  9. data/lib/paperclip/command_line.rb +80 -0
  10. data/lib/paperclip/geometry.rb +7 -7
  11. data/lib/paperclip/interpolations.rb +8 -2
  12. data/lib/paperclip/iostream.rb +11 -25
  13. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +3 -3
  14. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +0 -1
  15. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +0 -1
  16. data/lib/paperclip/processor.rb +15 -6
  17. data/lib/paperclip/railtie.rb +24 -0
  18. data/lib/paperclip/storage/cloud_files.rb +131 -0
  19. data/lib/paperclip/storage/filesystem.rb +73 -0
  20. data/lib/paperclip/storage/s3.rb +192 -0
  21. data/lib/paperclip/storage.rb +3 -371
  22. data/lib/paperclip/style.rb +11 -11
  23. data/lib/paperclip/thumbnail.rb +16 -15
  24. data/lib/paperclip/upfile.rb +5 -3
  25. data/lib/paperclip/version.rb +3 -0
  26. data/lib/paperclip.rb +78 -92
  27. data/lib/tasks/paperclip.rake +72 -0
  28. data/rails/init.rb +2 -0
  29. data/shoulda_macros/paperclip.rb +1 -2
  30. data/test/attachment_test.rb +74 -28
  31. data/test/command_line_test.rb +133 -0
  32. data/test/geometry_test.rb +2 -2
  33. data/test/helper.rb +22 -24
  34. data/test/integration_test.rb +10 -11
  35. data/test/interpolations_test.rb +7 -4
  36. data/test/iostream_test.rb +6 -13
  37. data/test/matchers/have_attached_file_matcher_test.rb +1 -1
  38. data/test/matchers/validate_attachment_content_type_matcher_test.rb +11 -1
  39. data/test/matchers/validate_attachment_presence_matcher_test.rb +1 -1
  40. data/test/matchers/validate_attachment_size_matcher_test.rb +1 -1
  41. data/test/paperclip_test.rb +54 -80
  42. data/test/processor_test.rb +1 -1
  43. data/test/storage_test.rb +32 -12
  44. data/test/style_test.rb +17 -17
  45. data/test/thumbnail_test.rb +18 -18
  46. data/test/upfile_test.rb +1 -1
  47. metadata +58 -31
  48. data/tasks/paperclip_tasks.rake +0 -79
data/README.rdoc CHANGED
@@ -16,12 +16,18 @@ See the documentation for +has_attached_file+ in Paperclip::ClassMethods for
16
16
  more detailed options.
17
17
 
18
18
  This fork has support for Rackspace Cloud Files. It requires the "cloudfiles"
19
- gem, >= 1.4.4, from Gemcutter.org. The Thoughtbot guys have indicated that they
19
+ gem, >= 1.4.9, from Gemcutter.org. The Thoughtbot guys have indicated that they
20
20
  don't want to pull any code into the official Paperclip mainline that they don't
21
21
  personally use on projects, so until they discover the joy of Cloud Files, this
22
22
  fork is available as the {paperclip-cloudfiles gem}[http://gemcutter.org/gems/paperclip-cloudfiles]
23
23
  on Gemcutter's gem server.
24
24
 
25
+ ==Installation
26
+
27
+ Include the gem in your Gemfile:
28
+
29
+ gem "paperclip-cloudfiles", "~> 2.3"
30
+
25
31
  ==Quick Start
26
32
 
27
33
  In your environment.rb:
@@ -150,7 +156,7 @@ For example, assuming we had this definition:
150
156
  has_attached_file :scan, :styles => { :text => { :quality => :better } },
151
157
  :processors => [:rotator, :ocr]
152
158
 
153
- then both the :rotator processor and the :ocr processor would receive the
159
+ then both the :rotator processor and the :ocr processor would receive the
154
160
  options "{ :quality => :better }". This parameter may not mean anything to one
155
161
  or more or the processors, and they are expected to ignore it.
156
162
 
@@ -185,7 +191,8 @@ If you'd like to contribute a feature or bugfix: Thanks! To make sure your
185
191
  fix/feature has a high chance of being included, please read the following
186
192
  guidelines:
187
193
 
188
- 1. Ask on the mailing list, or post a new GitHub Issue.
194
+ 1. Ask on the mailing list[http://groups.google.com/group/paperclip-plugin], or
195
+ post a new GitHub Issue[http://github.com/thoughtbot/paperclip/issues].
189
196
  2. Make sure there are tests! We will not accept any patch that is not tested.
190
197
  It's a rare time when explicit tests aren't needed. If you have questions
191
198
  about writing tests for paperclip, please ask the mailing list.
data/Rakefile CHANGED
@@ -1,3 +1,7 @@
1
+ require 'rubygems'
2
+ require 'appraisal'
3
+ require 'bundler/setup'
4
+
1
5
  require 'rake'
2
6
  require 'rake/testtask'
3
7
  require 'rake/rdoctask'
@@ -6,11 +10,11 @@ $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
6
10
  require 'paperclip'
7
11
 
8
12
  desc 'Default: run unit tests.'
9
- task :default => [:clean, :test]
13
+ task :default => [:clean, :all]
10
14
 
11
15
  desc 'Test the paperclip plugin under all supported Rails versions.'
12
16
  task :all do |t|
13
- exec('rake RAILS_VERSION=2.1 && rake RAILS_VERSION=2.3 && rake RAILS_VERSION=3.0')
17
+ exec('rake appraisal test')
14
18
  end
15
19
 
16
20
  desc 'Test the paperclip plugin.'
@@ -62,13 +66,13 @@ task :manifest => :clean do
62
66
  puts file
63
67
  end
64
68
  end
65
-
69
+
66
70
  desc "Generate a gemspec file for GitHub"
67
71
  task :gemspec => :clean do
68
72
  File.open("#{spec.name}.gemspec", 'w') do |f|
69
73
  f.write spec.to_ruby
70
74
  end
71
- end
75
+ end
72
76
 
73
77
  desc "Build the gem into the current directory"
74
78
  task :gem => :gemspec do
@@ -1,5 +1,5 @@
1
1
  Usage:
2
2
 
3
3
  script/generate paperclip Class attachment1 (attachment2 ...)
4
-
5
- This will create a migration that will add the proper columns to your class's table.
4
+
5
+ This will create a migration that will add the proper columns to your class's table.
@@ -1,12 +1,12 @@
1
1
  class PaperclipGenerator < Rails::Generator::NamedBase
2
2
  attr_accessor :attachments, :migration_name
3
-
3
+
4
4
  def initialize(args, options = {})
5
5
  super
6
6
  @class_name, @attachments = args[0], args[1..-1]
7
7
  end
8
-
9
- def manifest
8
+
9
+ def manifest
10
10
  file_name = generate_file_name
11
11
  @migration_name = file_name.camelize
12
12
  record do |m|
@@ -14,14 +14,14 @@ class PaperclipGenerator < Rails::Generator::NamedBase
14
14
  File.join('db', 'migrate'),
15
15
  :migration_file_name => file_name
16
16
  end
17
- end
18
-
19
- private
20
-
17
+ end
18
+
19
+ private
20
+
21
21
  def generate_file_name
22
22
  names = attachments.map{|a| a.underscore }
23
23
  names = names[0..-2] + ["and", names[-1]] if names.length > 1
24
24
  "add_attachments_#{names.join("_")}_to_#{@class_name.underscore}"
25
25
  end
26
-
26
+
27
27
  end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ rails generate paperclip Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,31 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ class PaperclipGenerator < ActiveRecord::Generators::Base
4
+ desc "Create a migration to add paperclip-specific fields to your model."
5
+
6
+ argument :attachment_names, :required => true, :type => :array, :desc => "The names of the attachment(s) to add.",
7
+ :banner => "attachment_one attachment_two attachment_three ..."
8
+
9
+ def self.source_root
10
+ @source_root ||= File.expand_path('../templates', __FILE__)
11
+ end
12
+
13
+ def generate_migration
14
+ migration_template "paperclip_migration.rb.erb", "db/migrate/#{migration_file_name}"
15
+ end
16
+
17
+ protected
18
+
19
+ def migration_name
20
+ "add_attachment_#{attachment_names.join("_")}_to_#{name.underscore}"
21
+ end
22
+
23
+ def migration_file_name
24
+ "#{migration_name}.rb"
25
+ end
26
+
27
+ def migration_class_name
28
+ migration_name.camelize
29
+ end
30
+
31
+ end
@@ -0,0 +1,19 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ <% attachment_names.each do |attachment| -%>
4
+ add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_name, :string
5
+ add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_content_type, :string
6
+ add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_size, :integer
7
+ add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at, :datetime
8
+ <% end -%>
9
+ end
10
+
11
+ def self.down
12
+ <% attachment_names.each do |attachment| -%>
13
+ remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_name
14
+ remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_content_type
15
+ remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_size
16
+ remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at
17
+ <% end -%>
18
+ end
19
+ end
@@ -4,7 +4,8 @@ module Paperclip
4
4
  # when the model saves, deletes when the model is destroyed, and processes
5
5
  # the file upon assignment.
6
6
  class Attachment
7
-
7
+ include IOStream
8
+
8
9
  def self.default_options
9
10
  @default_options ||= {
10
11
  :url => "/system/:attachment/:id/:style/:filename",
@@ -16,6 +17,7 @@ module Paperclip
16
17
  :default_style => :original,
17
18
  :validations => [],
18
19
  :storage => :filesystem,
20
+ :use_timestamp => true,
19
21
  :whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails]
20
22
  }
21
23
  end
@@ -41,6 +43,7 @@ module Paperclip
41
43
  @validations = options[:validations]
42
44
  @default_style = options[:default_style]
43
45
  @storage = options[:storage]
46
+ @use_timestamp = options[:use_timestamp]
44
47
  @whiny = options[:whiny_thumbnails] || options[:whiny]
45
48
  @convert_options = options[:convert_options]
46
49
  @processors = options[:processors]
@@ -53,17 +56,17 @@ module Paperclip
53
56
 
54
57
  initialize_storage
55
58
  end
56
-
59
+
57
60
  def styles
58
61
  unless @normalized_styles
59
62
  @normalized_styles = {}
60
63
  (@styles.respond_to?(:call) ? @styles.call(self) : @styles).each do |name, args|
61
- @normalized_styles[name] = Paperclip::Style.new(name, args, self)
64
+ @normalized_styles[name] = Paperclip::Style.new(name, args.dup, self)
62
65
  end
63
66
  end
64
67
  @normalized_styles
65
68
  end
66
-
69
+
67
70
  def processors
68
71
  @processors.respond_to?(:call) ? @processors.call(instance) : @processors
69
72
  end
@@ -72,7 +75,7 @@ module Paperclip
72
75
  # errors, assigns attributes, processes the file, and runs validations. It
73
76
  # also queues up the previous file for deletion, to be flushed away on
74
77
  # #save of its host. In addition to form uploads, you can also assign
75
- # another Paperclip attachment:
78
+ # another Paperclip attachment:
76
79
  # new_user.avatar = old_user.avatar
77
80
  # If the file that is assigned is not valid, the processing (i.e.
78
81
  # thumbnailing, etc) will NOT be run.
@@ -91,10 +94,11 @@ module Paperclip
91
94
 
92
95
  return nil if uploaded_file.nil?
93
96
 
94
- @queued_for_write[:original] = uploaded_file.to_tempfile
97
+ @queued_for_write[:original] = to_tempfile(uploaded_file)
95
98
  instance_write(:file_name, uploaded_file.original_filename.strip)
96
99
  instance_write(:content_type, uploaded_file.content_type.to_s.strip)
97
100
  instance_write(:file_size, uploaded_file.size.to_i)
101
+ instance_write(:fingerprint, generate_fingerprint(uploaded_file))
98
102
  instance_write(:updated_at, Time.now)
99
103
 
100
104
  @dirty = true
@@ -102,7 +106,8 @@ module Paperclip
102
106
  post_process if valid?
103
107
 
104
108
  # Reset the file size if the original file was reprocessed.
105
- instance_write(:file_size, @queued_for_write[:original].size.to_i)
109
+ instance_write(:file_size, @queued_for_write[:original].size.to_i)
110
+ instance_write(:fingerprint, generate_fingerprint(@queued_for_write[:original]))
106
111
  ensure
107
112
  uploaded_file.close if close_uploaded_file
108
113
  validate
@@ -112,12 +117,11 @@ module Paperclip
112
117
  # this does not necessarily need to point to a file that your web server
113
118
  # can access and can point to an action in your app, if you need fine
114
119
  # grained security. This is not recommended if you don't need the
115
- # security, however, for performance reasons. set
116
- # include_updated_timestamp to false if you want to stop the attachment
117
- # update time appended to the url
118
- def url style_name = default_style, include_updated_timestamp = true
120
+ # security, however, for performance reasons. Set use_timestamp to false
121
+ # if you want to stop the attachment update time appended to the url
122
+ def url(style_name = default_style, use_timestamp = @use_timestamp)
119
123
  url = original_filename.nil? ? interpolate(@default_url, style_name) : interpolate(@url, style_name)
120
- include_updated_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
124
+ use_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
121
125
  end
122
126
 
123
127
  # Returns the path of the attachment as defined by the :path option. If the
@@ -192,19 +196,31 @@ module Paperclip
192
196
  instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
193
197
  end
194
198
 
199
+ # Returns the hash of the file as originally assigned, and lives in the
200
+ # <attachment>_fingerprint attribute of the model.
201
+ def fingerprint
202
+ instance_read(:fingerprint) || (@queued_for_write[:original] && generate_fingerprint(@queued_for_write[:original]))
203
+ end
204
+
195
205
  # Returns the content_type of the file as originally assigned, and lives
196
206
  # in the <attachment>_content_type attribute of the model.
197
207
  def content_type
198
208
  instance_read(:content_type)
199
209
  end
200
-
201
- # Returns the last modified time of the file as originally assigned, and
210
+
211
+ # Returns the last modified time of the file as originally assigned, and
202
212
  # lives in the <attachment>_updated_at attribute of the model.
203
213
  def updated_at
204
214
  time = instance_read(:updated_at)
205
215
  time && time.to_f.to_i
206
216
  end
207
217
 
218
+ def generate_fingerprint(source)
219
+ data = source.read
220
+ source.rewind if source.respond_to?(:rewind)
221
+ Digest::MD5.hexdigest(data)
222
+ end
223
+
208
224
  # Paths and URLs can have a number of variables interpolated into them
209
225
  # to vary the storage location based on name, id, style, class, etc.
210
226
  # This method is a deprecated access into supplying and retrieving these
@@ -225,7 +241,7 @@ module Paperclip
225
241
  new_original = Tempfile.new("paperclip-reprocess")
226
242
  new_original.binmode
227
243
  if old_original = to_file(:original)
228
- new_original.write( old_original.read )
244
+ new_original.write( old_original.respond_to?(:get) ? old_original.get : old_original.read )
229
245
  new_original.rewind
230
246
 
231
247
  @queued_for_write = { :original => new_original }
@@ -238,7 +254,7 @@ module Paperclip
238
254
  true
239
255
  end
240
256
  end
241
-
257
+
242
258
  # Returns true if a file has been assigned.
243
259
  def file?
244
260
  !original_filename.blank?
@@ -330,7 +346,12 @@ module Paperclip
330
346
  end
331
347
 
332
348
  def initialize_storage #:nodoc:
333
- @storage_module = Paperclip::Storage.const_get(@storage.to_s.classify)
349
+ storage_class_name = @storage.to_s.capitalize
350
+ begin
351
+ @storage_module = Paperclip::Storage.const_get(storage_class_name)
352
+ rescue NameError
353
+ raise StorageMethodNotFound, "Cannot load storage module '#{storage_class_name}'"
354
+ end
334
355
  self.extend(@storage_module)
335
356
  end
336
357
 
@@ -389,4 +410,3 @@ module Paperclip
389
410
 
390
411
  end
391
412
  end
392
-
@@ -0,0 +1,80 @@
1
+ module Paperclip
2
+ class CommandLine
3
+ class << self
4
+ attr_accessor :path
5
+ end
6
+
7
+ def initialize(binary, params = "", options = {})
8
+ @binary = binary.dup
9
+ @params = params.dup
10
+ @options = options.dup
11
+ @swallow_stderr = @options.has_key?(:swallow_stderr) ? @options.delete(:swallow_stderr) : Paperclip.options[:swallow_stderr]
12
+ @expected_outcodes = @options.delete(:expected_outcodes)
13
+ @expected_outcodes ||= [0]
14
+ end
15
+
16
+ def command
17
+ cmd = []
18
+ cmd << full_path(@binary)
19
+ cmd << interpolate(@params, @options)
20
+ cmd << bit_bucket if @swallow_stderr
21
+ cmd.join(" ")
22
+ end
23
+
24
+ def run
25
+ Paperclip.log(command)
26
+ begin
27
+ output = self.class.send(:'`', command)
28
+ rescue Errno::ENOENT
29
+ raise Paperclip::CommandNotFoundError
30
+ end
31
+ if $?.exitstatus == 127
32
+ raise Paperclip::CommandNotFoundError
33
+ end
34
+ unless @expected_outcodes.include?($?.exitstatus)
35
+ raise Paperclip::PaperclipCommandLineError, "Command '#{command}' returned #{$?.exitstatus}. Expected #{@expected_outcodes.join(", ")}"
36
+ end
37
+ output
38
+ end
39
+
40
+ private
41
+
42
+ def full_path(binary)
43
+ [self.class.path, binary].compact.join("/")
44
+ end
45
+
46
+ def interpolate(pattern, vars)
47
+ # interpolates :variables and :{variables}
48
+ pattern.gsub(%r#:(?:\w+|\{\w+\})#) do |match|
49
+ key = match[1..-1]
50
+ key = key[1..-2] if key[0,1] == '{'
51
+ if invalid_variables.include?(key)
52
+ raise PaperclipCommandLineError,
53
+ "Interpolation of #{key} isn't allowed."
54
+ end
55
+ shell_quote(vars[key.to_sym])
56
+ end
57
+ end
58
+
59
+ def invalid_variables
60
+ %w(expected_outcodes swallow_stderr)
61
+ end
62
+
63
+ def shell_quote(string)
64
+ return "" if string.nil? or string.blank?
65
+ if self.class.unix?
66
+ string.split("'").map{|m| "'#{m}'" }.join("\\'")
67
+ else
68
+ %{"#{string}"}
69
+ end
70
+ end
71
+
72
+ def bit_bucket
73
+ self.class.unix? ? "2>/dev/null" : "2>NUL"
74
+ end
75
+
76
+ def self.unix?
77
+ File.exist?("/dev/null")
78
+ end
79
+ end
80
+ end
@@ -16,7 +16,7 @@ module Paperclip
16
16
  def self.from_file file
17
17
  file = file.path if file.respond_to? "path"
18
18
  geometry = begin
19
- Paperclip.run("identify", "-format", "%wx%h", "#{file}[0]")
19
+ Paperclip.run("identify", "-format %wx%h :file", :file => "#{file}[0]")
20
20
  rescue PaperclipCommandLineError
21
21
  ""
22
22
  end
@@ -75,12 +75,12 @@ module Paperclip
75
75
  to_s
76
76
  end
77
77
 
78
- # Returns the scaling and cropping geometries (in string-based ImageMagick format)
79
- # neccessary to transform this Geometry into the Geometry given. If crop is true,
80
- # then it is assumed the destination Geometry will be the exact final resolution.
81
- # In this case, the source Geometry is scaled so that an image containing the
82
- # destination Geometry would be completely filled by the source image, and any
83
- # overhanging image would be cropped. Useful for square thumbnail images. The cropping
78
+ # Returns the scaling and cropping geometries (in string-based ImageMagick format)
79
+ # neccessary to transform this Geometry into the Geometry given. If crop is true,
80
+ # then it is assumed the destination Geometry will be the exact final resolution.
81
+ # In this case, the source Geometry is scaled so that an image containing the
82
+ # destination Geometry would be completely filled by the source image, and any
83
+ # overhanging image would be cropped. Useful for square thumbnail images. The cropping
84
84
  # is weighted at the center of the Geometry.
85
85
  def transformation_to dst, crop = false
86
86
  if crop
@@ -41,8 +41,9 @@ module Paperclip
41
41
  # Returns the interpolated URL. Will raise an error if the url itself
42
42
  # contains ":url" to prevent infinite recursion. This interpolation
43
43
  # is used in the default :path to ease default specifications.
44
+ RIGHT_HERE = "#{__FILE__.gsub(%r{^\./}, "")}:#{__LINE__ + 3}"
44
45
  def url attachment, style_name
45
- raise InfiniteInterpolationError if attachment.options[:url].include?(":url")
46
+ raise InfiniteInterpolationError if caller.any?{|b| b.index(RIGHT_HERE) }
46
47
  attachment.url(style_name, false)
47
48
  end
48
49
 
@@ -78,7 +79,7 @@ module Paperclip
78
79
  # Returns the extension of the file. e.g. "jpg" for "file.jpg"
79
80
  # If the style has a format defined, it will return the format instead
80
81
  # of the actual extension.
81
- def extension attachment, style_name
82
+ def extension attachment, style_name
82
83
  ((style = attachment.styles[style_name]) && style[:format]) ||
83
84
  File.extname(attachment.original_filename).gsub(/^\.+/, "")
84
85
  end
@@ -88,6 +89,11 @@ module Paperclip
88
89
  attachment.instance.id
89
90
  end
90
91
 
92
+ # Returns the fingerprint of the instance.
93
+ def fingerprint attachment, style_name
94
+ attachment.fingerprint
95
+ end
96
+
91
97
  # Returns the id of the instance in a split path form. e.g. returns
92
98
  # 000/001/234 for an id of 1234.
93
99
  def id_partition attachment, style_name
@@ -1,48 +1,34 @@
1
1
  # Provides method that can be included on File-type objects (IO, StringIO, Tempfile, etc) to allow stream copying
2
2
  # and Tempfile conversion.
3
3
  module IOStream
4
-
5
4
  # Returns a Tempfile containing the contents of the readable object.
6
- def to_tempfile
7
- name = respond_to?(:original_filename) ? original_filename : (respond_to?(:path) ? path : "stream")
8
- tempfile = Paperclip::Tempfile.new("stream" + File.extname(name))
5
+ def to_tempfile(object)
6
+ return object.to_tempfile if object.respond_to?(:to_tempfile)
7
+ name = object.respond_to?(:original_filename) ? object.original_filename : (object.respond_to?(:path) ? object.path : "stream")
8
+ tempfile = Paperclip::Tempfile.new(["stream", File.extname(name)])
9
9
  tempfile.binmode
10
- self.stream_to(tempfile)
10
+ stream_to(object, tempfile)
11
11
  end
12
12
 
13
13
  # Copies one read-able object from one place to another in blocks, obviating the need to load
14
- # the whole thing into memory. Defaults to 8k blocks. If this module is included in both
15
- # StringIO and Tempfile, then either can have its data copied anywhere else without typing
16
- # worries or memory overhead worries. Returns a File if a String is passed in as the destination
17
- # and returns the IO or Tempfile as passed in if one is sent as the destination.
18
- def stream_to path_or_file, in_blocks_of = 8192
14
+ # the whole thing into memory. Defaults to 8k blocks. Returns a File if a String is passed
15
+ # in as the destination and returns the IO or Tempfile as passed in if one is sent as the destination.
16
+ def stream_to object, path_or_file, in_blocks_of = 8192
19
17
  dstio = case path_or_file
20
18
  when String then File.new(path_or_file, "wb+")
21
19
  when IO then path_or_file
22
20
  when Tempfile then path_or_file
23
21
  end
24
22
  buffer = ""
25
- self.rewind
26
- while self.read(in_blocks_of, buffer) do
23
+ object.rewind
24
+ while object.read(in_blocks_of, buffer) do
27
25
  dstio.write(buffer)
28
26
  end
29
- dstio.rewind
27
+ dstio.rewind
30
28
  dstio
31
29
  end
32
30
  end
33
31
 
34
- class IO #:nodoc:
35
- include IOStream
36
- end
37
-
38
- %w( Tempfile StringIO ).each do |klass|
39
- if Object.const_defined? klass
40
- Object.const_get(klass).class_eval do
41
- include IOStream
42
- end
43
- end
44
- end
45
-
46
32
  # Corrects a bug in Windows when asking for Tempfile size.
47
33
  if defined? Tempfile
48
34
  class Tempfile
@@ -42,7 +42,7 @@ module Paperclip
42
42
  end
43
43
 
44
44
  def negative_failure_message
45
- "Content types #{@allowed_types.join(", ")} should be rejected" +
45
+ "Content types #{@allowed_types.join(", ")} should be rejected" +
46
46
  " and #{@rejected_types.join(", ")} accepted by #{@attachment_name}"
47
47
  end
48
48
 
@@ -57,7 +57,8 @@ module Paperclip
57
57
  file = StringIO.new(".")
58
58
  file.content_type = type
59
59
  (subject = @subject.new).attachment_for(@attachment_name).assign(file)
60
- subject.valid? && subject.errors[:"#{@attachment_name}_content_type"].blank?
60
+ subject.valid?
61
+ subject.errors[:"#{@attachment_name}_content_type"].blank?
61
62
  end
62
63
  end
63
64
 
@@ -72,4 +73,3 @@ module Paperclip
72
73
  end
73
74
  end
74
75
  end
75
-
@@ -52,4 +52,3 @@ module Paperclip
52
52
  end
53
53
  end
54
54
  end
55
-
@@ -93,4 +93,3 @@ module Paperclip
93
93
  end
94
94
  end
95
95
  end
96
-
@@ -6,7 +6,7 @@ module Paperclip
6
6
  #
7
7
  # Processors are required to be defined inside the Paperclip module and
8
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:
9
+ # only one method you *must* implement to properly be a subclass:
10
10
  # #make, but #initialize may also be of use. Both methods accept 3
11
11
  # arguments: the file that will be operated on (which is an instance of
12
12
  # File), a hash of options that were defined in has_attached_file's
@@ -33,17 +33,26 @@ module Paperclip
33
33
  new(file, options, attachment).make
34
34
  end
35
35
  end
36
-
36
+
37
37
  # Due to how ImageMagick handles its image format conversion and how Tempfile
38
38
  # handles its naming scheme, it is necessary to override how Tempfile makes
39
39
  # its names so as to allow for file extensions. Idea taken from the comments
40
40
  # on this blog post:
41
41
  # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
42
42
  class Tempfile < ::Tempfile
43
- # Replaces Tempfile's +make_tmpname+ with one that honors file extensions.
44
- def make_tmpname(basename, n)
45
- extension = File.extname(basename)
46
- sprintf("%s,%d,%d%s", File.basename(basename, extension), $$, n, extension)
43
+ # This is Ruby 1.8.7's implementation.
44
+ if RUBY_VERSION <= "1.8.6"
45
+ def make_tmpname(basename, n)
46
+ case basename
47
+ when Array
48
+ prefix, suffix = *basename
49
+ else
50
+ prefix, suffix = basename, ''
51
+ end
52
+
53
+ t = Time.now.strftime("%y%m%d")
54
+ path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
55
+ end
47
56
  end
48
57
  end
49
58
  end
@@ -0,0 +1,24 @@
1
+ require 'paperclip'
2
+
3
+ module Paperclip
4
+ if defined? Rails::Railtie
5
+ require 'rails'
6
+ class Railtie < Rails::Railtie
7
+ initializer 'paperclip.insert_into_active_record' do
8
+ ActiveSupport.on_load :active_record do
9
+ Paperclip::Railtie.insert
10
+ end
11
+ end
12
+ rake_tasks do
13
+ load "tasks/paperclip.rake"
14
+ end
15
+ end
16
+ end
17
+
18
+ class Railtie
19
+ def self.insert
20
+ ActiveRecord::Base.send(:include, Paperclip::Glue)
21
+ File.send(:include, Paperclip::Upfile)
22
+ end
23
+ end
24
+ end