paperclip 4.2.2 → 5.0.0

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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +1066 -0
  3. data/.rubocop.yml +1 -0
  4. data/.travis.yml +18 -15
  5. data/Appraisals +20 -12
  6. data/CONTRIBUTING.md +19 -8
  7. data/Gemfile +4 -9
  8. data/LICENSE +1 -1
  9. data/NEWS +101 -31
  10. data/README.md +243 -159
  11. data/RELEASING.md +17 -0
  12. data/Rakefile +1 -1
  13. data/UPGRADING +12 -9
  14. data/features/basic_integration.feature +8 -4
  15. data/features/migration.feature +0 -24
  16. data/features/step_definitions/attachment_steps.rb +27 -21
  17. data/features/step_definitions/html_steps.rb +2 -2
  18. data/features/step_definitions/rails_steps.rb +11 -17
  19. data/features/step_definitions/s3_steps.rb +2 -2
  20. data/features/step_definitions/web_steps.rb +1 -103
  21. data/features/support/file_helpers.rb +2 -2
  22. data/gemfiles/4.2.awsv2.0.gemfile +17 -0
  23. data/gemfiles/4.2.awsv2.1.gemfile +17 -0
  24. data/gemfiles/{4.1.gemfile → 4.2.awsv2.gemfile} +4 -3
  25. data/gemfiles/5.0.awsv2.0.gemfile +17 -0
  26. data/gemfiles/5.0.awsv2.1.gemfile +17 -0
  27. data/gemfiles/{4.2.gemfile → 5.0.awsv2.gemfile} +4 -3
  28. data/lib/paperclip/attachment.rb +19 -16
  29. data/lib/paperclip/attachment_registry.rb +3 -2
  30. data/lib/paperclip/callbacks.rb +8 -6
  31. data/lib/paperclip/content_type_detector.rb +27 -11
  32. data/lib/paperclip/errors.rb +3 -1
  33. data/lib/paperclip/file_command_content_type_detector.rb +6 -8
  34. data/lib/paperclip/geometry_parser_factory.rb +1 -1
  35. data/lib/paperclip/glue.rb +1 -1
  36. data/lib/paperclip/has_attached_file.rb +9 -2
  37. data/lib/paperclip/helpers.rb +14 -10
  38. data/lib/paperclip/interpolations/plural_cache.rb +6 -5
  39. data/lib/paperclip/interpolations.rb +18 -13
  40. data/lib/paperclip/io_adapters/abstract_adapter.rb +1 -0
  41. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +1 -1
  42. data/lib/paperclip/io_adapters/uri_adapter.rb +3 -1
  43. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
  44. data/lib/paperclip/media_type_spoof_detector.rb +2 -2
  45. data/lib/paperclip/rails_environment.rb +25 -0
  46. data/lib/paperclip/schema.rb +3 -9
  47. data/lib/paperclip/storage/fog.rb +21 -12
  48. data/lib/paperclip/storage/s3.rb +51 -50
  49. data/lib/paperclip/thumbnail.rb +2 -3
  50. data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
  51. data/lib/paperclip/version.rb +3 -1
  52. data/lib/paperclip.rb +15 -4
  53. data/lib/tasks/paperclip.rake +17 -1
  54. data/paperclip.gemspec +18 -15
  55. data/spec/paperclip/attachment_definitions_spec.rb +1 -1
  56. data/spec/paperclip/attachment_processing_spec.rb +2 -4
  57. data/spec/paperclip/attachment_registry_spec.rb +84 -13
  58. data/spec/paperclip/attachment_spec.rb +91 -31
  59. data/spec/paperclip/content_type_detector_spec.rb +8 -1
  60. data/spec/paperclip/file_command_content_type_detector_spec.rb +0 -1
  61. data/spec/paperclip/geometry_spec.rb +1 -1
  62. data/spec/paperclip/glue_spec.rb +44 -0
  63. data/spec/paperclip/has_attached_file_spec.rb +24 -8
  64. data/spec/paperclip/integration_spec.rb +4 -3
  65. data/spec/paperclip/interpolations_spec.rb +16 -13
  66. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +2 -1
  67. data/spec/paperclip/io_adapters/file_adapter_spec.rb +4 -1
  68. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +12 -0
  69. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +4 -0
  70. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +27 -0
  71. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +10 -0
  72. data/spec/paperclip/media_type_spoof_detector_spec.rb +34 -11
  73. data/spec/paperclip/paperclip_spec.rb +4 -29
  74. data/spec/paperclip/plural_cache_spec.rb +17 -16
  75. data/spec/paperclip/rails_environment_spec.rb +33 -0
  76. data/spec/paperclip/storage/fog_spec.rb +42 -3
  77. data/spec/paperclip/storage/s3_live_spec.rb +8 -4
  78. data/spec/paperclip/storage/s3_spec.rb +255 -180
  79. data/spec/paperclip/tempfile_factory_spec.rb +4 -0
  80. data/spec/paperclip/thumbnail_spec.rb +16 -0
  81. data/spec/paperclip/url_generator_spec.rb +1 -1
  82. data/spec/paperclip/validators/attachment_size_validator_spec.rb +26 -20
  83. data/spec/paperclip/validators_spec.rb +3 -3
  84. data/spec/spec_helper.rb +6 -1
  85. data/spec/support/assertions.rb +7 -0
  86. data/spec/support/fake_model.rb +4 -0
  87. data/spec/support/fixtures/empty.xlsx +0 -0
  88. data/spec/support/matchers/have_column.rb +11 -2
  89. data/spec/support/model_reconstruction.rb +9 -1
  90. data/spec/support/reporting.rb +11 -0
  91. metadata +105 -54
  92. data/RUNNING_TESTS.md +0 -4
  93. data/cucumber/paperclip_steps.rb +0 -6
  94. data/gemfiles/3.2.gemfile +0 -19
  95. data/gemfiles/4.0.gemfile +0 -19
  96. data/lib/paperclip/locales/de.yml +0 -18
  97. data/lib/paperclip/locales/es.yml +0 -18
  98. data/lib/paperclip/locales/ja.yml +0 -18
  99. data/lib/paperclip/locales/pt-BR.yml +0 -18
  100. data/lib/paperclip/locales/zh-CN.yml +0 -18
  101. data/lib/paperclip/locales/zh-HK.yml +0 -18
  102. data/lib/paperclip/locales/zh-TW.yml +0 -18
  103. data/spec/support/mock_model.rb +0 -2
  104. data/spec/support/rails_helpers.rb +0 -7
