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
@@ -0,0 +1,21 @@
1
+ module Paperclip
2
+ module Logger
3
+ # Log a paperclip-specific line. This will log to STDOUT
4
+ # by default. Set Paperclip.options[:log] to false to turn off.
5
+ def log message
6
+ logger.info("[paperclip] #{message}") if logging?
7
+ end
8
+
9
+ def logger #:nodoc:
10
+ @logger ||= options[:logger] || ::Logger.new(STDOUT)
11
+ end
12
+
13
+ def logger=(logger)
14
+ @logger = logger
15
+ end
16
+
17
+ def logging? #:nodoc:
18
+ options[:log]
19
+ end
20
+ end
21
+ end
@@ -33,7 +33,7 @@ module Paperclip
33
33
 
34
34
  def matches? subject
35
35
  @subject = subject
36
- @subject = @subject.class unless Class === @subject
36
+ @subject = @subject.new if @subject.class == Class
37
37
  @allowed_types && @rejected_types &&
38
38
  allowed_types_allowed? && rejected_types_rejected?
39
39
  end
@@ -61,11 +61,11 @@ module Paperclip
61
61
  protected
62
62
 
63
63
  def type_allowed?(type)
64
- file = StringIO.new(".")
64
+ file = Paperclip.io_adapters.for(StringIO.new("."))
65
65
  file.content_type = type
66
- (subject = @subject.new).attachment_for(@attachment_name).assign(file)
67
- subject.valid?
68
- subject.errors[:"#{@attachment_name}_content_type"].blank?
66
+ @subject.attachment_for(@attachment_name).assign(file)
67
+ @subject.valid?
68
+ @subject.errors[:"#{@attachment_name}_content_type"].blank?
69
69
  end
70
70
 
71
71
  def allowed_types_allowed?
@@ -18,7 +18,7 @@ module Paperclip
18
18
 
19
19
  def matches? subject
20
20
  @subject = subject
21
- @subject = @subject.class unless Class === @subject
21
+ @subject = subject.new if subject.class == Class
22
22
  error_when_not_valid? && no_error_when_valid?
23
23
  end
24
24
 
@@ -37,16 +37,16 @@ module Paperclip
37
37
  protected
38
38
 
39
39
  def error_when_not_valid?
40
- (subject = @subject.new).send(@attachment_name).assign(nil)
41
- subject.valid?
42
- not subject.errors[:"#{@attachment_name}_file_name"].blank?
40
+ @subject.send(@attachment_name).assign(nil)
41
+ @subject.valid?
42
+ @subject.errors[:"#{@attachment_name}"].present?
43
43
  end
44
44
 
45
45
  def no_error_when_valid?
46
46
  @file = StringIO.new(".")
47
- (subject = @subject.new).send(@attachment_name).assign(@file)
48
- subject.valid?
49
- subject.errors[:"#{@attachment_name}_file_name"].blank?
47
+ @subject.send(@attachment_name).assign(@file)
48
+ @subject.valid?
49
+ @subject.errors[:"#{@attachment_name}"].blank?
50
50
  end
51
51
  end
52
52
  end
@@ -18,7 +18,6 @@ module Paperclip
18
18
  class ValidateAttachmentSizeMatcher
19
19
  def initialize attachment_name
20
20
  @attachment_name = attachment_name
21
- @low, @high = 0, (1.0/0)
22
21
  end
23
22
 
24
23
  def less_than size
@@ -38,7 +37,7 @@ module Paperclip
38
37
 
39
38
  def matches? subject
40
39
  @subject = subject
41
- @subject = @subject.class unless Class === @subject
40
+ @subject = @subject.new if @subject.class == Class
42
41
  lower_than_low? && higher_than_low? && lower_than_high? && higher_than_high?
43
42
  end
44
43
 
@@ -67,27 +66,28 @@ module Paperclip
67
66
  override_method(file, :size){ new_size }
