paperclip 3.0.4 → 3.1.0

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.

Files changed (56) hide show
  1. data/Appraisals +3 -3
  2. data/NEWS +43 -0
  3. data/README.md +81 -5
  4. data/features/basic_integration.feature +20 -2
  5. data/features/migration.feature +94 -0
  6. data/features/step_definitions/attachment_steps.rb +28 -0
  7. data/features/step_definitions/rails_steps.rb +19 -1
  8. data/features/step_definitions/web_steps.rb +3 -3
  9. data/gemfiles/3.0.gemfile +1 -1
  10. data/gemfiles/3.1.gemfile +1 -1
  11. data/gemfiles/3.2.gemfile +1 -1
  12. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +4 -8
  13. data/lib/paperclip.rb +2 -0
  14. data/lib/paperclip/attachment.rb +4 -0
  15. data/lib/paperclip/geometry.rb +33 -0
  16. data/lib/paperclip/glue.rb +2 -1
  17. data/lib/paperclip/io_adapters/abstract_adapter.rb +45 -0
  18. data/lib/paperclip/io_adapters/attachment_adapter.rb +13 -48
  19. data/lib/paperclip/io_adapters/file_adapter.rb +11 -61
  20. data/lib/paperclip/io_adapters/identity_adapter.rb +1 -1
  21. data/lib/paperclip/io_adapters/nil_adapter.rb +1 -1
  22. data/lib/paperclip/io_adapters/stringio_adapter.rb +11 -42
  23. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +6 -45
  24. data/lib/paperclip/matchers.rb +2 -2
  25. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +36 -17
  26. data/lib/paperclip/railtie.rb +5 -1
  27. data/lib/paperclip/schema.rb +59 -23
  28. data/lib/paperclip/storage/filesystem.rb +5 -0
  29. data/lib/paperclip/storage/fog.rb +36 -14
  30. data/lib/paperclip/storage/s3.rb +14 -16
  31. data/lib/paperclip/style.rb +2 -2
  32. data/lib/paperclip/tempfile_factory.rb +21 -0
  33. data/lib/paperclip/thumbnail.rb +10 -1
  34. data/lib/paperclip/version.rb +1 -1
  35. data/paperclip.gemspec +1 -1
  36. data/test/attachment_test.rb +56 -24
  37. data/test/fixtures/animated +0 -0
  38. data/test/fixtures/animated.unknown +0 -0
  39. data/test/generator_test.rb +26 -24
  40. data/test/geometry_test.rb +19 -0
  41. data/test/helper.rb +8 -0
  42. data/test/integration_test.rb +23 -23
  43. data/test/io_adapters/abstract_adapter_test.rb +44 -0
  44. data/test/io_adapters/attachment_adapter_test.rb +96 -34
  45. data/test/io_adapters/file_adapter_test.rb +13 -1
  46. data/test/io_adapters/stringio_adapter_test.rb +9 -10
  47. data/test/io_adapters/uploaded_file_adapter_test.rb +2 -1
  48. data/test/schema_test.rb +179 -77
  49. data/test/storage/filesystem_test.rb +18 -3
  50. data/test/storage/fog_test.rb +64 -1
  51. data/test/storage/s3_test.rb +38 -2
  52. data/test/tempfile_factory_test.rb +13 -0
  53. data/test/thumbnail_test.rb +45 -0
  54. metadata +16 -9
  55. data/features/support/fixtures/.boot_config.rb.swo +0 -0
  56. data/images.rake +0 -21
@@ -1,51 +1,113 @@
1
1
  require './test/helper'
2
2
 
3
3
  class AttachmentAdapterTest < Test::Unit::TestCase
4
+
4
5
  def setup
5
- rebuild_model :path => "tmp/:class/:attachment/:style/:filename"
6
+ rebuild_model :path => "tmp/:class/:attachment/:style/:filename", :styles => {:thumb => '50x50'}
6
7
  @attachment = Dummy.new.avatar
7
- @file = File.new(fixture_file("5k.png"))
8
- @file.binmode
9
-
10
- @attachment.assign(@file)
11
- @attachment.save
12
- @subject = Paperclip.io_adapters.for(@attachment)
13
8
  end
9
+
10
+ context "for an attachment" do
11
+ setup do
12
+ @file = File.new(fixture_file("5k.png"))
13
+ @file.binmode
14
14
 
