paperclip 3.1.2 → 3.1.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of paperclip might be problematic. Click here for more details.
- data/NEWS +19 -0
- data/README.md +9 -8
- data/lib/paperclip.rb +3 -1
- data/lib/paperclip/attachment.rb +21 -0
- data/lib/paperclip/content_type_detector.rb +67 -0
- data/lib/paperclip/geometry.rb +1 -1
- data/lib/paperclip/interpolations.rb +9 -3
- data/lib/paperclip/io_adapters/abstract_adapter.rb +4 -18
- data/lib/paperclip/io_adapters/file_adapter.rb +1 -12
- data/lib/paperclip/io_adapters/nil_adapter.rb +1 -1
- data/lib/paperclip/io_adapters/stringio_adapter.rb +1 -0
- data/lib/paperclip/io_adapters/uri_adapter.rb +42 -0
- data/lib/paperclip/storage/fog.rb +4 -9
- data/lib/paperclip/storage/s3.rb +13 -13
- data/lib/paperclip/version.rb +1 -1
- data/paperclip.gemspec +3 -1
- data/test/attachment_test.rb +30 -0
- data/test/content_type_detector_test.rb +40 -0
- data/test/integration_test.rb +34 -1
- data/test/interpolations_test.rb +12 -0
- data/test/io_adapters/abstract_adapter_test.rb +2 -3
- data/test/io_adapters/attachment_adapter_test.rb +7 -7
- data/test/io_adapters/stringio_adapter_test.rb +10 -0
- data/test/io_adapters/uri_adapter_test.rb +82 -0
- data/test/storage/fog_test.rb +25 -10
- data/test/storage/s3_test.rb +15 -0
- metadata +44 -6
data/NEWS
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
New in 3.1.4:
|
2
|
+
|
3
|
+
* Bug fix: Allow user to be able to set path without `:style` attribute and not raising an error.
|
4
|
+
This is a regression introduced in 3.1.3, and that feature will be postponed to another minor
|
5
|
+
release instead.
|
6
|
+
* Feature: Allow for URI Adapter as an optional paperclip io adapter.
|
7
|
+
|
8
|
+
New in 3.1.3:
|
9
|
+
|
10
|
+
* Bug fix: Copy empty attachment between instances is now working.
|
11
|
+
* Bug fix: Correctly rescue Fog error.
|
12
|
+
* Bug fix: Using default path and url options in Fog storage now work as expected.
|
13
|
+
* Bug fix: `Attachment#s3_protocol` now returns a protocol without colon suffix.
|
14
|
+
* Feature: Paperclip will now raise an error if multiple styles are defined but no `:style`
|
15
|
+
interpolation exists in `:path`.
|
16
|
+
* Feature: Add support for `#{attachment}_created_at` field
|
17
|
+
* Bug fix: Paperclip now gracefully handles msising file command.
|
18
|
+
* Bug fix: `StringIOAdapter` now accepts content type.
|
19
|
+
|
1
20
|
New in 3.1.2:
|
2
21
|
|
3
22
|
* Bug fix: #remove_attachment on 3.1.0 and 3.1.1 mistakenly trying to remove the column that has
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Paperclip
|
2
2
|
=========
|
3
3
|
|
4
|
-
[![Build Status](https://secure.travis-ci.org/thoughtbot/paperclip.png?branch=master)](http://travis-ci.org/thoughtbot/paperclip) [![Dependency Status](https://gemnasium.com/thoughtbot/paperclip.png?travis)](https://gemnasium.com/thoughtbot/paperclip)
|
4
|
+
[![Build Status](https://secure.travis-ci.org/thoughtbot/paperclip.png?branch=master)](http://travis-ci.org/thoughtbot/paperclip) [![Dependency Status](https://gemnasium.com/thoughtbot/paperclip.png?travis)](https://gemnasium.com/thoughtbot/paperclip) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/thoughtbot/paperclip)
|
5
5
|
|
6
6
|
Paperclip is intended as an easy file attachment library for Active Record. The
|
7
7
|
intent behind it was to keep setup as easy as possible and to treat files as
|
@@ -89,6 +89,7 @@ Quick Start
|
|
89
89
|
In your model:
|
90
90
|
|
91
91
|
class User < ActiveRecord::Base
|
92
|
+
attr_accessible :avatar
|
92
93
|
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
|
93
94
|
end
|
94
95
|
|
@@ -108,7 +109,7 @@ In your migrations:
|
|
108
109
|
|
109
110
|
In your edit and new views:
|
110
111
|
|
111
|
-
<%= form_for
|
112
|
+
<%= form_for @user, :url => users_path, :html => { :multipart => true } do |form| %>
|
112
113
|
<%= form.file_field :avatar %>
|
113
114
|
<% end %>
|
114
115
|
|
@@ -202,7 +203,7 @@ An example Rails initializer would look something like this:
|
|
202
203
|
Paperclip::Attachment.default_options[:storage] = :fog
|
203
204
|
Paperclip::Attachment.default_options[:fog_credentials] = {:provider => "Local", :local_root => "#{Rails.root}/public"}
|
204
205
|
Paperclip::Attachment.default_options[:fog_directory] = ""
|
205
|
-
Paperclip::Attachment.default_options[:fog_host] = "http://localhost:3000"
|
206
|
+
Paperclip::Attachment.default_options[:fog_host] = "http://localhost:3000"
|
206
207
|
```
|
207
208
|
|
208
209
|
Migrations
|
@@ -274,13 +275,13 @@ gems along side with Paperclip:
|
|
274
275
|
|
275
276
|
The files that are assigned as attachments are, by default, placed in the
|
276
277
|
directory specified by the `:path` option to `has_attached_file`. By default, this
|
277
|
-
location is `:rails_root/public/system/:attachment/:
|
278
|
-
location was chosen because on standard Capistrano deployments, the
|
278
|
+
location is `:rails_root/public/system/:class/:attachment/:id_partition/:style/:filename`.
|
279
|
+
This location was chosen because on standard Capistrano deployments, the
|
279
280
|
`public/system` directory is symlinked to the app's shared directory, meaning it
|
280
281
|
will survive between deployments. For example, using that `:path`, you may have a
|
281
282
|
file at
|
282
283
|
|
283
|
-
/data/myapp/releases/20081229172410/public/system/
|
284
|
+
/data/myapp/releases/20081229172410/public/system/users/avatar/000/000/013/small/my_pic.png
|
284
285
|
|
285
286
|
_**NOTE**: This is a change from previous versions of Paperclip, but is overall a
|
286
287
|
safer choice for the default file store._
|
@@ -447,7 +448,7 @@ allowing custom styles and processors to be applied for specific model
|
|
447
448
|
instances, rather than applying defined styles and processors across all
|
448
449
|
instances.
|
449
450
|
|
450
|
-
Dynamic Styles:
|
451
|
+
### Dynamic Styles:
|
451
452
|
|
452
453
|
Imagine a user model that had different styles based on the role of the user.
|
453
454
|
Perhaps some users are bosses (e.g. a User model instance responds to #boss?)
|
@@ -460,7 +461,7 @@ look as follows where a boss will receive a `300x300` thumbnail otherwise a
|
|
460
461
|
has_attached_file :avatar, :styles => lambda { |attachment| { :thumb => (attachment.instance.boss? ? "300x300>" : "100x100>") }
|
461
462
|
end
|
462
463
|
|
463
|
-
Dynamic Processors:
|
464
|
+
### Dynamic Processors:
|
464
465
|
|
465
466
|
Another contrived example is a user model that is aware of which file processors
|
466
467
|
should be applied to it (beyond the implied `thumbnail` processor invoked when
|
data/lib/paperclip.rb
CHANGED
@@ -40,6 +40,7 @@ require 'paperclip/attachment'
|
|
40
40
|
require 'paperclip/attachment_options'
|
41
41
|
require 'paperclip/storage'
|
42
42
|
require 'paperclip/callbacks'
|
43
|
+
require 'paperclip/content_type_detector'
|
43
44
|
require 'paperclip/glue'
|
44
45
|
require 'paperclip/errors'
|
45
46
|
require 'paperclip/missing_attachment_styles'
|
@@ -102,7 +103,7 @@ module Paperclip
|
|
102
103
|
# that can control permissions. You can specify the full domain and path, but usually
|
103
104
|
# just an absolute path is sufficient. The leading slash *must* be included manually for
|
104
105
|
# absolute paths. The default value is
|
105
|
-
# "/system/:attachment/:
|
106
|
+
# "/system/:class/:attachment/:id_partition/:style/:filename". See
|
106
107
|
# Paperclip::Attachment#interpolate for more information on variable interpolaton.
|
107
108
|
# :url => "/:class/:attachment/:id/:style_:filename"
|
108
109
|
# :url => "http://some.other.host/stuff/:class/:id_:extension"
|
@@ -222,3 +223,4 @@ require 'paperclip/io_adapters/stringio_adapter'
|
|
222
223
|
require 'paperclip/io_adapters/nil_adapter'
|
223
224
|
require 'paperclip/io_adapters/attachment_adapter'
|
224
225
|
require 'paperclip/io_adapters/uploaded_file_adapter'
|
226
|
+
require 'paperclip/io_adapters/uri_adapter'
|
data/lib/paperclip/attachment.rb
CHANGED
@@ -99,6 +99,7 @@ module Paperclip
|
|
99
99
|
instance_write(:content_type, file.content_type.to_s.strip)
|
100
100
|
instance_write(:file_size, file.size)
|
101
101
|
instance_write(:fingerprint, file.fingerprint) if instance_respond_to?(:fingerprint)
|
102
|
+
instance_write(:created_at, Time.now) if has_enabled_but_unset_created_at?
|
102
103
|
instance_write(:updated_at, Time.now)
|
103
104
|
|
104
105
|
@dirty = true
|
@@ -254,6 +255,15 @@ module Paperclip
|
|
254
255
|
instance_read(:content_type)
|
255
256
|
end
|
256
257
|
|
258
|
+
# Returns the creation time of the file as originally assigned, and
|
259
|
+
# lives in the <attachment>_created_at attribute of the model.
|
260
|
+
def created_at
|
261
|
+
if able_to_store_created_at?
|
262
|
+
time = instance_read(:created_at)
|
263
|
+
time && time.to_f.to_i
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
257
267
|
# Returns the last modified time of the file as originally assigned, and
|
258
268
|
# lives in the <attachment>_updated_at attribute of the model.
|
259
269
|
def updated_at
|
@@ -429,6 +439,7 @@ module Paperclip
|
|
429
439
|
instance_write(:content_type, nil)
|
430
440
|
instance_write(:file_size, nil)
|
431
441
|
instance_write(:fingerprint, nil)
|
442
|
+
instance_write(:created_at, nil) if has_enabled_but_unset_created_at?
|
432
443
|
instance_write(:updated_at, nil)
|
433
444
|
end
|
434
445
|
|
@@ -453,5 +464,15 @@ module Paperclip
|
|
453
464
|
filename
|
454
465
|
end
|
455
466
|
end
|
467
|
+
|
468
|
+
# Check if attachment database table has a created_at field
|
469
|
+
def able_to_store_created_at?
|
470
|
+
@instance.respond_to?("#{name}_created_at".to_sym)
|
471
|
+
end
|
472
|
+
|
473
|
+
# Check if attachment database table has a created_at field which is not yet set
|
474
|
+
def has_enabled_but_unset_created_at?
|
475
|
+
able_to_store_created_at? && !instance_read(:created_at)
|
476
|
+
end
|
456
477
|
end
|
457
478
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class ContentTypeDetector
|
3
|
+
EMPTY_TYPE = "inode/x-empty"
|
4
|
+
SENSIBLE_DEFAULT = "application/octet-stream"
|
5
|
+
|
6
|
+
def initialize(filename)
|
7
|
+
@filename = filename
|
8
|
+
end
|
9
|
+
|
10
|
+
def detect
|
11
|
+
if blank?
|
12
|
+
SENSIBLE_DEFAULT
|
13
|
+
elsif empty?
|
14
|
+
EMPTY_TYPE
|
15
|
+
elsif !match?
|
16
|
+
type_from_file_command
|
17
|
+
elsif !multiple?
|
18
|
+
possible_types.first
|
19
|
+
else
|
20
|
+
best_type_match
|
21
|
+
end.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def empty?
|
27
|
+
File.exists?(@filename) && File.size(@filename) == 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def blank?
|
31
|
+
@filename.nil? || @filename.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def possible_types
|
35
|
+
@possible_types ||= MIME::Types.type_for(@filename)
|
36
|
+
end
|
37
|
+
|
38
|
+
def match?
|
39
|
+
possible_types.length > 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def multiple?
|
43
|
+
possible_types.length > 1
|
44
|
+
end
|
45
|
+
|
46
|
+
def best_type_match
|
47
|
+
official_types = possible_types.reject {|type| type.content_type.match(/\/x-/) }
|
48
|
+
(official_types.first || possible_types.first).content_type
|
49
|
+
end
|
50
|
+
|
51
|
+
def type_from_file_command
|
52
|
+
type = begin
|
53
|
+
# On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
|
54
|
+
Paperclip.run("file", "-b --mime :file", :file => @filename)
|
55
|
+
rescue Cocaine::CommandLineError => e
|
56
|
+
Paperclip.log("Error while determining content type: #{e}")
|
57
|
+
SENSIBLE_DEFAULT
|
58
|
+
end
|
59
|
+
|
60
|
+
if type.match(/\(.*?\)/)
|
61
|
+
type = SENSIBLE_DEFAULT
|
62
|
+
end
|
63
|
+
type.split(/[:;\s]+/)[0]
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
data/lib/paperclip/geometry.rb
CHANGED
@@ -149,7 +149,7 @@ module Paperclip
|
|
149
149
|
# scale to the requested geometry and preserve the aspect ratio
|
150
150
|
def scale_to(new_geometry)
|
151
151
|
scale = [new_geometry.width.to_f / self.width.to_f , new_geometry.height.to_f / self.height.to_f].min
|
152
|
-
Paperclip::Geometry.new
|
152
|
+
Paperclip::Geometry.new((self.width * scale).round, (self.height * scale).round)
|
153
153
|
end
|
154
154
|
end
|
155
155
|
end
|
@@ -97,9 +97,12 @@ module Paperclip
|
|
97
97
|
File.extname(attachment.original_filename).gsub(/^\.+/, "")
|
98
98
|
end
|
99
99
|
|
100
|
-
# Returns an extension based on the content type. e.g. "jpeg" for
|
100
|
+
# Returns an extension based on the content type. e.g. "jpeg" for
|
101
|
+
# "image/jpeg". If the style has a specified format, it will override the
|
102
|
+
# content-type detection.
|
103
|
+
#
|
101
104
|
# Each mime type generally has multiple extensions associated with it, so
|
102
|
-
# if the extension from
|
105
|
+
# if the extension from the original filename is one of these extensions,
|
103
106
|
# that extension is used, otherwise, the first in the list is used.
|
104
107
|
def content_type_extension attachment, style_name
|
105
108
|
mime_type = MIME::Types[attachment.content_type]
|
@@ -110,7 +113,10 @@ module Paperclip
|
|
110
113
|
end
|
111
114
|
|
112
115
|
original_extension = extension(attachment, style_name)
|
113
|
-
|
116
|
+
style = attachment.styles[style_name.to_s.to_sym]
|
117
|
+
if style && style[:format]
|
118
|
+
style[:format].to_s
|
119
|
+
elsif extensions_for_mime_type.include? original_extension
|
114
120
|
original_extension
|
115
121
|
elsif !extensions_for_mime_type.empty?
|
116
122
|
extensions_for_mime_type.first
|
@@ -13,6 +13,10 @@ module Paperclip
|
|
13
13
|
@tempfile.read(length, buffer)
|
14
14
|
end
|
15
15
|
|
16
|
+
def inspect
|
17
|
+
"#{self.class}: #{self.original_filename}"
|
18
|
+
end
|
19
|
+
|
16
20
|
private
|
17
21
|
|
18
22
|
def destination
|
@@ -23,23 +27,5 @@ module Paperclip
|
|
23
27
|
FileUtils.cp(src.path, destination.path)
|
24
28
|
destination
|
25
29
|
end
|
26
|
-
|
27
|
-
def best_content_type_option(types)
|
28
|
-
best = types.reject {|type| type.content_type.match(/\/x-/) }
|
29
|
-
if best.size == 0
|
30
|
-
types.first.content_type
|
31
|
-
else
|
32
|
-
best.first.content_type
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def type_from_file_command
|
37
|
-
# On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
|
38
|
-
type = (File.extname(self.path.to_s)).downcase
|
39
|
-
type = "octet-stream" if type.empty?
|
40
|
-
mime_type = Paperclip.run("file", "-b --mime :file", :file => self.path).split(/[:;\s]+/)[0]
|
41
|
-
mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
|
42
|
-
mime_type
|
43
|
-
end
|
44
30
|
end
|
45
31
|
end
|
@@ -11,20 +11,9 @@ module Paperclip
|
|
11
11
|
@original_filename = @target.original_filename if @target.respond_to?(:original_filename)
|
12
12
|
@original_filename ||= File.basename(@target.path)
|
13
13
|
@tempfile = copy_to_tempfile(@target)
|
14
|
-
@content_type =
|
14
|
+
@content_type = ContentTypeDetector.new(@target.path).detect
|
15
15
|
@size = File.size(@target)
|
16
16
|
end
|
17
|
-
|
18
|
-
def calculate_content_type
|
19
|
-
types = MIME::Types.type_for(original_filename)
|
20
|
-
if types.length == 0
|
21
|
-
type_from_file_command
|
22
|
-
elsif types.length == 1
|
23
|
-
types.first.content_type
|
24
|
-
else
|
25
|
-
best_content_type_option(types)
|
26
|
-
end
|
27
|
-
end
|
28
17
|
end
|
29
18
|
end
|
30
19
|
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module Paperclip
|
4
|
+
class UriAdapter < AbstractAdapter
|
5
|
+
def initialize(target)
|
6
|
+
@target = target
|
7
|
+
@content = download_content
|
8
|
+
cache_current_values
|
9
|
+
@tempfile = copy_to_tempfile(@content)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_writer :original_filename, :content_type
|
13
|
+
private
|
14
|
+
|
15
|
+
def download_content
|
16
|
+
open(@target)
|
17
|
+
end
|
18
|
+
|
19
|
+
def cache_current_values
|
20
|
+
@original_filename = @target.path.split("/").last
|
21
|
+
@original_filename ||= "index.html"
|
22
|
+
@original_filename = @original_filename.strip
|
23
|
+
|
24
|
+
@content_type = @content.content_type if @content.respond_to?(:content_type)
|
25
|
+
@content_type ||= "text/html"
|
26
|
+
|
27
|
+
@size = @content.size
|
28
|
+
end
|
29
|
+
|
30
|
+
def copy_to_tempfile(src)
|
31
|
+
while data = src.read(16*1024)
|
32
|
+
destination.write(data)
|
33
|
+
end
|
34
|
+
destination.rewind
|
35
|
+
destination
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Paperclip.io_adapters.register Paperclip::UriAdapter do |target|
|
41
|
+
target.kind_of?(URI)
|
42
|
+
end
|
@@ -42,7 +42,7 @@ module Paperclip
|
|
42
42
|
|
43
43
|
base.instance_eval do
|
44
44
|
unless @options[:url].to_s.match(/^:fog.*url$/)
|
45
|
-
@options[:path] = @options[:path].gsub(/:url/, @options[:url])
|
45
|
+
@options[:path] = @options[:path].gsub(/:url/, @options[:url]).gsub(/^:rails_root\/public\/system\//, '')
|
46
46
|
@options[:url] = ':fog_public_url'
|
47
47
|
end
|
48
48
|
Paperclip.interpolates(:fog_public_url) do |attachment, style|
|
@@ -120,7 +120,7 @@ module Paperclip
|
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
|
-
def expiring_url(time = 3600, style = default_style)
|
123
|
+
def expiring_url(time = (Time.now + 3600), style = default_style)
|
124
124
|
expiring_url = directory.files.get_http_url(path(style), time)
|
125
125
|
|
126
126
|
if @options[:fog_host]
|
@@ -142,7 +142,7 @@ module Paperclip
|
|
142
142
|
file = directory.files.get(path(style))
|
143
143
|
local_file.write(file.body)
|
144
144
|
local_file.close
|
145
|
-
rescue Fog::Errors::Error => e
|
145
|
+
rescue ::Fog::Errors::Error => e
|
146
146
|
warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
|
147
147
|
false
|
148
148
|
end
|
@@ -159,12 +159,7 @@ module Paperclip
|
|
159
159
|
|
160
160
|
def host_name_for_directory
|
161
161
|
if @options[:fog_directory].to_s =~ Fog::AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX
|
162
|
-
#
|
163
|
-
"#{@options[:fog_directory]}."
|
164
|
-
|
165
|
-
# Should be modified to this:
|
166
|
-
# "#{@options[:fog_directory]}.s3.amazonaws.com"
|
167
|
-
# When fog with https://github.com/fog/fog/pull/857 gets released
|
162
|
+
"#{@options[:fog_directory]}.s3.amazonaws.com"
|
168
163
|
else
|
169
164
|
"s3.amazonaws.com/#{@options[:fog_directory]}"
|
170
165
|
end
|
data/lib/paperclip/storage/s3.rb
CHANGED
@@ -150,13 +150,13 @@ module Paperclip
|
|
150
150
|
end
|
151
151
|
|
152
152
|
Paperclip.interpolates(:s3_alias_url) do |attachment, style|
|
153
|
-
"#{attachment.s3_protocol(style)}//#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
153
|
+
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
154
154
|
end unless Paperclip::Interpolations.respond_to? :s3_alias_url
|
155
155
|
Paperclip.interpolates(:s3_path_url) do |attachment, style|
|
156
|
-
"#{attachment.s3_protocol(style)}//#{attachment.s3_host_name}/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
156
|
+
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_name}/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
157
157
|
end unless Paperclip::Interpolations.respond_to? :s3_path_url
|
158
158
|
Paperclip.interpolates(:s3_domain_url) do |attachment, style|
|
159
|
-
"#{attachment.s3_protocol(style)}//#{attachment.bucket_name}.#{attachment.s3_host_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
159
|
+
"#{attachment.s3_protocol(style, true)}//#{attachment.bucket_name}.#{attachment.s3_host_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
160
160
|
end unless Paperclip::Interpolations.respond_to? :s3_domain_url
|
161
161
|
Paperclip.interpolates(:asset_host) do |attachment, style|
|
162
162
|
"#{attachment.path(style).gsub(%r{^/}, "")}"
|
@@ -276,15 +276,15 @@ module Paperclip
|
|
276
276
|
s3_permissions
|
277
277
|
end
|
278
278
|
|
279
|
-
def s3_protocol(style = default_style)
|
280
|
-
protocol =
|
281
|
-
|
279
|
+
def s3_protocol(style = default_style, with_colon = false)
|
280
|
+
protocol = @s3_protocol
|
281
|
+
protocol = protocol.call(style, self) if protocol.respond_to?(:call)
|
282
|
+
|
283
|
+
if with_colon && !protocol.empty?
|
284
|
+
"#{protocol}:"
|
282
285
|
else
|
283
|
-
|
286
|
+
protocol.to_s
|
284
287
|
end
|
285
|
-
|
286
|
-
protocol = protocol.split(":").first + ":" unless protocol.empty?
|
287
|
-
protocol
|
288
288
|
end
|
289
289
|
|
290
290
|
def create_bucket
|
@@ -343,6 +343,8 @@ module Paperclip
|
|
343
343
|
false
|
344
344
|
end
|
345
345
|
|
346
|
+
private
|
347
|
+
|
346
348
|
def find_credentials creds
|
347
349
|
case creds
|
348
350
|
when File
|
@@ -355,12 +357,10 @@ module Paperclip
|
|
355
357
|
raise ArgumentError, "Credentials are not a path, file, proc, or hash."
|
356
358
|
end
|
357
359
|
end
|
358
|
-
private :find_credentials
|
359
360
|
|
360
361
|
def use_secure_protocol?(style_name)
|
361
|
-
s3_protocol(style_name) == "https
|
362
|
+
s3_protocol(style_name) == "https"
|
362
363
|
end
|
363
|
-
private :use_secure_protocol?
|
364
364
|
end
|
365
365
|
end
|
366
366
|
end
|
data/lib/paperclip/version.rb
CHANGED
data/paperclip.gemspec
CHANGED
@@ -43,9 +43,11 @@ Gem::Specification.new do |s|
|
|
43
43
|
s.add_development_dependency('capybara')
|
44
44
|
s.add_development_dependency('bundler')
|
45
45
|
s.add_development_dependency('cocaine', '~> 0.2')
|
46
|
-
s.add_development_dependency('fog')
|
46
|
+
s.add_development_dependency('fog', '~> 1.4.0')
|
47
47
|
s.add_development_dependency('pry')
|
48
48
|
s.add_development_dependency('launchy')
|
49
49
|
s.add_development_dependency('rake')
|
50
50
|
s.add_development_dependency('fakeweb')
|
51
|
+
s.add_development_dependency('railties')
|
52
|
+
s.add_development_dependency('actionmailer')
|
51
53
|
end
|
data/test/attachment_test.rb
CHANGED
@@ -1101,6 +1101,36 @@ class AttachmentTest < Test::Unit::TestCase
|
|
1101
1101
|
assert_equal File.size(@file), @dummy.avatar.size
|
1102
1102
|
end
|
1103
1103
|
|
1104
|
+
context "and avatar_created_at column" do
|
1105
|
+
setup do
|
1106
|
+
ActiveRecord::Base.connection.add_column :dummies, :avatar_created_at, :timestamp
|
1107
|
+
rebuild_class
|
1108
|
+
@dummy = Dummy.new
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
should "not error when assigned an attachment" do
|
1112
|
+
assert_nothing_raised { @dummy.avatar = @file }
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
should "return the creation time when sent #avatar_created_at" do
|
1116
|
+
now = Time.now
|
1117
|
+
Time.stubs(:now).returns(now)
|
1118
|
+
@dummy.avatar = @file
|
1119
|
+
assert_equal now.to_i, @dummy.avatar.created_at
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
should "return the creation time when sent #avatar_created_at and the entry has been updated" do
|
1123
|
+
creation = 2.hours.ago
|
1124
|
+
now = Time.now
|
1125
|
+
Time.stubs(:now).returns(creation)
|
1126
|
+
@dummy.avatar = @file
|
1127
|
+
Time.stubs(:now).returns(now)
|
1128
|
+
@dummy.avatar = @file
|
1129
|
+
assert_equal creation.to_i, @dummy.avatar.created_at
|
1130
|
+
assert_not_equal now.to_i, @dummy.avatar.created_at
|
1131
|
+
end
|
1132
|
+
end
|
1133
|
+
|
1104
1134
|
context "and avatar_updated_at column" do
|
1105
1135
|
setup do
|
1106
1136
|
ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require './test/helper'
|
2
|
+
|
3
|
+
class ContentTypeDetectorTest < Test::Unit::TestCase
|
4
|
+
context 'given a name' do
|
5
|
+
should 'return a content type based on that name' do
|
6
|
+
@filename = "/path/to/something.jpg"
|
7
|
+
assert_equal "image/jpeg", Paperclip::ContentTypeDetector.new(@filename).detect
|
8
|
+
end
|
9
|
+
|
10
|
+
should 'return a content type based on the content of the file' do
|
11
|
+
tempfile = Tempfile.new("something")
|
12
|
+
tempfile.write("This is a file.")
|
13
|
+
tempfile.rewind
|
14
|
+
|
15
|
+
assert_equal "text/plain", Paperclip::ContentTypeDetector.new(tempfile.path).detect
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'return an empty content type if the file is empty' do
|
19
|
+
tempfile = Tempfile.new("something")
|
20
|
+
tempfile.rewind
|
21
|
+
|
22
|
+
assert_equal "inode/x-empty", Paperclip::ContentTypeDetector.new(tempfile.path).detect
|
23
|
+
end
|
24
|
+
|
25
|
+
should 'return a sensible default if no filename is supplied' do
|
26
|
+
assert_equal "application/octet-stream", Paperclip::ContentTypeDetector.new('').detect
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'return a sensible default if something goes wrong' do
|
30
|
+
@filename = "/path/to/something"
|
31
|
+
assert_equal "application/octet-stream", Paperclip::ContentTypeDetector.new(@filename).detect
|
32
|
+
end
|
33
|
+
|
34
|
+
should 'return a sensible default when the file command is missing' do
|
35
|
+
Paperclip.stubs(:run).raises(Cocaine::CommandLineError.new)
|
36
|
+
@filename = "/path/to/something"
|
37
|
+
assert_equal "application/octet-stream", Paperclip::ContentTypeDetector.new(@filename).detect
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/test/integration_test.rb
CHANGED
@@ -630,7 +630,6 @@ class IntegrationTest < Test::Unit::TestCase
|
|
630
630
|
|
631
631
|
context "with non-english character in the file name" do
|
632
632
|
setup do
|
633
|
-
|
634
633
|
@file.stubs(:original_filename).returns("クリップ.png")
|
635
634
|
@dummy.avatar = @file
|
636
635
|
end
|
@@ -641,4 +640,38 @@ class IntegrationTest < Test::Unit::TestCase
|
|
641
640
|
end
|
642
641
|
end
|
643
642
|
end
|
643
|
+
|
644
|
+
context "Copying attachments between models" do
|
645
|
+
setup do
|
646
|
+
rebuild_model
|
647
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
648
|
+
end
|
649
|
+
|
650
|
+
teardown { @file.close }
|
651
|
+
|
652
|
+
should "succeed when original attachment is a file" do
|
653
|
+
original = Dummy.new
|
654
|
+
original.avatar = @file
|
655
|
+
assert original.save
|
656
|
+
|
657
|
+
copy = Dummy.new
|
658
|
+
copy.avatar = original.avatar
|
659
|
+
assert copy.save
|
660
|
+
|
661
|
+
assert copy.avatar.present?
|
662
|
+
end
|
663
|
+
|
664
|
+
should "succeed when original attachment is empty" do
|
665
|
+
original = Dummy.create!
|
666
|
+
|
667
|
+
copy = Dummy.new
|
668
|
+
copy.avatar = @file
|
669
|
+
assert copy.save
|
670
|
+
assert copy.avatar.present?
|
671
|
+
|
672
|
+
copy.avatar = original.avatar
|
673
|
+
assert copy.save
|
674
|
+
assert !copy.avatar.present?
|
675
|
+
end
|
676
|
+
end
|
644
677
|
end
|
data/test/interpolations_test.rb
CHANGED
@@ -56,6 +56,7 @@ class InterpolationsTest < Test::Unit::TestCase
|
|
56
56
|
should "return the extension of the file based on the content type" do
|
57
57
|
attachment = mock
|
58
58
|
attachment.expects(:content_type).returns('image/jpeg')
|
59
|
+
attachment.expects(:styles).returns({})
|
59
60
|
interpolations = Paperclip::Interpolations
|
60
61
|
interpolations.expects(:extension).returns('random')
|
61
62
|
assert_equal "jpeg", interpolations.content_type_extension(attachment, :style)
|
@@ -64,6 +65,7 @@ class InterpolationsTest < Test::Unit::TestCase
|
|
64
65
|
should "return the original extension of the file if it matches a content type extension" do
|
65
66
|
attachment = mock
|
66
67
|
attachment.expects(:content_type).returns('image/jpeg')
|
68
|
+
attachment.expects(:styles).returns({})
|
67
69
|
interpolations = Paperclip::Interpolations
|
68
70
|
interpolations.expects(:extension).returns('jpe')
|
69
71
|
assert_equal "jpe", interpolations.content_type_extension(attachment, :style)
|
@@ -72,11 +74,21 @@ class InterpolationsTest < Test::Unit::TestCase
|
|
72
74
|
should "return the latter half of the content type of the extension if no match found" do
|
73
75
|
attachment = mock
|
74
76
|
attachment.expects(:content_type).at_least_once().returns('not/found')
|
77
|
+
attachment.expects(:styles).returns({})
|
75
78
|
interpolations = Paperclip::Interpolations
|
76
79
|
interpolations.expects(:extension).returns('random')
|
77
80
|
assert_equal "found", interpolations.content_type_extension(attachment, :style)
|
78
81
|
end
|
79
82
|
|
83
|
+
should "return the format if defined in the style, ignoring the content type" do
|
84
|
+
attachment = mock
|
85
|
+
attachment.expects(:content_type).returns('image/jpeg')
|
86
|
+
attachment.expects(:styles).returns({:style => {:format => "png"}})
|
87
|
+
interpolations = Paperclip::Interpolations
|
88
|
+
interpolations.expects(:extension).returns('random')
|
89
|
+
assert_equal "png", interpolations.content_type_extension(attachment, :style)
|
90
|
+
end
|
91
|
+
|
80
92
|
should "be able to handle numeric style names" do
|
81
93
|
attachment = mock(
|
82
94
|
:styles => {:"4" => {:format => :expected_extension}}
|
@@ -5,15 +5,14 @@ class AbstractAdapterTest < Test::Unit::TestCase
|
|
5
5
|
attr_accessor :original_file_name, :tempfile
|
6
6
|
|
7
7
|
def content_type
|
8
|
-
|
8
|
+
Paperclip::ContentTypeDetector.new(path).detect
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
context "content type from file command" do
|
13
13
|
setup do
|
14
14
|
@adapter = TestAdapter.new
|
15
|
-
@adapter.stubs(:path)
|
16
|
-
Paperclip.stubs(:run).returns("image/png\n")
|
15
|
+
@adapter.stubs(:path).returns("image.png")
|
17
16
|
end
|
18
17
|
|
19
18
|
should "return the content type without newline" do
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require './test/helper'
|
2
2
|
|
3
3
|
class AttachmentAdapterTest < Test::Unit::TestCase
|
4
|
-
|
4
|
+
|
5
5
|
def setup
|
6
6
|
rebuild_model :path => "tmp/:class/:attachment/:style/:filename", :styles => {:thumb => '50x50'}
|
7
7
|
@attachment = Dummy.new.avatar
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
context "for an attachment" do
|
11
11
|
setup do
|
12
12
|
@file = File.new(fixture_file("5k.png"))
|
@@ -53,19 +53,19 @@ class AttachmentAdapterTest < Test::Unit::TestCase
|
|
53
53
|
assert_equal expected.length, actual.length
|
54
54
|
assert_equal expected, actual
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
context "for a style" do
|
60
60
|
setup do
|
61
61
|
@file = File.new(fixture_file("5k.png"))
|
62
62
|
@file.binmode
|
63
63
|
|
64
64
|
@attachment.assign(@file)
|
65
|
-
|
65
|
+
|
66
66
|
@thumb = Tempfile.new("thumbnail").tap(&:binmode)
|
67
67
|
FileUtils.cp @attachment.queued_for_write[:thumb].path, @thumb.path
|
68
|
-
|
68
|
+
|
69
69
|
@attachment.save
|
70
70
|
@subject = Paperclip.io_adapters.for(@attachment.styles[:thumb])
|
71
71
|
end
|
@@ -108,6 +108,6 @@ class AttachmentAdapterTest < Test::Unit::TestCase
|
|
108
108
|
assert_equal expected.length, actual.length
|
109
109
|
assert_equal expected, actual
|
110
110
|
end
|
111
|
-
|
111
|
+
|
112
112
|
end
|
113
113
|
end
|
@@ -37,5 +37,15 @@ class StringioFileProxyTest < Test::Unit::TestCase
|
|
37
37
|
assert_equal "abc123", @subject.read
|
38
38
|
end
|
39
39
|
|
40
|
+
should 'accept a content_type' do
|
41
|
+
@subject.content_type = 'image/png'
|
42
|
+
assert_equal 'image/png', @subject.content_type
|
43
|
+
end
|
44
|
+
|
45
|
+
should 'accept an orgiginal_filename' do
|
46
|
+
@subject.original_filename = 'image.png'
|
47
|
+
assert_equal 'image.png', @subject.original_filename
|
48
|
+
end
|
49
|
+
|
40
50
|
end
|
41
51
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require './test/helper'
|
2
|
+
|
3
|
+
class UriProxyTest < Test::Unit::TestCase
|
4
|
+
context "a new instance" do
|
5
|
+
setup do
|
6
|
+
@open_return = StringIO.new("xxx")
|
7
|
+
@open_return.stubs(:content_type).returns("image/png")
|
8
|
+
Paperclip::UriAdapter.any_instance.stubs(:download_content).returns(@open_return)
|
9
|
+
@uri = URI.parse("http://thoughtbot.com/images/thoughtbot-logo.png")
|
10
|
+
@subject = Paperclip.io_adapters.for(@uri)
|
11
|
+
end
|
12
|
+
|
13
|
+
should "return a file name" do
|
14
|
+
assert_equal "thoughtbot-logo.png", @subject.original_filename
|
15
|
+
end
|
16
|
+
|
17
|
+
should "return a content type" do
|
18
|
+
assert_equal "image/png", @subject.content_type
|
19
|
+
end
|
20
|
+
|
21
|
+
should "return the size of the data" do
|
22
|
+
assert_equal @open_return.size, @subject.size
|
23
|
+
end
|
24
|
+
|
25
|
+
should "generate an MD5 hash of the contents" do
|
26
|
+
assert_equal Digest::MD5.hexdigest("xxx"), @subject.fingerprint
|
27
|
+
end
|
28
|
+
|
29
|
+
should "generate correct fingerprint after read" do
|
30
|
+
fingerprint = Digest::MD5.hexdigest(@subject.read)
|
31
|
+
assert_equal fingerprint, @subject.fingerprint
|
32
|
+
end
|
33
|
+
|
34
|
+
should "generate same fingerprint" do
|
35
|
+
assert_equal @subject.fingerprint, @subject.fingerprint
|
36
|
+
end
|
37
|
+
|
38
|
+
should "return the data contained in the StringIO" do
|
39
|
+
assert_equal "xxx", @subject.read
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'accept a content_type' do
|
43
|
+
@subject.content_type = 'image/png'
|
44
|
+
assert_equal 'image/png', @subject.content_type
|
45
|
+
end
|
46
|
+
|
47
|
+
should 'accept an orgiginal_filename' do
|
48
|
+
@subject.original_filename = 'image.png'
|
49
|
+
assert_equal 'image.png', @subject.original_filename
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
context "a directory index url" do
|
55
|
+
setup do
|
56
|
+
Paperclip::UriAdapter.any_instance.stubs(:download_content).returns(StringIO.new("xxx"))
|
57
|
+
@uri = URI.parse("http://thoughtbot.com")
|
58
|
+
@subject = Paperclip.io_adapters.for(@uri)
|
59
|
+
end
|
60
|
+
|
61
|
+
should "return a file name" do
|
62
|
+
assert_equal "index.html", @subject.original_filename
|
63
|
+
end
|
64
|
+
|
65
|
+
should "return a content type" do
|
66
|
+
assert_equal "text/html", @subject.content_type
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "a url with query params" do
|
71
|
+
setup do
|
72
|
+
Paperclip::UriAdapter.any_instance.stubs(:download_content).returns(StringIO.new("xxx"))
|
73
|
+
@uri = URI.parse("https://github.com/thoughtbot/paperclip?file=test")
|
74
|
+
@subject = Paperclip.io_adapters.for(@uri)
|
75
|
+
end
|
76
|
+
|
77
|
+
should "return a file name" do
|
78
|
+
assert_equal "paperclip", @subject.original_filename
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
data/test/storage/fog_test.rb
CHANGED
@@ -66,6 +66,29 @@ class FogTest < Test::Unit::TestCase
|
|
66
66
|
@dummy.avatar.path
|
67
67
|
end
|
68
68
|
end
|
69
|
+
|
70
|
+
context "with no path or url given and using defaults" do
|
71
|
+
setup do
|
72
|
+
rebuild_model :styles => { :medium => "300x300>", :thumb => "100x100>" },
|
73
|
+
:storage => :fog,
|
74
|
+
:fog_directory => "paperclip",
|
75
|
+
:fog_credentials => {
|
76
|
+
:provider => 'AWS',
|
77
|
+
:aws_access_key_id => 'AWS_ID',
|
78
|
+
:aws_secret_access_key => 'AWS_SECRET'
|
79
|
+
}
|
80
|
+
@file = File.new(fixture_file('5k.png'), 'rb')
|
81
|
+
@dummy = Dummy.new
|
82
|
+
@dummy.id = 1
|
83
|
+
@dummy.avatar = @file
|
84
|
+
end
|
85
|
+
|
86
|
+
teardown { @file.close }
|
87
|
+
|
88
|
+
should "have correct path and url from interpolated defaults" do
|
89
|
+
assert_equal "dummies/avatars/000/000/001/original/5k.png", @dummy.avatar.path
|
90
|
+
end
|
91
|
+
end
|
69
92
|
|
70
93
|
setup do
|
71
94
|
@fog_directory = 'papercliptests'
|
@@ -211,19 +234,11 @@ class FogTest < Test::Unit::TestCase
|
|
211
234
|
|
212
235
|
context "with a valid bucket name for a subdomain" do
|
213
236
|
should "provide an url in subdomain style" do
|
214
|
-
|
215
|
-
# https://github.com/fog/fog/pull/857
|
216
|
-
# assert_match /^http:\/\/papercliptests.s3.amazonaws.com\/avatars\/5k.png\?AWSAccessKeyId=.+$/, @dummy.avatar.expiring_url
|
217
|
-
# For now, use this passing one:
|
218
|
-
assert_match /^https:\/\/papercliptests.\/avatars\/5k.png\?\d*$/, @dummy.avatar.url
|
237
|
+
assert_match @dummy.avatar.url, /^https:\/\/papercliptests.s3.amazonaws.com\/avatars\/5k.png/
|
219
238
|
end
|
220
239
|
|
221
240
|
should "provide an url that expires in subdomain style" do
|
222
|
-
|
223
|
-
# https://github.com/fog/fog/pull/857
|
224
|
-
# assert_match /^http:\/\/papercliptests.s3.amazonaws.com\/avatars\/5k.png\?AWSAccessKeyId=.+$/, @dummy.avatar.expiring_url
|
225
|
-
# For now, use this passing one:
|
226
|
-
assert_match /^http:\/\/papercliptests.\/avatars\/5k.png\?AWSAccessKeyId=.+$/, @dummy.avatar.expiring_url
|
241
|
+
assert_match /^http:\/\/papercliptests.s3.amazonaws.com\/avatars\/5k.png\?AWSAccessKeyId=.+$/, @dummy.avatar.expiring_url
|
227
242
|
end
|
228
243
|
end
|
229
244
|
|
data/test/storage/s3_test.rb
CHANGED
@@ -125,7 +125,22 @@ class S3Test < Test::Unit::TestCase
|
|
125
125
|
should "use the correct key" do
|
126
126
|
assert_equal "avatars/stringio.txt", @dummy.avatar.s3_object.key
|
127
127
|
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "s3_protocol" do
|
131
|
+
["http", :http, ""].each do |protocol|
|
132
|
+
context "as #{protocol.inspect}" do
|
133
|
+
setup do
|
134
|
+
rebuild_model :storage => :s3, :s3_protocol => protocol
|
128
135
|
|
136
|
+
@dummy = Dummy.new
|
137
|
+
end
|
138
|
+
|
139
|
+
should "return the s3_protocol in string" do
|
140
|
+
assert_equal protocol.to_s, @dummy.avatar.s3_protocol
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
129
144
|
end
|
130
145
|
|
131
146
|
context ":s3_protocol => 'https'" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paperclip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-07-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -288,17 +288,17 @@ dependencies:
|
|
288
288
|
requirement: !ruby/object:Gem::Requirement
|
289
289
|
none: false
|
290
290
|
requirements:
|
291
|
-
- -
|
291
|
+
- - ~>
|
292
292
|
- !ruby/object:Gem::Version
|
293
|
-
version:
|
293
|
+
version: 1.4.0
|
294
294
|
type: :development
|
295
295
|
prerelease: false
|
296
296
|
version_requirements: !ruby/object:Gem::Requirement
|
297
297
|
none: false
|
298
298
|
requirements:
|
299
|
-
- -
|
299
|
+
- - ~>
|
300
300
|
- !ruby/object:Gem::Version
|
301
|
-
version:
|
301
|
+
version: 1.4.0
|
302
302
|
- !ruby/object:Gem::Dependency
|
303
303
|
name: pry
|
304
304
|
requirement: !ruby/object:Gem::Requirement
|
@@ -363,6 +363,38 @@ dependencies:
|
|
363
363
|
- - ! '>='
|
364
364
|
- !ruby/object:Gem::Version
|
365
365
|
version: '0'
|
366
|
+
- !ruby/object:Gem::Dependency
|
367
|
+
name: railties
|
368
|
+
requirement: !ruby/object:Gem::Requirement
|
369
|
+
none: false
|
370
|
+
requirements:
|
371
|
+
- - ! '>='
|
372
|
+
- !ruby/object:Gem::Version
|
373
|
+
version: '0'
|
374
|
+
type: :development
|
375
|
+
prerelease: false
|
376
|
+
version_requirements: !ruby/object:Gem::Requirement
|
377
|
+
none: false
|
378
|
+
requirements:
|
379
|
+
- - ! '>='
|
380
|
+
- !ruby/object:Gem::Version
|
381
|
+
version: '0'
|
382
|
+
- !ruby/object:Gem::Dependency
|
383
|
+
name: actionmailer
|
384
|
+
requirement: !ruby/object:Gem::Requirement
|
385
|
+
none: false
|
386
|
+
requirements:
|
387
|
+
- - ! '>='
|
388
|
+
- !ruby/object:Gem::Version
|
389
|
+
version: '0'
|
390
|
+
type: :development
|
391
|
+
prerelease: false
|
392
|
+
version_requirements: !ruby/object:Gem::Requirement
|
393
|
+
none: false
|
394
|
+
requirements:
|
395
|
+
- - ! '>='
|
396
|
+
- !ruby/object:Gem::Version
|
397
|
+
version: '0'
|
366
398
|
description: Easy upload management for ActiveRecord
|
367
399
|
email:
|
368
400
|
- jyurek@thoughtbot.com
|
@@ -410,6 +442,7 @@ files:
|
|
410
442
|
- lib/paperclip/attachment.rb
|
411
443
|
- lib/paperclip/attachment_options.rb
|
412
444
|
- lib/paperclip/callbacks.rb
|
445
|
+
- lib/paperclip/content_type_detector.rb
|
413
446
|
- lib/paperclip/errors.rb
|
414
447
|
- lib/paperclip/geometry.rb
|
415
448
|
- lib/paperclip/glue.rb
|
@@ -424,6 +457,7 @@ files:
|
|
424
457
|
- lib/paperclip/io_adapters/registry.rb
|
425
458
|
- lib/paperclip/io_adapters/stringio_adapter.rb
|
426
459
|
- lib/paperclip/io_adapters/uploaded_file_adapter.rb
|
460
|
+
- lib/paperclip/io_adapters/uri_adapter.rb
|
427
461
|
- lib/paperclip/locales/en.yml
|
428
462
|
- lib/paperclip/logger.rb
|
429
463
|
- lib/paperclip/matchers.rb
|
@@ -454,6 +488,7 @@ files:
|
|
454
488
|
- shoulda_macros/paperclip.rb
|
455
489
|
- test/attachment_options_test.rb
|
456
490
|
- test/attachment_test.rb
|
491
|
+
- test/content_type_detector_test.rb
|
457
492
|
- test/database.yml
|
458
493
|
- test/fixtures/12k.png
|
459
494
|
- test/fixtures/50x50.png
|
@@ -481,6 +516,7 @@ files:
|
|
481
516
|
- test/io_adapters/registry_test.rb
|
482
517
|
- test/io_adapters/stringio_adapter_test.rb
|
483
518
|
- test/io_adapters/uploaded_file_adapter_test.rb
|
519
|
+
- test/io_adapters/uri_adapter_test.rb
|
484
520
|
- test/matchers/have_attached_file_matcher_test.rb
|
485
521
|
- test/matchers/validate_attachment_content_type_matcher_test.rb
|
486
522
|
- test/matchers/validate_attachment_presence_matcher_test.rb
|
@@ -557,6 +593,7 @@ test_files:
|
|
557
593
|
- features/support/selectors.rb
|
558
594
|
- test/attachment_options_test.rb
|
559
595
|
- test/attachment_test.rb
|
596
|
+
- test/content_type_detector_test.rb
|
560
597
|
- test/database.yml
|
561
598
|
- test/fixtures/12k.png
|
562
599
|
- test/fixtures/50x50.png
|
@@ -584,6 +621,7 @@ test_files:
|
|
584
621
|
- test/io_adapters/registry_test.rb
|
585
622
|
- test/io_adapters/stringio_adapter_test.rb
|
586
623
|
- test/io_adapters/uploaded_file_adapter_test.rb
|
624
|
+
- test/io_adapters/uri_adapter_test.rb
|
587
625
|
- test/matchers/have_attached_file_matcher_test.rb
|
588
626
|
- test/matchers/validate_attachment_content_type_matcher_test.rb
|
589
627
|
- test/matchers/validate_attachment_presence_matcher_test.rb
|