68
67
  override_method(file, :to_tempfile){ file }
69
68
 
70
- (subject = @subject.new).send(@attachment_name).assign(file)
71
- subject.valid?
72
- subject.errors[:"#{@attachment_name}_file_size"].blank?
69
+ @subject.send(@attachment_name).post_processing = false
70
+ @subject.send(@attachment_name).assign(file)
71
+ @subject.valid?
72
+ @subject.errors[:"#{@attachment_name}_file_size"].blank?
73
+ ensure
74
+ @subject.send(@attachment_name).post_processing = true
73
75
  end
74
76
 
75
77
  def lower_than_low?
76
- not passes_validation_with_size(@low - 1)
78
+ @low.nil? || !passes_validation_with_size(@low - 1)
77
79
  end
78
80
 
79
81
  def higher_than_low?
80
- passes_validation_with_size(@low + 1)
82
+ @low.nil? || passes_validation_with_size(@low + 1)
81
83
  end
82
84
 
83
85
  def lower_than_high?
84
- return true if @high == (1.0/0)
85
- passes_validation_with_size(@high - 1)
86
+ @high.nil? || @high == Float::INFINITY || passes_validation_with_size(@high - 1)
86
87
  end
87
88
 
88
89
  def higher_than_high?
89
- return true if @high == (1.0/0)
90
- not passes_validation_with_size(@high + 1)
90
+ @high.nil? || @high == Float::INFINITY || !passes_validation_with_size(@high + 1)
91
91
  end
92
92
  end
93
93
  end
@@ -1,7 +1,6 @@
1
1
 
2
2
  require 'set'
3
3
  module Paperclip
4
-
5
4
  class << self
6
5
  attr_accessor :classes_with_attachments
7
6
  attr_writer :registered_attachments_styles_path
@@ -12,7 +11,6 @@ module Paperclip
12
11
 
13
12
  self.classes_with_attachments = Set.new
14
13
 
15
-
16
14
  # Get list of styles saved on previous deploy (running rake paperclip:refresh:missing_styles)
17
15
  def self.get_registered_attachments_styles
18
16
  YAML.load_file(Paperclip.registered_attachments_styles_path)
@@ -49,9 +47,9 @@ module Paperclip
49
47
  current_styles[klass_sym][attachment_name.to_sym] ||= Array.new
50
48
  current_styles[klass_sym][attachment_name.to_sym] << style_name.to_sym
51
49
  current_styles[klass_sym][attachment_name.to_sym].map!(&:to_s).sort!.map!(&:to_sym).uniq!
52
- end
53
- end
54
- end
50
+ end
51
+ end
52
+ end
55
53
  end
56
54
  end
57
55
  private_class_method :current_attachments_styles
@@ -70,8 +68,8 @@ module Paperclip
70
68
  Hash.new.tap do |missing_styles|
71
69
  current_styles.each do |klass, attachment_definitions|
72
70
  attachment_definitions.each do |attachment_name, styles|
73
- registered = registered_styles[klass][attachment_name] rescue []
74
- missed = styles - registered
71
+ registered = registered_styles[klass][attachment_name] || [] rescue []
72
+ missed = styles - registered
75
73
  if missed.present?
76
74
  klass_sym = klass.to_s.to_sym
77
75
  missing_styles[klass_sym] ||= Hash.new
@@ -80,8 +78,7 @@ module Paperclip
80
78
  missing_styles[klass_sym][attachment_name.to_sym].map!(&:to_s).sort!.map!(&:to_sym).uniq!
81
79
  end
82
80
  end
83
- end
81
+ end
84
82
  end
85
83
  end
86
-
87
84
  end
@@ -34,25 +34,40 @@ module Paperclip
34
34
  end
35
35
  end
36
36
 