15
- def teardown
16
- @file.close
17
- end
15
+ @attachment.assign(@file)
16
+ @attachment.save
17
+ @subject = Paperclip.io_adapters.for(@attachment)
18
+ end
18
19
 
19
- should "get the right filename" do
20
- assert_equal "5k.png", @subject.original_filename
21
- end
20
+ teardown do
21
+ @file.close
22
+ end
22
23
 
23
- should "force binmode on tempfile" do
24
- assert @subject.instance_variable_get("@tempfile").binmode?
25
- end
24
+ should "get the right filename" do
25
+ assert_equal "5k.png", @subject.original_filename
26
+ end
26
27
 
27
- should "get the content type" do
28
- assert_equal "image/png", @subject.content_type
29
- end
28
+ should "force binmode on tempfile" do
29
+ assert @subject.instance_variable_get("@tempfile").binmode?
30
+ end
30
31
 
31
- should "get the file's size" do
32
- assert_equal 4456, @subject.size
33
- end
32
+ should "get the content type" do
33
+ assert_equal "image/png", @subject.content_type
34
+ end
34
35
 
35
- should "return false for a call to nil?" do
36
- assert ! @subject.nil?
37
- end
36
+ should "get the file's size" do
37
+ assert_equal 4456, @subject.size
38
+ end
39
+
40
+ should "return false for a call to nil?" do
41
+ assert ! @subject.nil?
42
+ end
43
+
44
+ should "generate a MD5 hash of the contents" do
45
+ expected = Digest::MD5.file(@file.path).to_s
46
+ assert_equal expected, @subject.fingerprint
47
+ end
38
48
 
39
- should "generate a MD5 hash of the contents" do
40
- expected = Digest::MD5.file(@file.path).to_s
41
- assert_equal expected, @subject.fingerprint
49
+ should "read the contents of the file" do
50
+ expected = @file.read
51
+ actual = @subject.read
52
+ assert expected.length > 0
53
+ assert_equal expected.length, actual.length
54
+ assert_equal expected, actual
55
+ end
56
+
42
57
  end
58
+
59
+ context "for a style" do
60
+ setup do
61
+ @file = File.new(fixture_file("5k.png"))
62
+ @file.binmode
63
+
64
+ @attachment.assign(@file)
65
+
66
+ @thumb = Tempfile.new("thumbnail").tap(&:binmode)
67
+ FileUtils.cp @attachment.queued_for_write[:thumb].path, @thumb.path
68
+
69
+ @attachment.save
70
+ @subject = Paperclip.io_adapters.for(@attachment.styles[:thumb])
71
+ end
72
+
73
+ teardown do
74
+ @file.close
75
+ @thumb.close
76
+ end
77
+
78
+ should "get the original filename" do
79
+ assert_equal "5k.png", @subject.original_filename
80
+ end
81
+
82
+ should "force binmode on tempfile" do
83
+ assert @subject.instance_variable_get("@tempfile").binmode?
84
+ end
85
+
86
+ should "get the content type" do
87
+ assert_equal "image/png", @subject.content_type
88
+ end
89
+
90
+ should "get the thumbnail's file size" do
91
+ assert_equal @thumb.size, @subject.size
92
+ end
93
+
94
+ should "return false for a call to nil?" do
95
+ assert ! @subject.nil?
96
+ end
97
+
98
+ should "generate a MD5 hash of the contents" do
99
+ expected = Digest::MD5.file(@thumb.path).to_s
100
+ assert_equal expected, @subject.fingerprint
101
+ end
43
102
 
44
- should "read the contents of the file" do
45
- expected = @file.read
46
- actual = @subject.read
47
- assert expected.length > 0
48
- assert_equal expected.length, actual.length
49
- assert_equal expected, actual
103
+ should "read the contents of the thumbnail" do
104
+ @thumb.rewind
105
+ expected = @thumb.read
106
+ actual = @subject.read
107
+ assert expected.length > 0
108
+ assert_equal expected.length, actual.length
109
+ assert_equal expected, actual
110
+ end
111
+
50
112
  end
51
113
  end
@@ -60,10 +60,22 @@ class FileAdapterTest < Test::Unit::TestCase
60
60
  end
61
61
  end
62
62
 