@@ -31,8 +31,8 @@ describe 'Attachment Registry' do
31
31
  it 'calls the block with the class, attachment name, and options' do
32
32
  foo = Class.new
33
33
  expected_accumulations = [
34
- [foo, :avatar, { yo: 'greeting' }],
35
- [foo, :greeter, { ciao: 'greeting' }]
34
+ [foo, :avatar, { yo: "greeting" }],
35
+ [foo, :greeter, { ciao: "greeting" }]
36
36
  ]
37
37
  expected_accumulations.each do |args|
38
38
  Paperclip::AttachmentRegistry.register(*args)
@@ -50,25 +50,92 @@ describe 'Attachment Registry' do
50
50
  context '.definitions_for' do
51
51
  it 'produces the attachment name and options' do
52
52
  expected_definitions = {
53
- avatar: { yo: 'greeting' },
54
- greeter: { ciao: 'greeting' }
53
+ avatar: { yo: "greeting" },
54
+ greeter: { ciao: "greeting" }
55
55
  }
56
56
  foo = Class.new
57
- Paperclip::AttachmentRegistry.register(foo, :avatar, { yo: 'greeting' })
58
- Paperclip::AttachmentRegistry.register(foo, :greeter, { ciao: 'greeting' })
57
+ Paperclip::AttachmentRegistry.register(
58
+ foo,
59
+ :avatar,
60
+ yo: "greeting"
61
+ )
62
+ Paperclip::AttachmentRegistry.register(
63
+ foo,
64
+ :greeter,
65
+ ciao: "greeting"
66
+ )
59
67
 
60
68
  definitions = Paperclip::AttachmentRegistry.definitions_for(foo)
61
69
 
62
70
  assert_equal expected_definitions, definitions
63
71
  end
64
72
 