37
- # Due to how ImageMagick handles its image format conversion and how Tempfile
38
- # handles its naming scheme, it is necessary to override how Tempfile makes
39
- # its names so as to allow for file extensions. Idea taken from the comments
40
- # on this blog post:
41
- # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
42
- class Tempfile < ::Tempfile
43
- # This is Ruby 1.8.7's implementation.
44
- if RUBY_VERSION <= "1.8.6" || RUBY_PLATFORM =~ /java/
45
- def make_tmpname(basename, n)
46
- case basename
47
- when Array
48
- prefix, suffix = *basename
49
- else
50
- prefix, suffix = basename, ''
51
- end
37
+ module ProcessorHelpers
38
+ def processor(name) #:nodoc:
39
+ @known_processors ||= {}
40
+ if @known_processors[name.to_s]
41
+ @known_processors[name.to_s]
42
+ else
43
+ name = name.to_s.camelize
44
+ load_processor(name) unless Paperclip.const_defined?(name)
45
+ processor = Paperclip.const_get(name)
46
+ @known_processors[name.to_s] = processor
47
+ end
48
+ end
52
49
 
53
- t = Time.now.strftime("%y%m%d")
54
- path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
50
+ def load_processor(name)
51
+ if defined?(Rails.root) && Rails.root
52
+ require File.expand_path(Rails.root.join("lib", "paperclip_processors", "#{name.underscore}.rb"))
55
53
  end
56
54
  end
55
+
56
+ def clear_processors!
57
+ @known_processors.try(:clear)
58
+ end
59
+
60
+ # You can add your own processor via the Paperclip configuration. Normally
61
+ # Paperclip will load all processors from the
62
+ # Rails.root/lib/paperclip_processors directory, but here you can add any
63
+ # existing class using this mechanism.
64
+ #
65
+ # Paperclip.configure do |c|
66
+ # c.register_processor :watermarker, WatermarkingProcessor.new
67
+ # end
68
+ def register_processor(name, processor)
69
+ @known_processors ||= {}
70
+ @known_processors[name.to_s] = processor
71
+ end
57
72
  end
58
73
  end
@@ -2,30 +2,26 @@ require 'paperclip'
2
2
  require 'paperclip/schema'
3
3
 
4
4
  module Paperclip
5
- if defined? Rails::Railtie
6
- require 'rails'
7
- class Railtie < Rails::Railtie
8
- initializer 'paperclip.insert_into_active_record' do
9
- ActiveSupport.on_load :active_record do
10
- Paperclip::Railtie.insert
11
- end
12
- end
13
- rake_tasks do
14
- load "tasks/paperclip.rake"
5
+ require 'rails'
6
+
7
+ class Railtie < Rails::Railtie
8
+ initializer 'paperclip.insert_into_active_record' do
9
+ ActiveSupport.on_load :active_record do
10
+ Paperclip::Railtie.insert
15
11
  end
16
12
  end
13
+
14
+ rake_tasks { load "tasks/paperclip.rake" }
17
15
  end
18
16
 
19
17
  class Railtie
20
18
  def self.insert
21
- ActiveRecord::Base.send(:include, Paperclip::Glue)
22
- File.send(:include, Paperclip::Upfile)
19
+ Paperclip.options[:logger] = Rails.logger
23
20
 
24
- Paperclip.options[:logger] = defined?(ActiveRecord) ? ActiveRecord::Base.logger : Rails.logger
25
-
26
- ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, Paperclip::Schema)
27
- ActiveRecord::ConnectionAdapters::Table.send(:include, Paperclip::Schema)
28
- ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Paperclip::Schema)
21
+ if defined?(ActiveRecord)
22
+ Paperclip.options[:logger] = ActiveRecord::Base.logger
23
+ ActiveRecord::Base.send(:include, Paperclip::Glue)
24
+ end
29
25
  end
30
26
  end
31
27
  end
@@ -27,22 +27,13 @@ module Paperclip
27
27
  end
28
28
  end
29
29
 