63
+ context "file with multiple possible x-types but no official type" do
64
+ setup do
65
+ MIME::Types.stubs(:type_for).returns([MIME::Type.new('image/x-mp4'), MIME::Type.new('image/x-video')])
66
+ @subject = Paperclip.io_adapters.for(@file)
67
+ end
68
+
69
+ should "return the first" do
70
+ assert_equal "image/x-mp4", @subject.content_type
71
+ end
72
+ end
73
+
63
74
  context "file with content type derived from file command on *nix" do
64
75
  setup do
65
76
  MIME::Types.stubs(:type_for).returns([])
66
77
  Paperclip.stubs(:run).returns("application/vnd.ms-office\n")
78
+ @subject = Paperclip.io_adapters.for(@file)
67
79
  end
68
80
 
69
81
  should "return content type without newline character" do
@@ -81,7 +93,7 @@ class FileAdapterTest < Test::Unit::TestCase
81
93
  teardown { @file.close }
82
94
 
83
95
  should "provide correct mime-type" do
84
- assert_equal "application/x-empty", @subject.content_type
96
+ assert_match %r{.*/x-empty}, @subject.content_type
85
97
  end
86
98
  end
87
99
  end
@@ -12,20 +12,10 @@ class StringioFileProxyTest < Test::Unit::TestCase
12
12
  assert_equal "stringio.txt", @subject.original_filename
13
13
  end
14
14
 
15
- should "allow us to set a name" do
16
- @subject.original_filename = "data.txt"
17
- assert_equal "data.txt", @subject.original_filename
18
- end
19
-
20
15
  should "return a content type" do
21
16
  assert_equal "text/plain", @subject.content_type
22
17
  end
23
18
 
24
- should "allow us to set a content type" do
25
- @subject.content_type = "image/jpg"
26
- assert_equal "image/jpg", @subject.content_type
27
- end
28
-
29
19
  should "return the size of the data" do
30
20
  assert_equal 6, @subject.size
31
21
  end
@@ -34,6 +24,15 @@ class StringioFileProxyTest < Test::Unit::TestCase
34
24
  assert_equal Digest::MD5.hexdigest(@contents), @subject.fingerprint
35
25
  end
36
26
 
27
+ should "generate correct fingerprint after read" do
28
+ fingerprint = Digest::MD5.hexdigest(@subject.read)
29
+ assert_equal fingerprint, @subject.fingerprint
30
+ end
31
+
32
+ should "generate same fingerprint" do
33
+ assert_equal @subject.fingerprint, @subject.fingerprint
34
+ end
35
+
37
36
  should "return the data contained in the StringIO" do
38
37
  assert_equal "abc123", @subject.read
39
38
  end
@@ -12,7 +12,8 @@ class UploadedFileAdapterTest < Test::Unit::TestCase
12
12
  :original_filename => "5k.png",
13
13
  :content_type => "image/png",
14
14
  :head => "",
15
- :tempfile => tempfile
15
+ :tempfile => tempfile,
16
+ :path => tempfile.path
16
17
  )
17
18
  @subject = Paperclip.io_adapters.for(@file)
18
19
  end
@@ -1,98 +1,200 @@
1
1
  require './test/helper'
2
2
  require 'paperclip/schema'
3
+ require 'active_support/testing/deprecation'
3
4
 
4
- class MockSchema
5
- include Paperclip::Schema
6
-
7
- def initialize(table_name = nil)
8
- @table_name = table_name
9
- @columns = {}
10
- @deleted_columns = []
11
- end
12
-
13
- def column(name, type)
14
- @columns[name] = type
15
- end
16
-
17
- def remove_column(table_name, column_name)
18
- return if @table_name && @table_name != table_name
19
- @columns.delete(column_name)
20
- @deleted_columns.push(column_name)
21
- end
22
-
23
- def has_column?(column_name)
24
- @columns.key?(column_name)
25
- end
5
+ class SchemaTest < Test::Unit::TestCase
6
+ include ActiveSupport::Testing::Deprecation
26
7
 
27
- def deleted_column?(column_name)
28
- @deleted_columns.include?(column_name)
8
+ def setup
9
+ rebuild_class
29
10
  end
30
11
 
31
- def type_of(column_name)
32
- @columns[column_name]
12
+ def teardown
13
+ Dummy.connection.drop_table :dummies rescue nil
33
14
  end