65
- it "produces defintions for subclasses" do
66
- expected_definitions = { avatar: { yo: 'greeting' } }
67
- Foo = Class.new
68
- Bar = Class.new(Foo)
69
- Paperclip::AttachmentRegistry.register(Foo, :avatar, expected_definitions[:avatar])
73
+ it 'produces defintions for subclasses' do
74
+ expected_definitions = { avatar: { yo: "greeting" } }
75
+ foo = Class.new
76
+ bar = Class.new(foo)
77
+ Paperclip::AttachmentRegistry.register(
78
+ foo,
79
+ :avatar,
80
+ expected_definitions[:avatar]
81
+ )
82
+
83
+ definitions = Paperclip::AttachmentRegistry.definitions_for(bar)
84
+
85
+ assert_equal expected_definitions, definitions
86
+ end
87
+
88
+ it 'produces defintions for subclasses but deep merging them' do
89
+ foo_definitions = { avatar: { yo: "greeting" } }
90
+ bar_definitions = { avatar: { ciao: "greeting" } }
91
+ expected_definitions = {
92
+ avatar: {
93
+ yo: "greeting",
94
+ ciao: "greeting"
95
+ }
96
+ }
97
+ foo = Class.new
98
+ bar = Class.new(foo)
99
+ Paperclip::AttachmentRegistry.register(
100
+ foo,
101
+ :avatar,
102
+ foo_definitions[:avatar]
103
+ )
104
+ Paperclip::AttachmentRegistry.register(
105
+ bar,
106
+ :avatar,
107
+ bar_definitions[:avatar]
108
+ )
109
+
110
+ definitions = Paperclip::AttachmentRegistry.definitions_for(bar)
70
111
 
71
- definitions = Paperclip::AttachmentRegistry.definitions_for(Bar)
112
+ assert_equal expected_definitions, definitions
113
+ end
114
+
115
+ it 'allows subclasses to override attachment defitions' do
116
+ foo_definitions = { avatar: { yo: "greeting" } }
117
+ bar_definitions = { avatar: { yo: "hello" } }
118
+
119
+ expected_definitions = {
120
+ avatar: {
121
+ yo: "hello"
122
+ }
123
+ }
124
+
125
+ foo = Class.new
126
+ bar = Class.new(foo)
127
+ Paperclip::AttachmentRegistry.register(
128
+ foo,
129
+ :avatar,
130
+ foo_definitions[:avatar]
131
+ )
132
+ Paperclip::AttachmentRegistry.register(
133
+ bar,
134
+ :avatar,
135
+ bar_definitions[:avatar]
136
+ )
137
+
138
+ definitions = Paperclip::AttachmentRegistry.definitions_for(bar)
72
139
 
73
140
  assert_equal expected_definitions, definitions
74
141
  end
@@ -77,7 +144,11 @@ describe 'Attachment Registry' do
77
144
  context '.clear' do
78
145
  it 'removes all of the existing attachment definitions' do
79
146
  foo = Class.new
80
- Paperclip::AttachmentRegistry.register(foo, :greeter, { ciao: 'greeting' })
147
+ Paperclip::AttachmentRegistry.register(
148
+ foo,
149
+ :greeter,
150
+ ciao: "greeting"
151
+ )
81
152
 
82
153
  Paperclip::AttachmentRegistry.clear
83
154
 
@@ -13,7 +13,7 @@ describe Paperclip::Attachment do
13
13
  it "is present when the file is set" do
14
14
  rebuild_class
15
15
  dummy = Dummy.new
16
- dummy.avatar = File.new(fixture_file("50x50.png"), "rb")
16
+ dummy.avatar = File.new(fixture_file("50x50.png"), "rb")
17
17
  expect(dummy.avatar).to_not be_blank
18
18
  expect(dummy.avatar).to be_present
19
19
  end
@@ -34,9 +34,9 @@ describe Paperclip::Attachment do
34
34
  it "does not delete styles that don't get reprocessed" do
35
35
  file = File.new(fixture_file("50x50.png"), 'rb')
36
36
  rebuild_class styles: {
37
- small: '100x>',
38
- large: '500x>',
39
- original: '42x42#'
37
+ small: "100x>",
38
+ large: "500x>",
39
+ original: "42x42#"
40
40
  }
41
41
 
42
42
  dummy = Dummy.new
@@ -75,7 +75,11 @@ describe Paperclip::Attachment do
75
75
 
76
76
  it "handles a boolean second argument to #url" do
77
77
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
78
- attachment = Paperclip::Attachment.new(:name, :instance, url_generator: mock_url_generator_builder)
78
+ attachment = Paperclip::Attachment.new(
79
+ :name,
80
+ FakeModel.new,
81
+ url_generator: mock_url_generator_builder
82
+ )
79
83
 
80
84
  attachment.url(:style_name, true)
81
85
  expect(mock_url_generator_builder.has_generated_url_with_options?(timestamp: true, escape: true)).to eq true