30
- # Returns representation of the data of the file assigned to the given
31
- # style, in the format most representative of the current storage.
32
- def to_file style_name = default_style
33
- @queued_for_write[style_name] || (File.new(path(style_name), 'rb') if exists?(style_name))
34
- end
35
-
36
30
  def flush_writes #:nodoc:
37
31
  @queued_for_write.each do |style_name, file|
38
- file.close
39
32
  FileUtils.mkdir_p(File.dirname(path(style_name)))
40
- log("saving #{path(style_name)}")
41
- begin
42
- FileUtils.mv(file.path, path(style_name))
43
- rescue SystemCallError
44
- FileUtils.cp(file.path, path(style_name))
45
- FileUtils.rm(file.path)
33
+ File.open(path(style_name), "wb") do |new_file|
34
+ while chunk = file.read(16 * 1024)
35
+ new_file.write(chunk)
36
+ end
46
37
  end
47
38
  FileUtils.chmod(0666&~File.umask, path(style_name))
48
39
  end
@@ -80,9 +80,10 @@ module Paperclip
80
80
  retried = false
81
81
  begin
82
82
  directory.files.create(fog_file.merge(
83
- :body => file,
84
- :key => path(style),
85
- :public => fog_public
83
+ :body => file,
84
+ :key => path(style),
85
+ :public => fog_public,
86
+ :content_type => file.content_type
86
87
  ))
87
88
  rescue Excon::Errors::NotFound
88
89
  raise if retried
@@ -105,27 +106,14 @@ module Paperclip
105
106
  @queued_for_delete = []
106
107
  end
107
108
 
108
- # Returns representation of the data of the file assigned to the given
109
- # style, in the format most representative of the current storage.
110
- def to_file(style = default_style)
111
- if @queued_for_write[style]
112
- @queued_for_write[style]
113
- else
114
- body = directory.files.get(path(style)).body
115
- filename = path(style)
116
- extname = File.extname(filename)
117
- basename = File.basename(filename, extname)
118
- file = Tempfile.new([basename, extname])
119
- file.binmode
120
- file.write(body)
121
- file.rewind
122
- file
123
- end
124
- end
125
-
126
109
  def public_url(style = default_style)
127
110
  if @options[:fog_host]
128
- host = (@options[:fog_host] =~ /%d/) ? @options[:fog_host] % (path(style).hash % 4) : @options[:fog_host]
111
+ host = if @options[:fog_host].respond_to?(:call)
112
+ @options[:fog_host].call(self)
113
+ else
114
+ (@options[:fog_host] =~ /%d/) ? @options[:fog_host] % (path(style).hash % 4) : @options[:fog_host]
115
+ end
116
+
129
117
  "#{host}/#{path(style)}"
130
118
  else
131
119
  if fog_credentials[:provider] == 'AWS'
@@ -147,6 +135,17 @@ module Paperclip
147
135
  (creds[env] || creds).symbolize_keys
148
136
  end
149
137
 
138
+ def copy_to_local_file(style, local_dest_path)
139
+ log("copying #{path(style)} to local file #{local_dest_path}")
140
+ local_file = ::File.open(local_dest_path, 'wb')
141
+ file = directory.files.get(path(style))
142
+ local_file.write(file.body)
143
+ local_file.close
144
+ rescue Fog::Errors::Error => e
145
+ warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
146
+ false
147
+ end
148
+
150
149
  private
151
150
 
152
151
  def find_credentials(creds)
@@ -158,7 +157,11 @@ module Paperclip
158
157
  when Hash
159
158
  creds
160
159
  else
161
- raise ArgumentError, "Credentials are not a path, file, or hash."
160
+ if creds.respond_to?(:call)
161
+ creds.call(self)
162
+ else
163
+ raise ArgumentError, "Credentials are not a path, file, hash or proc."
164
+ end
162
165
  end
163
166
  end
164
167
 
@@ -167,7 +170,13 @@ module Paperclip
167
170
  end
168
171
 