34
- end
35
-
36
- class SchemaTest < Test::Unit::TestCase
37
- context "Migrating up" do
38
- setup do
39
- @schema = MockSchema.new
40
- @schema.has_attached_file :avatar
41
- end
42
-
43
- should "create the file_name column" do
44
- assert @schema.has_column?(:avatar_file_name)
45
- end
46
-
47
- should "create the content_type column" do
48
- assert @schema.has_column?(:avatar_content_type)
49
- end
50
-
51
- should "create the file_size column" do
52
- assert @schema.has_column?(:avatar_file_size)
53
- end
54
-
55
- should "create the updated_at column" do
56
- assert @schema.has_column?(:avatar_updated_at)
57
- end
58
15
 
59
- should "make the file_name column a string" do
60
- assert_equal :string, @schema.type_of(:avatar_file_name)
16
+ context "within table definition" do
17
+ context "using #has_attached_file" do
18
+ should "create attachment columns" do
19
+ Dummy.connection.create_table :dummies, :force => true do |t|
20
+ ActiveSupport::Deprecation.silence do
21
+ t.has_attached_file :avatar
22
+ end
23
+ end
24
+ rebuild_class
25
+
26
+ columns = Dummy.columns.map{ |column| [column.name, column.type] }
27
+
28
+ assert_includes columns, ['avatar_file_name', :string]
29
+ assert_includes columns, ['avatar_content_type', :string]
30
+ assert_includes columns, ['avatar_file_size', :integer]
31
+ assert_includes columns, ['avatar_updated_at', :datetime]
32
+ end
33
+
34
+ should "display deprecation warning" do
35
+ Dummy.connection.create_table :dummies, :force => true do |t|
36
+ assert_deprecated do
37
+ t.has_attached_file :avatar
38
+ end
39
+ end
40
+ end
61
41
  end
62
42
 
63
- should "make the content_type column a string" do
64
- assert_equal :string, @schema.type_of(:avatar_content_type)
65
- end
66
-
67
- should "make the file_size column an integer" do
68
- assert_equal :integer, @schema.type_of(:avatar_file_size)
69
- end
70
-
71
- should "make the updated_at column a datetime" do
72
- assert_equal :datetime, @schema.type_of(:avatar_updated_at)
43
+ context "using #attachment" do
44
+ setup do
45
+ Dummy.connection.create_table :dummies, :force => true do |t|
46
+ t.attachment :avatar
47
+ end
48
+ rebuild_class
49
+ end
50
+
51
+ should "create attachment columns" do
52
+ columns = Dummy.columns.map{ |column| [column.name, column.type] }
53
+
54
+ assert_includes columns, ['avatar_file_name', :string]
55
+ assert_includes columns, ['avatar_content_type', :string]
56
+ assert_includes columns, ['avatar_file_size', :integer]
57
+ assert_includes columns, ['avatar_updated_at', :datetime]
58
+ end
73
59
  end
74
60
  end
75
61
 
76
- context "Migrating down" do
62
+ context "within schema statement" do
77
63
  setup do
78
- @schema = MockSchema.new(:users)
79
- @schema.drop_attached_file :users, :avatar
80
- end
81
-
82
- should "remove the file_name column" do
83
- assert @schema.deleted_column?(:avatar_file_name)
84
- end
85
-
86
- should "remove the content_type column" do
87
- assert @schema.deleted_column?(:avatar_content_type)
64
+ Dummy.connection.create_table :dummies, :force => true
88
65
  end
89
66
 
90
- should "remove the file_size column" do
91
- assert @schema.deleted_column?(:avatar_file_size)
67
+ context "migrating up" do
68
+ context "with single attachment" do
69
+ setup do
70
+ Dummy.connection.add_attachment :dummies, :avatar
71
+ rebuild_class
72
+ end
73
+
74
+ should "create attachment columns" do
75
+ columns = Dummy.columns.map{ |column| [column.name, column.type] }
76
+
77
+ assert_includes columns, ['avatar_file_name', :string]
78
+ assert_includes columns, ['avatar_content_type', :string]
79
+ assert_includes columns, ['avatar_file_size', :integer]
80
+ assert_includes columns, ['avatar_updated_at', :datetime]
81
+ end
82
+ end
83
+
84
+ context "with multiple attachments" do
85
+ setup do
86
+ Dummy.connection.add_attachment :dummies, :avatar, :photo
87
+ rebuild_class
88
+ end
89
+
90
+ should "create attachment columns" do
91
+ columns = Dummy.columns.map{ |column| [column.name, column.type] }
92
+
93
+ assert_includes columns, ['avatar_file_name', :string]
94
+ assert_includes columns, ['avatar_content_type', :string]
95
+ assert_includes columns, ['avatar_file_size', :integer]
96
+ assert_includes columns, ['avatar_updated_at', :datetime]
97
+ assert_includes columns, ['photo_file_name', :string]
98
+ assert_includes columns, ['photo_content_type', :string]
99
+ assert_includes columns, ['photo_file_size', :integer]
100
+ assert_includes columns, ['photo_updated_at', :datetime]
101
+ end
102
+ end
103
+
104
+ context "with no attachment" do
105
+ should "raise an error" do
106
+ assert_raise ArgumentError do
107
+ Dummy.connection.add_attachment :dummies
108
+ rebuild_class
109
+ end
110
+ end
111
+ end
92
112
  end