@@ -86,7 +90,11 @@ describe Paperclip::Attachment do
86
90
 
87
91
  it "passes the style and options through to the URL generator on #url" do
88
92
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
89
- attachment = Paperclip::Attachment.new(:name, :instance, url_generator: mock_url_generator_builder)
93
+ attachment = Paperclip::Attachment.new(
94
+ :name,
95
+ FakeModel.new,
96
+ url_generator: mock_url_generator_builder
97
+ )
90
98
 
91
99
  attachment.url(:style_name, options: :values)
92
100
  expect(mock_url_generator_builder.has_generated_url_with_options?(options: :values)).to eq true
@@ -95,7 +103,7 @@ describe Paperclip::Attachment do
95
103
  it "passes default options through when #url is given one argument" do
96
104
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
97
105
  attachment = Paperclip::Attachment.new(:name,
98
- :instance,
106
+ FakeModel.new,
99
107
  url_generator: mock_url_generator_builder,
100
108
  use_timestamp: true)
101
109
 
@@ -106,7 +114,7 @@ describe Paperclip::Attachment do
106
114
  it "passes default style and options through when #url is given no arguments" do
107
115
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
108
116
  attachment = Paperclip::Attachment.new(:name,
109
- :instance,
117
+ FakeModel.new,
110
118
  default_style: 'default style',
111
119
  url_generator: mock_url_generator_builder,
112
120
  use_timestamp: true)
@@ -119,7 +127,7 @@ describe Paperclip::Attachment do
119
127
  it "passes the option timestamp: true if :use_timestamp is true and :timestamp is not passed" do
120
128
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
121
129
  attachment = Paperclip::Attachment.new(:name,
122
- :instance,
130
+ FakeModel.new,
123
131
  url_generator: mock_url_generator_builder,
124
132
  use_timestamp: true)
125
133
 
@@ -130,7 +138,7 @@ describe Paperclip::Attachment do
130
138
  it "passes the option timestamp: false if :use_timestamp is false and :timestamp is not passed" do
131
139
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
132
140
  attachment = Paperclip::Attachment.new(:name,
133
- :instance,
141
+ FakeModel.new,
134
142
  url_generator: mock_url_generator_builder,
135
143
  use_timestamp: false)
136
144
 
@@ -141,7 +149,7 @@ describe Paperclip::Attachment do
141
149
  it "does not change the :timestamp if :timestamp is passed" do
142
150
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
143
151
  attachment = Paperclip::Attachment.new(:name,
144
- :instance,
152
+ FakeModel.new,
145
153
  url_generator: mock_url_generator_builder,
146
154
  use_timestamp: false)
147
155
 
@@ -152,7 +160,7 @@ describe Paperclip::Attachment do
152
160
  it "renders JSON as default style" do
153
161
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
154
162
  attachment = Paperclip::Attachment.new(:name,
155
- :instance,
163
+ FakeModel.new,
156
164
  default_style: 'default style',
157
165
  url_generator: mock_url_generator_builder)
158
166
 
@@ -163,7 +171,7 @@ describe Paperclip::Attachment do
163
171
  it "passes the option escape: true if :escape_url is true and :escape is not passed" do
164
172
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
165
173
  attachment = Paperclip::Attachment.new(:name,
166
- :instance,
174
+ FakeModel.new,
167
175
  url_generator: mock_url_generator_builder,
168
176
  escape_url: true)
169
177
 
@@ -174,7 +182,7 @@ describe Paperclip::Attachment do
174
182
  it "passes the option escape: false if :escape_url is false and :escape is not passed" do
175
183
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
176
184
  attachment = Paperclip::Attachment.new(:name,
177
- :instance,
185
+ FakeModel.new,
178
186
  url_generator: mock_url_generator_builder,
179
187
  escape_url: false)
180
188
 
@@ -212,10 +220,8 @@ describe Paperclip::Attachment do
212
220
  dummy = Dummy.new
213
221
  dummy.id = 1234
214
222
  dummy.avatar_file_name = "fake.jpg"
223
+ dummy.stubs(:new_record?).returns(false)
215
224
  expected_string = '{"avatar":"/system/dummies/avatars/000/001/234/original/fake.jpg"}'