169
172
  def directory
170
- @directory ||= connection.directories.new(:key => @options[:fog_directory])
173
+ dir = if @options[:fog_directory].respond_to?(:call)
174
+ @options[:fog_directory].call(self)
175
+ else
176
+ @options[:fog_directory]
177
+ end
178
+
179
+ @directory ||= connection.directories.new(:key => dir)
171
180
  end
172
181
  end
173
182
  end
@@ -41,7 +41,9 @@ module Paperclip
41
41
  # * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
42
42
  # 'http' or 'https'. Defaults to 'http' when your :s3_permissions are :public_read (the
43
43
  # default), and 'https' when your :s3_permissions are anything else.
44
- # * +s3_headers+: A hash of headers such as {'Expires' => 1.year.from_now.httpdate}
44
+ # * +s3_headers+: A hash of headers or a Proc. You may specify a hash such as
45
+ # {'Expires' => 1.year.from_now.httpdate}. If you use a Proc, headers are determined at
46
+ # runtime. Paperclip will call that Proc with attachment as the only argument.
45
47
  # * +bucket+: This is the name of the S3 bucket that will store your files. Remember
46
48
  # that the bucket must be unique across all of Amazon S3. If the bucket does not exist
47
49
  # Paperclip will attempt to create it. The bucket name will not be interpolated.
@@ -93,12 +95,14 @@ module Paperclip
93
95
  @s3_permissions = set_permissions(@options[:s3_permissions])
94
96
  @s3_protocol = @options[:s3_protocol] ||
95
97
  Proc.new do |style, attachment|
96
- permission = (@s3_permissions[style.to_sym] || @s3_permissions[:default])
98
+ permission = (@s3_permissions[style.to_s.to_sym] || @s3_permissions[:default])
97
99
  permission = permission.call(attachment, style) if permission.is_a?(Proc)
98
100
  (permission == :public_read) ? 'http' : 'https'
99
101
  end
100
102
  @s3_metadata = @options[:s3_metadata] || {}
101
- @s3_headers = (@options[:s3_headers] || {}).inject({}) do |headers,(name,value)|
103
+ @s3_headers = @options[:s3_headers] || {}
104
+ @s3_headers = @s3_headers.call(instance) if @s3_headers.is_a?(Proc)
105
+ @s3_headers = (@s3_headers).inject({}) do |headers,(name,value)|
102
106
  case name.to_s
103
107
  when /^x-amz-meta-(.*)/i
104
108
  @s3_metadata[$1.downcase] = value
@@ -111,6 +115,8 @@ module Paperclip
111
115
 
112
116
  @s3_headers[:storage_class] = @options[:s3_storage_class] if @options[:s3_storage_class]
113
117
 
118
+ @s3_server_side_encryption = @options[:s3_server_side_encryption]
119
+
114
120
  unless @options[:url].to_s.match(/^:s3.*url$/) || @options[:url] == ":asset_host"
115
121
  @options[:path] = @options[:path].gsub(/:url/, @options[:url]).gsub(/^:rails_root\/public\/system/, '')
116
122
  @options[:url] = ":s3_path_url"
@@ -135,7 +141,8 @@ module Paperclip
135
141
 
136
142
  def expiring_url(time = 3600, style_name = default_style)
137
143
  if path
138
- s3_object(style_name).url_for(:read, :expires => time, :secure => use_secure_protocol?(style_name)).to_s
144
+ base_options = { :expires => time, :secure => use_secure_protocol?(style_name) }
145
+ s3_object(style_name).url_for(:read, base_options.merge(s3_url_options)).to_s
139
146
  end
140
147
  end
141
148
 
@@ -153,6 +160,12 @@ module Paperclip
153
160
  @s3_host_alias
154
161
  end
155
162
 
163
+ def s3_url_options
164
+ s3_url_options = @options[:s3_url_options] || {}
165
+ s3_url_options = s3_url_options.call(instance) if s3_url_options.is_a?(Proc)
166
+ s3_url_options
167
+ end
168
+
156
169
  def bucket_name