93
113
 
94
- should "remove the updated_at column" do
95
- assert @schema.deleted_column?(:avatar_updated_at)
114
+ context "migrating down" do
115
+ setup do
116
+ Dummy.connection.change_table :dummies do |t|
117
+ t.column :avatar_file_name, :string
118
+ t.column :avatar_content_type, :string
119
+ t.column :avatar_file_size, :integer
120
+ t.column :avatar_updated_at, :datetime
121
+ end
122
+ end
123
+
124
+ context "using #drop_attached_file" do
125
+ should "remove the attachment columns" do
126
+ ActiveSupport::Deprecation.silence do
127
+ Dummy.connection.drop_attached_file :dummies, :avatar
128
+ end
129
+ rebuild_class
130
+
131
+ columns = Dummy.columns.map{ |column| [column.name, column.type] }
132
+
133
+ assert_not_includes columns, ['avatar_file_name', :string]
134
+ assert_not_includes columns, ['avatar_content_type', :string]
135
+ assert_not_includes columns, ['avatar_file_size', :integer]
136
+ assert_not_includes columns, ['avatar_updated_at', :datetime]
137
+ end
138
+
139
+ should "display a deprecation warning" do
140
+ assert_deprecated do
141
+ Dummy.connection.drop_attached_file :dummies, :avatar
142
+ end
143
+ end
144
+ end
145
+
146
+ context "using #remove_attachment" do
147
+ context "with single attachment" do
148
+ setup do
149
+ Dummy.connection.remove_attachment :dummies, :avatar
150
+ rebuild_class
151
+ end
152
+
153
+ should "remove the attachment columns" do
154
+ columns = Dummy.columns.map{ |column| [column.name, column.type] }
155
+
156
+ assert_not_includes columns, ['avatar_file_name', :string]
157
+ assert_not_includes columns, ['avatar_content_type', :string]
158
+ assert_not_includes columns, ['avatar_file_size', :integer]
159
+ assert_not_includes columns, ['avatar_updated_at', :datetime]
160
+ end
161
+ end
162
+
163
+ context "with multiple attachments" do
164
+ setup do
165
+ Dummy.connection.change_table :dummies do |t|
166
+ t.column :photo_file_name, :string
167
+ t.column :photo_content_type, :string
168
+ t.column :photo_file_size, :integer
169
+ t.column :photo_updated_at, :datetime
170
+ end
171
+
172
+ Dummy.connection.remove_attachment :dummies, :avatar, :photo
173
+ rebuild_class
174
+ end
175
+
176
+ should "remove the attachment columns" do
177
+ columns = Dummy.columns.map{ |column| [column.name, column.type] }
178
+
179
+ assert_not_includes columns, ['avatar_file_name', :string]
180
+ assert_not_includes columns, ['avatar_content_type', :string]
181
+ assert_not_includes columns, ['avatar_file_size', :integer]
182
+ assert_not_includes columns, ['avatar_updated_at', :datetime]
183
+ assert_not_includes columns, ['photo_file_name', :string]
184
+ assert_not_includes columns, ['photo_content_type', :string]
185
+ assert_not_includes columns, ['photo_file_size', :integer]
186
+ assert_not_includes columns, ['photo_updated_at', :datetime]
187
+ end
188
+ end
189
+
190
+ context "with no attachment" do
191
+ should "raise an error" do
192
+ assert_raise ArgumentError do
193
+ Dummy.connection.remove_attachment :dummies
194
+ end
195
+ end
196
+ end
197
+ end
96
198
  end
97
199
  end
98
200
  end