216
- if ActiveRecord::Base.include_root_in_json # This is true by default in Rails 3, and false in 4
217
- expected_string = %({"dummy":#{expected_string}})
218
- end
219
225
  # active_model pre-3.2 checks only by calling any? on it, thus it doesn't work if it is empty
220
226
  assert_equal expected_string, dummy.to_json(only: [:dummy_key_for_old_active_model], methods: [:avatar])
221
227
  end
@@ -245,12 +251,17 @@ describe Paperclip::Attachment do
245
251
 
246
252
  context "without an Attachment" do
247
253
  before do
254
+ rebuild_model default_url: "default.url"
248
255
  @dummy = Dummy.new
249
256
  end
250
257
 
251
258
  it "returns false when asked exists?" do
252
259
  assert !@dummy.avatar.exists?
253
260
  end
261
+
262
+ it "#url returns the default_url" do
263
+ expect(@dummy.avatar.url).to eq "default.url"
264
+ end
254
265
  end
255
266
 
256
267
  context "on an Attachment" do
@@ -635,15 +646,40 @@ describe Paperclip::Attachment do
635
646
  before do
636
647
  rebuild_model processor: [:thumbnail], styles: { small: '' }, whiny_thumbnails: true
637
648
  @dummy = Dummy.new
638
- Paperclip::Thumbnail.expects(:make).raises(Paperclip::Error, "cannot be processed.")
639
649
  @file = StringIO.new("...")
640
650
  @file.stubs(:to_tempfile).returns(@file)
641
- @dummy.avatar = @file
642
651
  end
643
652
 
644
- it "correctly forwards processing error message to the instance" do
645
- @dummy.valid?
646
- assert_contains @dummy.errors.full_messages, "Avatar cannot be processed."
653
+ context "when error is meaningful for the end user" do
654
+ before do
655
+ Paperclip::Thumbnail.expects(:make).raises(
656
+ Paperclip::Errors::NotIdentifiedByImageMagickError,
657
+ "cannot be processed."
658
+ )
659
+ end
660
+
661
+ it "correctly forwards processing error message to the instance" do
662
+ @dummy.avatar = @file
663
+ @dummy.valid?
664
+ assert_contains(
665
+ @dummy.errors.full_messages,
666
+ "Avatar cannot be processed."
667
+ )
668
+ end
669
+ end
670
+
671
+ context "when error is intended for the developer" do
672
+ before do
673
+ Paperclip::Thumbnail.expects(:make).raises(
674
+ Paperclip::Errors::CommandNotFoundError
675
+ )
676
+ end
677
+
678
+ it "propagates the error" do
679
+ assert_raises(Paperclip::Errors::CommandNotFoundError) do
680
+ @dummy.avatar = @file
681
+ end
682
+ end
647
683
  end
648
684
  end
649
685
 
@@ -661,9 +697,6 @@ describe Paperclip::Attachment do
661
697
 
662
698
  context "when assigned" do
663
699
  it "calls #make on all specified processors" do
664
- Paperclip::Thumbnail.stubs(:make).with(any_parameters).returns(@file)
665
- Paperclip::Test.stubs(:make).with(any_parameters).returns(@file)
666
-
667
700
  @dummy.avatar = @file
668
701
 
669
702
  expect(Paperclip::Thumbnail).to have_received(:make)
@@ -678,7 +711,6 @@ describe Paperclip::Attachment do
678
711
  convert_options: "",
679
712
  source_file_options: ""
680
713
  })
681
- Paperclip::Thumbnail.stubs(:make).returns(@file)
682
714
 
683
715
  @dummy.avatar = @file
684
716
 
@@ -686,12 +718,36 @@ describe Paperclip::Attachment do
686
718
  end
687
719
 
688
720
  it "calls #make with attachment passed as third argument" do
689
- Paperclip::Test.expects(:make).returns(@file)
690
-
691
721
  @dummy.avatar = @file
692
722
 
693
723
  expect(Paperclip::Test).to have_received(:make).with(anything, anything, @dummy.avatar)
694
724
  end
725
+
726
+ it "calls #make and unlinks intermediary files afterward" do
727
+ @dummy.avatar.expects(:unlink_files).with([@file, @file])
728
+
729
+ @dummy.avatar = @file
730
+ end
731
+ end
732
+ end
733
+
734
+ context "An attachment with a processor that returns original file" do
735
+ before do
736
+ class Paperclip::Test < Paperclip::Processor
737
+ def make; @file; end
738
+ end
739
+ rebuild_model processors: [:test], styles: { once: "100x100" }
740
+ @file = StringIO.new("...")
741
+ @file.stubs(:close)
742
+ @dummy = Dummy.new
743
+ end
744
+
745
+ context "when assigned" do
746
+ it "#calls #make and doesn't unlink the original file" do
747
+ @dummy.avatar.expects(:unlink_files).with([])
748
+
749
+ @dummy.avatar = @file
750
+ end
695
751
  end