157
170
  @bucket = @options[:bucket] || s3_credentials[:bucket]
158
171
  @bucket = @bucket.call(self) if @bucket.is_a?(Proc)
@@ -221,6 +234,7 @@ module Paperclip
221
234
  end
222
235
 
223
236
  def parse_credentials creds
237
+ creds = creds.respond_to?('call') ? creds.call(self) : creds
224
238
  creds = find_credentials(creds).stringify_keys
225
239
  env = Object.const_defined?(:Rails) ? Rails.env : nil
226
240
  (creds[env] || creds).symbolize_keys
@@ -232,6 +246,8 @@ module Paperclip
232
246
  else
233
247
  false
234
248
  end
249
+ rescue AWS::Errors::Base => e
250
+ false
235
251
  end
236
252
 
237
253
  def s3_permissions(style = default_style)
@@ -248,20 +264,6 @@ module Paperclip
248
264
  end
249
265
  end
250
266
 
251
- # Returns representation of the data of the file assigned to the given
252
- # style, in the format most representative of the current storage.
253
- def to_file style = default_style
254
- return @queued_for_write[style] if @queued_for_write[style]
255
- filename = path(style)
256
- extname = File.extname(filename)
257
- basename = File.basename(filename, extname)
258
- file = Tempfile.new([basename, extname])
259
- file.binmode
260
- file.write(s3_object(style).read)
261
- file.rewind
262
- return file
263
- end
264
-
265
267
  def create_bucket
266
268
  s3_interface.buckets.create(bucket_name)
267
269
  end
@@ -273,10 +275,13 @@ module Paperclip
273
275
  acl = @s3_permissions[style] || @s3_permissions[:default]
274
276
  acl = acl.call(self, style) if acl.respond_to?(:call)
275
277
  write_options = {
276
- :content_type => file.content_type.to_s.strip,
278
+ :content_type => file.content_type,
277
279
  :acl => acl
278
280
  }
279
281
  write_options[:metadata] = @s3_metadata unless @s3_metadata.empty?
282
+ unless @s3_server_side_encryption.blank?
283
+ write_options[:server_side_encryption] = @s3_server_side_encryption
284
+ end
280
285
  write_options.merge!(@s3_headers)
281
286
  s3_object(style).write(file, write_options)
282
287
  rescue AWS::S3::Errors::NoSuchBucket => e
@@ -302,6 +307,17 @@ module Paperclip
302
307
  @queued_for_delete = []
303
308
  end
304
309
 
310
+ def copy_to_local_file(style, local_dest_path)
311
+ log("copying #{path(style)} to local file #{local_dest_path}")
312
+ local_file = ::File.open(local_dest_path, 'wb')
313
+ file = s3_object(style)
314
+ local_file.write(file.read)
315
+ local_file.close
316
+ rescue AWS::Errors::Base => e
317
+ warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
318
+ false
319
+ end
320
+
305
321
  def find_credentials creds
306
322
  case creds
307
323
  when File
@@ -311,19 +327,11 @@ module Paperclip
311
327
  when Hash
312
328
  creds
313
329
  else
314
- raise ArgumentError, "Credentials are not a path, file, or hash."
330
+ raise ArgumentError, "Credentials are not a path, file, proc, or hash."
315
331
  end
316
332
  end
317
333
  private :find_credentials
318
334
 
319
- def establish_connection!
320
- @connection ||= AWS::S3::Base.establish_connection!( @s3_options.merge(
321
- :access_key_id => s3_credentials[:access_key_id],
322
- :secret_access_key => s3_credentials[:secret_access_key]
323
- ))
324
- end
325
- private :establish_connection!
326
-
327
335
  def use_secure_protocol?(style_name)
328
336
  s3_protocol(style_name) == "https"
329
337
  end