ripta-dm-paperclip 2.2.9.2 → 2.5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +12 -1
- data/Rakefile +10 -2
- data/lib/dm-paperclip.rb +79 -44
- data/lib/dm-paperclip/attachment.rb +11 -9
- data/lib/dm-paperclip/interpolations.rb +7 -7
- data/lib/dm-paperclip/iostream.rb +4 -3
- data/lib/dm-paperclip/storage.rb +71 -57
- data/lib/dm-paperclip/validations.rb +40 -5
- data/test/attachment_test.rb +19 -16
- data/test/geometry_test.rb +1 -1
- data/test/helper.rb +17 -5
- data/test/iostream_test.rb +42 -24
- data/test/paperclip_test.rb +1 -1
- data/test/storage_test.rb +191 -40
- metadata +80 -9
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
=DataMapper Paperclip
|
2
2
|
|
3
|
-
DM-Paperclip is a port of Thoughtbot's Paperclip plugin to work with DataMapper
|
3
|
+
DM-Paperclip is a port of Thoughtbot's Paperclip plugin to work with DataMapper. This plugin is fully compatible with
|
4
4
|
the original ActiveRecord-oriented Paperclip. You could take an existing ActiveRecord database and use it with DataMapper.
|
5
5
|
The module also includes updates validation handling and automatic including of the necessary 'property' fields into
|
6
6
|
your model.
|
@@ -41,6 +41,17 @@ In your model:
|
|
41
41
|
:thumb => "100x100>" }
|
42
42
|
end
|
43
43
|
|
44
|
+
You will need to add an initializer to configure Paperclip. If on Rails, can add a config/initializers/paperclip.rb, on Merb
|
45
|
+
can use config/init.rb and add it to the Merb::BootLoader.after_app_loads section. Can also use environment configs, rackup
|
46
|
+
file, Rake task, wherever.
|
47
|
+
|
48
|
+
Paperclip.configure do |config|
|
49
|
+
config.root = Rails.root # the application root to anchor relative urls (defaults to Dir.pwd)
|
50
|
+
config.env = Rails.env # server env support, defaults to ENV['RACK_ENV'] or 'development'
|
51
|
+
config.use_dm_validations = true # validate attachment sizes and such, defaults to false
|
52
|
+
config.processors_path = 'lib/pc' # relative path to look for processors, defaults to 'lib/paperclip_processors'
|
53
|
+
end
|
54
|
+
|
44
55
|
Your database will need to add four columns, avatar_file_name (varchar), avatar_content_type (varchar), and
|
45
56
|
avatar_file_size (integer), and avatar_updated_at (datetime). You can either add these manually, auto-
|
46
57
|
migrate, or use the following migration:
|
data/Rakefile
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup
|
4
|
+
Bundler.require(:development)
|
5
|
+
|
1
6
|
require 'rake'
|
2
7
|
require 'rake/testtask'
|
3
8
|
require 'rake/rdoctask'
|
4
9
|
require 'rake/gempackagetask'
|
5
10
|
|
6
11
|
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
|
7
|
-
require 'dm-core'
|
8
|
-
require 'dm-validations'
|
9
12
|
require 'dm-paperclip'
|
10
13
|
|
11
14
|
desc 'Default: run unit tests.'
|
@@ -79,6 +82,11 @@ Rake::GemPackageTask.new(spec) do |pkg|
|
|
79
82
|
pkg.need_tar = true
|
80
83
|
end
|
81
84
|
|
85
|
+
desc 'Generate gemspec'
|
86
|
+
task :gemspec do
|
87
|
+
File.open("#{spec.name}.gemspec", 'w') { |f| f.puts(spec.to_ruby) }
|
88
|
+
end
|
89
|
+
|
82
90
|
WIN32 = (PLATFORM =~ /win32|cygwin/) rescue nil
|
83
91
|
SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
|
84
92
|
|
data/lib/dm-paperclip.rb
CHANGED
@@ -25,7 +25,11 @@
|
|
25
25
|
#
|
26
26
|
# See the +has_attached_file+ documentation for more details.
|
27
27
|
|
28
|
+
require 'erb'
|
28
29
|
require 'tempfile'
|
30
|
+
|
31
|
+
require 'dm-core'
|
32
|
+
|
29
33
|
require 'dm-paperclip/upfile'
|
30
34
|
require 'dm-paperclip/iostream'
|
31
35
|
require 'dm-paperclip/geometry'
|
@@ -34,22 +38,70 @@ require 'dm-paperclip/thumbnail'
|
|
34
38
|
require 'dm-paperclip/storage'
|
35
39
|
require 'dm-paperclip/interpolations'
|
36
40
|
require 'dm-paperclip/attachment'
|
37
|
-
if defined? RAILS_ROOT
|
38
|
-
Dir.glob(File.join(File.expand_path(RAILS_ROOT), "lib", "paperclip_processors", "*.rb")).each do |processor|
|
39
|
-
require processor
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Only include validations if dm-validations is loaded
|
44
|
-
require 'dm-paperclip/validations' unless defined?(DataMapper::Validate).nil?
|
45
41
|
|
46
42
|
# The base module that gets included in ActiveRecord::Base. See the
|
47
43
|
# documentation for Paperclip::ClassMethods for more useful information.
|
48
44
|
module Paperclip
|
49
45
|
|
50
|
-
VERSION = "2.
|
46
|
+
VERSION = "2.5.0.1"
|
47
|
+
|
48
|
+
# To configure Paperclip, put this code in an initializer, Rake task, or wherever:
|
49
|
+
#
|
50
|
+
# Paperclip.configure do |config|
|
51
|
+
# config.root = Rails.root # the application root to anchor relative urls (defaults to Dir.pwd)
|
52
|
+
# config.env = Rails.env # server env support, defaults to ENV['RACK_ENV'] or 'development'
|
53
|
+
# config.use_dm_validations = true # validate attachment sizes and such, defaults to false
|
54
|
+
# config.processors_path = 'lib/pc' # relative path to look for processors, defaults to 'lib/paperclip_processors'
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
def self.configure
|
58
|
+
yield @config = Configuration.new
|
59
|
+
Paperclip.config = @config
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.config=(config)
|
63
|
+
@config = config
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.config
|
67
|
+
@config ||= Configuration.new
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.require_processors
|
71
|
+
return if @processors_already_required
|
72
|
+
Dir.glob(File.expand_path("#{Paperclip.config.processors_path}/*.rb")).sort.each do |processor|
|
73
|
+
require processor
|
74
|
+
end
|
75
|
+
@processors_already_required = true
|
76
|
+
end
|
77
|
+
|
78
|
+
class Configuration
|
79
|
+
|
80
|
+
DEFAULT_PROCESSORS_PATH = 'lib/paperclip_processors'
|
81
|
+
|
82
|
+
attr_writer :root, :env
|
83
|
+
attr_accessor :use_dm_validations
|
84
|
+
|
85
|
+
def root
|
86
|
+
@root ||= Dir.pwd
|
87
|
+
end
|
88
|
+
|
89
|
+
def env
|
90
|
+
@env ||= (ENV['RACK_ENV'] || 'development')
|
91
|
+
end
|
92
|
+
|
93
|
+
def processors_path=(path)
|
94
|
+
@processors_path = File.expand_path(path, root)
|
95
|
+
end
|
96
|
+
|
97
|
+
def processors_path
|
98
|
+
@processors_path ||= File.expand_path("../#{DEFAULT_PROCESSORS_PATH}", root)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
51
102
|
|
52
103
|
class << self
|
104
|
+
|
53
105
|
# Provides configurability to Paperclip. There are a number of options available, such as:
|
54
106
|
# * whiny: Will raise an error if Paperclip cannot process thumbnails of
|
55
107
|
# an uploaded image. Defaults to true.
|
@@ -121,7 +173,7 @@ module Paperclip
|
|
121
173
|
name = name.to_s.camel_case
|
122
174
|
processor = Paperclip.const_get(name)
|
123
175
|
unless processor.ancestors.include?(Paperclip::Processor)
|
124
|
-
raise PaperclipError.new("Processor #{name} was not found")
|
176
|
+
raise PaperclipError.new("[paperclip] Processor #{name} was not found")
|
125
177
|
end
|
126
178
|
processor
|
127
179
|
end
|
@@ -154,7 +206,9 @@ module Paperclip
|
|
154
206
|
end
|
155
207
|
|
156
208
|
module Resource
|
209
|
+
|
157
210
|
def self.included(base)
|
211
|
+
|
158
212
|
base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
159
213
|
class_variable_set(:@@attachment_definitions,nil) unless class_variable_defined?(:@@attachment_definitions)
|
160
214
|
def self.attachment_definitions
|
@@ -165,8 +219,21 @@ module Paperclip
|
|
165
219
|
@@attachment_definitions = obj
|
166
220
|
end
|
167
221
|
RUBY
|
222
|
+
|
168
223
|
base.extend Paperclip::ClassMethods
|
224
|
+
|
225
|
+
# Done at this time to ensure that the user
|
226
|
+
# had a chance to configure the app in an initializer
|
227
|
+
if Paperclip.config.use_dm_validations
|
228
|
+
require 'dm-validations'
|
229
|
+
require 'dm-paperclip/validations'
|
230
|
+
base.extend Paperclip::Validate::ClassMethods
|
231
|
+
end
|
232
|
+
|
233
|
+
Paperclip.require_processors
|
234
|
+
|
169
235
|
end
|
236
|
+
|
170
237
|
end
|
171
238
|
|
172
239
|
module ClassMethods
|
@@ -258,44 +325,12 @@ module Paperclip
|
|
258
325
|
! attachment_for(name).original_filename.blank?
|
259
326
|
end
|
260
327
|
|
261
|
-
|
328
|
+
if Paperclip.config.use_dm_validations
|
262
329
|
add_validator_to_context(opts_from_validator_args([name]), [name], Paperclip::Validate::CopyAttachmentErrors)
|
263
330
|
end
|
264
|
-
end
|
265
|
-
|
266
|
-
unless defined?(DataMapper::Validate).nil?
|
267
|
-
# Places ActiveRecord-style validations on the size of the file assigned. The
|
268
|
-
# possible options are:
|
269
|
-
# * +in+: a Range of bytes (i.e. +1..1.megabyte+),
|
270
|
-
# * +less_than+: equivalent to :in => 0..options[:less_than]
|
271
|
-
# * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
|
272
|
-
# * +message+: error message to display, use :min and :max as replacements
|
273
|
-
def validates_attachment_size(*fields)
|
274
|
-
opts = opts_from_validator_args(fields)
|
275
|
-
add_validator_to_context(opts, fields, Paperclip::Validate::SizeValidator)
|
276
|
-
end
|
277
331
|
|
278
|
-
# Adds errors if thumbnail creation fails. The same as specifying :whiny_thumbnails => true.
|
279
|
-
def validates_attachment_thumbnails name, options = {}
|
280
|
-
self.attachment_definitions[name][:whiny_thumbnails] = true
|
281
|
-
end
|
282
|
-
|
283
|
-
# Places ActiveRecord-style validations on the presence of a file.
|
284
|
-
def validates_attachment_presence(*fields)
|
285
|
-
opts = opts_from_validator_args(fields)
|
286
|
-
add_validator_to_context(opts, fields, Paperclip::Validate::RequiredFieldValidator)
|
287
|
-
end
|
288
|
-
|
289
|
-
# Places ActiveRecord-style validations on the content type of the file assigned. The
|
290
|
-
# possible options are:
|
291
|
-
# * +content_type+: Allowed content types. Can be a single content type or an array. Allows all by default.
|
292
|
-
# * +message+: The message to display when the uploaded file has an invalid content type.
|
293
|
-
def validates_attachment_content_type(*fields)
|
294
|
-
opts = opts_from_validator_args(fields)
|
295
|
-
add_validator_to_context(opts, fields, Paperclip::Validate::ContentTypeValidator)
|
296
|
-
end
|
297
332
|
end
|
298
|
-
|
333
|
+
|
299
334
|
# Returns the attachment definitions defined by each call to
|
300
335
|
# has_attached_file.
|
301
336
|
def attachment_definitions
|
@@ -7,7 +7,7 @@ module Paperclip
|
|
7
7
|
def self.default_options
|
8
8
|
@default_options ||= {
|
9
9
|
:url => "/system/:attachment/:id/:style/:filename",
|
10
|
-
:path => ":
|
10
|
+
:path => ":rails_root/public:url",
|
11
11
|
:styles => {},
|
12
12
|
:default_url => "/:attachment/:style/missing.png",
|
13
13
|
:default_style => :original,
|
@@ -77,14 +77,16 @@ module Paperclip
|
|
77
77
|
return nil if uploaded_file.nil?
|
78
78
|
|
79
79
|
if uploaded_file.respond_to?(:[])
|
80
|
+
uploaded_file = uploaded_file.to_mash
|
81
|
+
|
80
82
|
@queued_for_write[:original] = uploaded_file['tempfile']
|
81
|
-
instance_write(:file_name, uploaded_file['filename'].strip.gsub(/[^\w\d\.\-]+/, '_')
|
82
|
-
instance_write(:content_type, uploaded_file['content_type'].strip)
|
83
|
-
instance_write(:file_size, uploaded_file['size'].to_i)
|
83
|
+
instance_write(:file_name, uploaded_file['filename'].strip.gsub(/[^\w\d\.\-]+/, '_'))
|
84
|
+
instance_write(:content_type, uploaded_file['content_type'] ? uploaded_file['content_type'].strip : uploaded_file['tempfile'].content_type.to_s.strip)
|
85
|
+
instance_write(:file_size, uploaded_file['size'] ? uploaded_file['size'].to_i : uploaded_file['tempfile'].size.to_i)
|
84
86
|
instance_write(:updated_at, Time.now)
|
85
87
|
else
|
86
|
-
@queued_for_write[:original] = uploaded_file.
|
87
|
-
instance_write(:file_name, uploaded_file.original_filename.strip.gsub(/[^\w\d\.\-]+/, '_')
|
88
|
+
@queued_for_write[:original] = uploaded_file.tempfile
|
89
|
+
instance_write(:file_name, uploaded_file.original_filename.strip.gsub(/[^\w\d\.\-]+/, '_'))
|
88
90
|
instance_write(:content_type, uploaded_file.content_type.to_s.strip)
|
89
91
|
instance_write(:file_size, uploaded_file.size.to_i)
|
90
92
|
instance_write(:updated_at, Time.now)
|
@@ -111,7 +113,7 @@ module Paperclip
|
|
111
113
|
# update time appended to the url
|
112
114
|
def url style = default_style, include_updated_timestamp = true
|
113
115
|
the_url = original_filename.nil? ? interpolate(@default_url, style) : interpolate(@url, style)
|
114
|
-
include_updated_timestamp && updated_at ? [the_url, updated_at].compact.join(the_url.include?("?") ? "&" : "?") : the_url
|
116
|
+
include_updated_timestamp && updated_at ? [the_url, updated_at.to_time.to_i].compact.join(the_url.include?("?") ? "&" : "?") : the_url
|
115
117
|
end
|
116
118
|
|
117
119
|
# Returns the path of the attachment as defined by the :path option. If the
|
@@ -272,8 +274,8 @@ module Paperclip
|
|
272
274
|
end
|
273
275
|
|
274
276
|
def valid_assignment? file #:nodoc:
|
275
|
-
if file.
|
276
|
-
file[:filename]
|
277
|
+
if file.is_a?(Hash) || (defined?(Mash) && file.is_a?(Mash))
|
278
|
+
file[:filename] || file['filename']
|
277
279
|
else
|
278
280
|
file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
|
279
281
|
end
|
@@ -50,25 +50,25 @@ module Paperclip
|
|
50
50
|
def timestamp attachment, style
|
51
51
|
attachment.instance_read(:updated_at).to_s
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
def web_root attachment, style
|
55
55
|
if Object.const_defined?('Merb')
|
56
56
|
merb_root(attachment, style)
|
57
|
-
elsif Object.const_defined("
|
57
|
+
elsif Object.const_defined("Rails")
|
58
58
|
rails_root(attachment, style)
|
59
59
|
else
|
60
60
|
""
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
# Returns the
|
64
|
+
# Returns the Rails.root constant.
|
65
65
|
def rails_root attachment, style
|
66
|
-
Object.const_defined?('
|
66
|
+
Object.const_defined?('Rails') ? Rails.root : nil
|
67
67
|
end
|
68
68
|
|
69
|
-
# Returns the
|
69
|
+
# Returns the Rails.env constant.
|
70
70
|
def rails_env attachment, style
|
71
|
-
Object.const_defined?('
|
71
|
+
Object.const_defined?('Rails') ? Rails.env : nil
|
72
72
|
end
|
73
73
|
|
74
74
|
def merb_root attachment, style
|
@@ -82,7 +82,7 @@ module Paperclip
|
|
82
82
|
# Returns the snake cased, pluralized version of the class name.
|
83
83
|
# e.g. "users" for the User class.
|
84
84
|
def class attachment, style
|
85
|
-
attachment.instance.class.to_s.
|
85
|
+
attachment.instance.class.to_s.underscore.pluralize
|
86
86
|
end
|
87
87
|
|
88
88
|
# Returns the basename of the file. e.g. "file" for "file.jpg"
|
@@ -4,7 +4,8 @@ module IOStream
|
|
4
4
|
|
5
5
|
# Returns a Tempfile containing the contents of the readable object.
|
6
6
|
def to_tempfile
|
7
|
-
|
7
|
+
name = respond_to?(:original_filename) ? original_filename : (respond_to?(:path) ? path : "stream")
|
8
|
+
tempfile = Paperclip::Tempfile.new(File.basename(name))
|
8
9
|
tempfile.binmode
|
9
10
|
self.stream_to(tempfile)
|
10
11
|
end
|
@@ -25,7 +26,7 @@ module IOStream
|
|
25
26
|
while self.read(in_blocks_of, buffer) do
|
26
27
|
dstio.write(buffer)
|
27
28
|
end
|
28
|
-
dstio.rewind
|
29
|
+
dstio.rewind
|
29
30
|
dstio
|
30
31
|
end
|
31
32
|
end
|
@@ -55,4 +56,4 @@ if defined? Tempfile
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
58
|
-
end
|
59
|
+
end
|
data/lib/dm-paperclip/storage.rb
CHANGED
@@ -8,21 +8,21 @@ module Paperclip
|
|
8
8
|
# * +path+: The location of the repository of attachments on disk. This can (and, in
|
9
9
|
# almost all cases, should) be coordinated with the value of the +url+ option to
|
10
10
|
# allow files to be saved into a place where Apache can serve them without
|
11
|
-
# hitting your app. Defaults to
|
11
|
+
# hitting your app. Defaults to
|
12
12
|
# ":rails_root/public/:attachment/:id/:style/:basename.:extension"
|
13
|
-
# By default this places the files in the app's public directory which can be served
|
14
|
-
# directly. If you are using capistrano for deployment, a good idea would be to
|
15
|
-
# make a symlink to the capistrano-created system directory from inside your app's
|
13
|
+
# By default this places the files in the app's public directory which can be served
|
14
|
+
# directly. If you are using capistrano for deployment, a good idea would be to
|
15
|
+
# make a symlink to the capistrano-created system directory from inside your app's
|
16
16
|
# public directory.
|
17
17
|
# See Paperclip::Attachment#interpolate for more information on variable interpolaton.
|
18
18
|
# :path => "/var/app/attachments/:class/:id/:style/:basename.:extension"
|
19
19
|
module Filesystem
|
20
20
|
def self.extended base
|
21
21
|
end
|
22
|
-
|
23
|
-
def exists?(
|
22
|
+
|
23
|
+
def exists?(style_name = default_style)
|
24
24
|
if original_filename
|
25
|
-
File.exist?(path(
|
25
|
+
File.exist?(path(style_name))
|
26
26
|
else
|
27
27
|
false
|
28
28
|
end
|
@@ -30,18 +30,16 @@ module Paperclip
|
|
30
30
|
|
31
31
|
# Returns representation of the data of the file assigned to the given
|
32
32
|
# style, in the format most representative of the current storage.
|
33
|
-
def to_file
|
34
|
-
@queued_for_write[
|
33
|
+
def to_file style_name = default_style
|
34
|
+
@queued_for_write[style_name] || (File.new(path(style_name), 'rb') if exists?(style_name))
|
35
35
|
end
|
36
|
-
alias_method :to_io, :to_file
|
37
36
|
|
38
37
|
def flush_writes #:nodoc:
|
39
|
-
@queued_for_write.each do |
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
FileUtils.
|
44
|
-
FileUtils.chmod(0644, path(style))
|
38
|
+
@queued_for_write.each do |style_name, file|
|
39
|
+
FileUtils.mkdir_p(File.dirname(path(style_name)))
|
40
|
+
log("saving #{path(style_name)}")
|
41
|
+
FileUtils.cp(file.path, path(style_name))
|
42
|
+
FileUtils.chmod(0644, path(style_name))
|
45
43
|
end
|
46
44
|
@queued_for_write = {}
|
47
45
|
end
|
@@ -79,26 +77,26 @@ module Paperclip
|
|
79
77
|
# database.yml file, so different environments can use different accounts:
|
80
78
|
# development:
|
81
79
|
# access_key_id: 123...
|
82
|
-
# secret_access_key: 123...
|
80
|
+
# secret_access_key: 123...
|
83
81
|
# test:
|
84
82
|
# access_key_id: abc...
|
85
|
-
# secret_access_key: abc...
|
83
|
+
# secret_access_key: abc...
|
86
84
|
# production:
|
87
85
|
# access_key_id: 456...
|
88
|
-
# secret_access_key: 456...
|
86
|
+
# secret_access_key: 456...
|
89
87
|
# This is not required, however, and the file may simply look like this:
|
90
88
|
# access_key_id: 456...
|
91
|
-
# secret_access_key: 456...
|
89
|
+
# secret_access_key: 456...
|
92
90
|
# In which case, those access keys will be used in all environments. You can also
|
93
91
|
# put your bucket name in this file, instead of adding it to the code directly.
|
94
|
-
# This is useful when you want the same account but a different bucket for
|
92
|
+
# This is useful when you want the same account but a different bucket for
|
95
93
|
# development versus production.
|
96
94
|
# * +s3_permissions+: This is a String that should be one of the "canned" access
|
97
95
|
# policies that S3 provides (more information can be found here:
|
98
96
|
# http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html#RESTCannedAccessPolicies)
|
99
|
-
# The default for Paperclip is
|
100
|
-
# * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
|
101
|
-
# 'http' or 'https'. Defaults to 'http' when your :s3_permissions are
|
97
|
+
# The default for Paperclip is :public_read.
|
98
|
+
# * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
|
99
|
+
# 'http' or 'https'. Defaults to 'http' when your :s3_permissions are :public_read (the
|
102
100
|
# default), and 'https' when your :s3_permissions are anything else.
|
103
101
|
# * +s3_headers+: A hash of headers such as {'Expires' => 1.year.from_now.httpdate}
|
104
102
|
# * +bucket+: This is the name of the S3 bucket that will store your files. Remember
|
@@ -112,7 +110,7 @@ module Paperclip
|
|
112
110
|
# * +url+: There are three options for the S3 url. You can choose to have the bucket's name
|
113
111
|
# placed domain-style (bucket.s3.amazonaws.com) or path-style (s3.amazonaws.com/bucket).
|
114
112
|
# Lastly, you can specify a CNAME (which requires the CNAME to be specified as
|
115
|
-
# :s3_alias_url. You can read more about CNAMEs and S3 at
|
113
|
+
# :s3_alias_url. You can read more about CNAMEs and S3 at
|
116
114
|
# http://docs.amazonwebservices.com/AmazonS3/latest/index.html?VirtualHosting.html
|
117
115
|
# Normally, this won't matter in the slightest and you can leave the default (which is
|
118
116
|
# path-style, or :s3_path_url). But in some cases paths don't work and you need to use
|
@@ -128,19 +126,27 @@ module Paperclip
|
|
128
126
|
# separate parts of your file name.
|
129
127
|
module S3
|
130
128
|
def self.extended base
|
131
|
-
|
132
|
-
|
133
|
-
|
129
|
+
begin
|
130
|
+
require 'aws/s3'
|
131
|
+
rescue LoadError => e
|
132
|
+
e.message << " (You may need to install the aws-s3 gem)"
|
133
|
+
raise e
|
134
|
+
end
|
135
|
+
|
134
136
|
base.instance_eval do
|
135
137
|
@s3_credentials = parse_credentials(@options[:s3_credentials])
|
136
138
|
@bucket = @options[:bucket] || @s3_credentials[:bucket]
|
137
139
|
@bucket = @bucket.call(self) if @bucket.is_a?(Proc)
|
138
140
|
@s3_options = @options[:s3_options] || {}
|
139
|
-
@s3_permissions = @options[:s3_permissions] ||
|
140
|
-
@s3_protocol = @options[:s3_protocol] || (@s3_permissions ==
|
141
|
+
@s3_permissions = @options[:s3_permissions] || :public_read
|
142
|
+
@s3_protocol = @options[:s3_protocol] || (@s3_permissions == :public_read ? 'http' : 'https')
|
141
143
|
@s3_headers = @options[:s3_headers] || {}
|
142
144
|
@s3_host_alias = @options[:s3_host_alias]
|
143
145
|
@url = ":s3_path_url" unless @url.to_s.match(/^:s3.*url$/)
|
146
|
+
AWS::S3::Base.establish_connection!( @s3_options.merge(
|
147
|
+
:access_key_id => @s3_credentials[:access_key_id],
|
148
|
+
:secret_access_key => @s3_credentials[:secret_access_key]
|
149
|
+
))
|
144
150
|
end
|
145
151
|
Paperclip.interpolates(:s3_alias_url) do |attachment, style|
|
146
152
|
"#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
@@ -152,15 +158,9 @@ module Paperclip
|
|
152
158
|
"#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
|
153
159
|
end
|
154
160
|
end
|
155
|
-
|
156
|
-
def
|
157
|
-
|
158
|
-
@s3_credentials[:secret_access_key],
|
159
|
-
@s3_options)
|
160
|
-
end
|
161
|
-
|
162
|
-
def s3_bucket
|
163
|
-
@s3_bucket ||= s3.bucket(@bucket, true, @s3_permissions)
|
161
|
+
|
162
|
+
def expiring_url(time = 3600)
|
163
|
+
AWS::S3::S3Object.url_for(path, bucket_name, :expires_in => time )
|
164
164
|
end
|
165
165
|
|
166
166
|
def bucket_name
|
@@ -172,16 +172,26 @@ module Paperclip
|
|
172
172
|
end
|
173
173
|
|
174
174
|
def parse_credentials creds
|
175
|
-
creds = find_credentials(creds).to_mash
|
175
|
+
creds = find_credentials(creds).to_mash.stringify_keys!
|
176
176
|
if defined? Merb && Merb.respond_to?(:env)
|
177
|
-
(creds[Merb.env] || creds)
|
177
|
+
(creds[Merb.env] || creds).symbolize_keys
|
178
|
+
elsif defined? RAILS_ENV
|
179
|
+
(creds[RAILS_ENV] || creds).symbolize_keys
|
180
|
+
elsif defined? Rails && Rails.respond_to(:env)
|
181
|
+
(creds[Rails.env] || creds).symbolize_keys
|
182
|
+
elsif defined? RACK_ENV
|
183
|
+
(creds[RACK_ENV] || creds).symbolize_keys
|
178
184
|
else
|
179
|
-
|
185
|
+
creds.symbolize_keys
|
180
186
|
end
|
181
187
|
end
|
182
|
-
|
188
|
+
|
183
189
|
def exists?(style = default_style)
|
184
|
-
|
190
|
+
if original_filename
|
191
|
+
AWS::S3::S3Object.exists?(path(style), bucket_name)
|
192
|
+
else
|
193
|
+
false
|
194
|
+
end
|
185
195
|
end
|
186
196
|
|
187
197
|
def s3_protocol
|
@@ -191,18 +201,24 @@ module Paperclip
|
|
191
201
|
# Returns representation of the data of the file assigned to the given
|
192
202
|
# style, in the format most representative of the current storage.
|
193
203
|
def to_file style = default_style
|
194
|
-
@queued_for_write[style]
|
204
|
+
return @queued_for_write[style] if @queued_for_write[style]
|
205
|
+
file = Tempfile.new(path(style))
|
206
|
+
file.write(AWS::S3::S3Object.value(path(style), bucket_name))
|
207
|
+
file.rewind
|
208
|
+
return file
|
195
209
|
end
|
196
|
-
alias_method :to_io, :to_file
|
197
210
|
|
198
211
|
def flush_writes #:nodoc:
|
199
212
|
@queued_for_write.each do |style, file|
|
200
213
|
begin
|
201
214
|
log("saving #{path(style)}")
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
215
|
+
AWS::S3::S3Object.store(path(style),
|
216
|
+
file,
|
217
|
+
bucket_name,
|
218
|
+
{:content_type => instance_read(:content_type),
|
219
|
+
:access => @s3_permissions,
|
220
|
+
}.merge(@s3_headers))
|
221
|
+
rescue AWS::S3::ResponseError => e
|
206
222
|
raise
|
207
223
|
end
|
208
224
|
end
|
@@ -213,22 +229,20 @@ module Paperclip
|
|
213
229
|
@queued_for_delete.each do |path|
|
214
230
|
begin
|
215
231
|
log("deleting #{path}")
|
216
|
-
|
217
|
-
|
218
|
-
end
|
219
|
-
rescue RightAws::AwsError
|
232
|
+
AWS::S3::S3Object.delete(path, bucket_name)
|
233
|
+
rescue AWS::S3::ResponseError
|
220
234
|
# Ignore this.
|
221
235
|
end
|
222
236
|
end
|
223
237
|
@queued_for_delete = []
|
224
238
|
end
|
225
|
-
|
239
|
+
|
226
240
|
def find_credentials creds
|
227
241
|
case creds
|
228
242
|
when File
|
229
|
-
YAML.
|
243
|
+
YAML::load(ERB.new(File.read(creds.path)).result)
|
230
244
|
when String
|
231
|
-
YAML.
|
245
|
+
YAML::load(ERB.new(File.read(creds)).result)
|
232
246
|
when Hash
|
233
247
|
creds
|
234
248
|
else
|