696
752
  end
697
753
 
@@ -1065,7 +1121,7 @@ describe Paperclip::Attachment do
1065
1121
  context "with a file assigned but not saved yet" do
1066
1122
  it "clears out any attached files" do
1067
1123
  @attachment.assign(@file)
1068
- assert !@attachment.queued_for_write.blank?
1124
+ assert @attachment.queued_for_write.present?
1069
1125
  @attachment.clear
1070
1126
  assert @attachment.queued_for_write.blank?
1071
1127
  end
@@ -1315,6 +1371,12 @@ describe Paperclip::Attachment do
1315
1371
  end
1316
1372
 
1317
1373
  it "does not calculate fingerprint" do
1374
+ Digest::MD5.stubs(:file)
1375
+ @dummy.avatar = @file
1376
+ expect(Digest::MD5).to have_received(:file).never
1377
+ end
1378
+
1379
+ it "does not assign fingerprint" do
1318
1380
  @dummy.avatar = @file
1319
1381
  assert_nil @dummy.avatar.fingerprint
1320
1382
  end
@@ -1452,6 +1514,4 @@ describe Paperclip::Attachment do
1452
1514
  assert_file_exists(@path)
1453
1515
  end
1454
1516
  end
1455
-
1456
1517
  end
1457
-
@@ -1,6 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Paperclip::ContentTypeDetector do
4
+ it 'returns a meaningful content type for open xml spreadsheets' do
5
+ file = File.new(fixture_file("empty.xlsx"))
6
+ assert_equal "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
7
+ Paperclip::ContentTypeDetector.new(file.path).detect
8
+ end
9
+
4
10
  it 'gives a sensible default when the name is empty' do
5
11
  assert_equal "application/octet-stream", Paperclip::ContentTypeDetector.new("").detect
6
12
  end
@@ -13,7 +19,8 @@ describe Paperclip::ContentTypeDetector do
13
19
 
14
20
  it 'returns content type of file if it is an acceptable type' do
15
21
  MIME::Types.stubs(:type_for).returns([MIME::Type.new('application/mp4'), MIME::Type.new('video/mp4'), MIME::Type.new('audio/mp4')])
16
- Paperclip.stubs(:run).returns("video/mp4")
22
+ Paperclip::ContentTypeDetector.any_instance
23
+ .stubs(:type_from_file_contents).returns("video/mp4")
17
24
  @filename = "my_file.mp4"
18
25
  assert_equal "video/mp4", Paperclip::ContentTypeDetector.new(@filename).detect
19
26
  end
@@ -24,4 +24,3 @@ describe Paperclip::FileCommandContentTypeDetector do
24
24
  Paperclip::FileCommandContentTypeDetector.new("windows").detect
25
25
  end
26
26
  end
27
-
@@ -82,7 +82,7 @@ describe Paperclip::Geometry do
82
82
  assert_equal 456, @upper.height
83
83
  end
84
84
 
85
- ['>', '<', '#', '@', '%', '^', '!', nil].each do |mod|
85
+ ['>', '<', '#', '@', '@>', '>@', '%', '^', '!', nil].each do |mod|
86
86
  it "ensures the modifier #{description} is preserved" do
87
87
  assert @geo = Paperclip::Geometry.parse("123x456#{mod}")
88
88
  assert_equal mod, @geo.modifier
@@ -0,0 +1,44 @@
1
+ # require "spec_helper"
2
+
3
+ describe Paperclip::Glue do
4
+ describe "when ActiveRecord does not exist" do
5
+ before do
6
+ ActiveRecordSaved = ActiveRecord
7
+ Object.send :remove_const, "ActiveRecord"
8
+ end
9
+
10
+ after do
11
+ ActiveRecord = ActiveRecordSaved
12
+ Object.send :remove_const, "ActiveRecordSaved"
13
+ end
14
+
15
+ it "does not fail" do
16
+ NonActiveRecordModel = Class.new
17
+ NonActiveRecordModel.send :include, Paperclip::Glue
18
+ Object.send :remove_const, "NonActiveRecordModel"
19
+ end
20
+ end
21
+
22
+ describe "when ActiveRecord does exist" do
23
+ before do
24
+ if Object.const_defined?("ActiveRecord")
25
+ @defined_active_record = false
26
+ else
27
+ ActiveRecord = :defined
28
+ @defined_active_record = true
29
+ end
30
+ end
31
+
32
+ after do
33
+ if @defined_active_record
34
+ Object.send :remove_const, "ActiveRecord"
35
+ end
36
+ end
37
+
38
+ it "does not fail" do
39
+ NonActiveRecordModel = Class.new
40
+ NonActiveRecordModel.send :include, Paperclip::Glue
41
+ Object.send :remove_const, "NonActiveRecordModel"
42
+ end
43
+ end
44
+ end
@@ -38,6 +38,15 @@ describe Paperclip::HasAttachedFile do
38
38
  assert_adding_attachment('avatar').defines_callback('after_commit')
39
39
  end
40
40
 
41
+ context 'when the class does not allow after_commit callbacks' do
42
+ it 'defines an after_destroy callback' do
43
+ assert_adding_attachment(
44
+ 'avatar',
45
+ unstub_methods: [:after_commit]
46
+ ).defines_callback('after_destroy')
47
+ end
48
+ end
49
+
41
50
  it 'defines the Paperclip-specific callbacks' do
42
51
  assert_adding_attachment('avatar').defines_callback('define_paperclip_callbacks')
43
52
  end
@@ -53,20 +62,26 @@ describe Paperclip::HasAttachedFile do
53
62
 
54
63
  private
55
64
 
56
- def assert_adding_attachment(attachment_name)
57
- AttachmentAdder.new(attachment_name)
65
+ def assert_adding_attachment(attachment_name, options={})
66
+ AttachmentAdder.new(attachment_name, options)
58
67
  end
59
68
 
60
69
  class AttachmentAdder
61
70
  include Mocha::API
62
71
  include RSpec::Matchers
63
72
 
64
- def initialize(attachment_name)
73
+ def initialize(attachment_name, options = {})
65
74
  @attachment_name = attachment_name
75
+ @stubbed_class = stub_class
76
+ if options.present?
77
+ options[:unstub_methods].each do |method|
78
+ @stubbed_class.unstub(method)
79
+ end
80
+ end
66
81
  end
67
82
 
68
83
  def defines_method(method_name)
69
- a_class = stub_class
84
+ a_class = @stubbed_class
70
85
 
71
86
  Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {})
72
87
 
@@ -74,7 +89,7 @@ describe Paperclip::HasAttachedFile do
74
89
  end
75
90
 
76
91
  def defines_class_method(method_name)
77
- a_class = stub_class
92
+ a_class = @stubbed_class
78
93
  a_class.class.stubs(:define_method)
79
94
 
80
95
  Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {})
@@ -83,7 +98,7 @@ describe Paperclip::HasAttachedFile do
83
98
  end
84
99
 
85
100
  def defines_validation
86
- a_class = stub_class
101
+ a_class = @stubbed_class
87
102
 
88
103
  Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {})
89
104
 
@@ -91,7 +106,7 @@ describe Paperclip::HasAttachedFile do
91
106
  end
92
107
 
93
108
  def registers_attachment
94
- a_class = stub_class
109
+ a_class = @stubbed_class
95
110
  Paperclip::AttachmentRegistry.stubs(:register)
96
111
 
97
112
  Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {size: 1})
@@ -100,7 +115,7 @@ describe Paperclip::HasAttachedFile do
100
115
  end
101
116
 
102
117
  def defines_callback(callback_name)
103
- a_class = stub_class
118
+ a_class = @stubbed_class
104
119
 
105
120
  Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {})
106
121
 
@@ -132,6 +147,7 @@ describe Paperclip::HasAttachedFile do
132
147
  after_save: nil,
133
148
  before_destroy: nil,
134
149
  after_commit: nil,
150
+ after_destroy: nil,
135
151
  define_paperclip_callbacks: nil,
136
152
  extend: nil,
137
153
  name: 'Billy',
@@ -7,9 +7,10 @@ describe 'Paperclip' do
7
7
  before do
8
8
  rebuild_model
9
9
  @file = File.new(fixture_file("5k.png"), 'rb')
10
- 300.times do |i|
11
- Dummy.create! avatar: @file
12
- end
10
+ # Deals with `Too many open files` error
11
+ Dummy.import 100.times.map { Dummy.new avatar: @file }
12
+ Dummy.import 100.times.map { Dummy.new avatar: @file }
13
+ Dummy.import 100.times.map { Dummy.new avatar: @file }
13
14
  end
14
15
 
15
16
  after { @file.close }
@@ -24,15 +24,16 @@ describe Paperclip::Interpolations do
24
24
  end
25
25
 
26
26
  it "returns the class of the instance" do
27
+ class Thing ; end
27
28
  attachment = mock
28
29
  attachment.expects(:instance).returns(attachment)
29
- attachment.expects(:class).returns("Thing")
30
+ attachment.expects(:class).returns(Thing)
30
31
  assert_equal "things", Paperclip::Interpolations.class(attachment, :style)
31
32
  end
32
33
 
33
34
  it "returns the basename of the file" do
34
35
  attachment = mock
35
- attachment.expects(:original_filename).returns("one.jpg").times(2)
36
+ attachment.expects(:original_filename).returns("one.jpg").times(1)
36
37
  assert_equal "one", Paperclip::Interpolations.basename(attachment, :style)
37
38
  end
38
39
 
@@ -138,14 +139,7 @@ describe Paperclip::Interpolations do
138
139
  assert_equal "000/000/023", Paperclip::Interpolations.id_partition(attachment, :style)
139
140
  end
140
141
 
141
- it "returns the partitioned id of the attachment when the id is a short string" do
142
- attachment = mock
143
- attachment.expects(:id).returns("fnj23")
144
- attachment.expects(:instance).returns(attachment)
145
- assert_equal "000/0fn/j23", Paperclip::Interpolations.id_partition(attachment, :style)
146
- end
147
-
148
- it "returns the partitioned id of the attachment when the id is a long string" do
142
+ it "returns the partitioned id of the attachment when the id is a string" do
149
143
  attachment = mock
150
144
  attachment.expects(:id).returns("32fnj23oio2f")
151
145
  attachment.expects(:instance).returns(attachment)
@@ -194,14 +188,14 @@ describe Paperclip::Interpolations do
194
188
  it "returns the filename as basename.extension" do
195
189
  attachment = mock
196
190
  attachment.expects(:styles).returns({})
197
- attachment.expects(:original_filename).returns("one.jpg").times(3)
191
+ attachment.expects(:original_filename).returns("one.jpg").times(2)
198
192
  assert_equal "one.jpg", Paperclip::Interpolations.filename(attachment, :style)
199
193
  end
200
194
 
201
195
  it "returns the filename as basename.extension when format supplied" do
202
196
  attachment = mock
203
197
  attachment.expects(:styles).returns({style: {format: :png}})
204
- attachment.expects(:original_filename).returns("one.jpg").times(2)
198
+ attachment.expects(:original_filename).returns("one.jpg").times(1)
205
199
  assert_equal "one.png", Paperclip::Interpolations.filename(attachment, :style)
206
200
  end
207
201
 
@@ -211,7 +205,7 @@ describe Paperclip::Interpolations do
211
205
  attachment.stubs(:original_filename).returns("one")
212
206
  assert_equal "one", Paperclip::Interpolations.filename(attachment, :style)
213
207
  end
214
-
208
+
215
209
  it "returns the basename when the extension contains regexp special characters" do
216
210
  attachment = mock
217
211
  attachment.stubs(:styles).returns({})
@@ -256,4 +250,13 @@ describe Paperclip::Interpolations do
256
250
  value = Paperclip::Interpolations.interpolate(":notreal/:id/:attachment", :attachment, :style)
257
251
  assert_equal ":notreal/1234/attachments", value
258
252
  end
253
+
254
+ it "handles question marks" do
255
+ Paperclip.interpolates :foo? do
256
+ "bar"
257
+ end
258
+ Paperclip::Interpolations.expects(:fool).never
259
+ value = Paperclip::Interpolations.interpolate(":fo/:foo?")
260
+ assert_equal ":fo/bar", value
261
+ end
259
262
  end
@@ -9,11 +9,12 @@ describe Paperclip::AbstractAdapter do
9
9
  end
10
10
  end
11
11
 
12
- context "content type from file command" do
12
+ context "content type from file contents" do
13
13
  before do
14
14
  @adapter = TestAdapter.new
15
15
  @adapter.stubs(:path).returns("image.png")
16
16
  Paperclip.stubs(:run).returns("image/png\n")
17
+ Paperclip::ContentTypeDetector.any_instance.stubs(:type_from_mime_magic).returns("image/png")
17
18
  end
18
19
 
19
20
  it "returns the content type without newline" do