paperclip_jk 5.0.0.beta2

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 (190) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +21 -0
  3. data/.hound.yml +1066 -0
  4. data/.rubocop.yml +1 -0
  5. data/.travis.yml +26 -0
  6. data/Appraisals +27 -0
  7. data/CONTRIBUTING.md +86 -0
  8. data/Gemfile +15 -0
  9. data/LICENSE +24 -0
  10. data/NEWS +424 -0
  11. data/README.md +999 -0
  12. data/RELEASING.md +17 -0
  13. data/Rakefile +44 -0
  14. data/UPGRADING +17 -0
  15. data/features/basic_integration.feature +81 -0
  16. data/features/migration.feature +70 -0
  17. data/features/rake_tasks.feature +62 -0
  18. data/features/step_definitions/attachment_steps.rb +110 -0
  19. data/features/step_definitions/html_steps.rb +15 -0
  20. data/features/step_definitions/rails_steps.rb +230 -0
  21. data/features/step_definitions/s3_steps.rb +14 -0
  22. data/features/step_definitions/web_steps.rb +107 -0
  23. data/features/support/env.rb +11 -0
  24. data/features/support/fakeweb.rb +13 -0
  25. data/features/support/file_helpers.rb +34 -0
  26. data/features/support/fixtures/boot_config.txt +15 -0
  27. data/features/support/fixtures/gemfile.txt +5 -0
  28. data/features/support/fixtures/preinitializer.txt +20 -0
  29. data/features/support/paths.rb +28 -0
  30. data/features/support/rails.rb +63 -0
  31. data/features/support/selectors.rb +19 -0
  32. data/gemfiles/4.2.awsv2.0.gemfile +17 -0
  33. data/gemfiles/4.2.awsv2.1.gemfile +17 -0
  34. data/gemfiles/4.2.awsv2.gemfile +20 -0
  35. data/gemfiles/5.0.awsv2.0.gemfile +17 -0
  36. data/gemfiles/5.0.awsv2.1.gemfile +17 -0
  37. data/gemfiles/5.0.awsv2.gemfile +25 -0
  38. data/lib/generators/paperclip/USAGE +8 -0
  39. data/lib/generators/paperclip/paperclip_generator.rb +30 -0
  40. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
  41. data/lib/paperclip.rb +211 -0
  42. data/lib/paperclip/attachment.rb +610 -0
  43. data/lib/paperclip/attachment_registry.rb +60 -0
  44. data/lib/paperclip/callbacks.rb +42 -0
  45. data/lib/paperclip/content_type_detector.rb +80 -0
  46. data/lib/paperclip/errors.rb +34 -0
  47. data/lib/paperclip/file_command_content_type_detector.rb +30 -0
  48. data/lib/paperclip/filename_cleaner.rb +16 -0
  49. data/lib/paperclip/geometry.rb +158 -0
  50. data/lib/paperclip/geometry_detector_factory.rb +48 -0
  51. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  52. data/lib/paperclip/glue.rb +17 -0
  53. data/lib/paperclip/has_attached_file.rb +115 -0
  54. data/lib/paperclip/helpers.rb +60 -0
  55. data/lib/paperclip/interpolations.rb +197 -0
  56. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  57. data/lib/paperclip/io_adapters/abstract_adapter.rb +47 -0
  58. data/lib/paperclip/io_adapters/attachment_adapter.rb +36 -0
  59. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  60. data/lib/paperclip/io_adapters/empty_string_adapter.rb +18 -0
  61. data/lib/paperclip/io_adapters/file_adapter.rb +22 -0
  62. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +15 -0
  63. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
  64. data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
  65. data/lib/paperclip/io_adapters/registry.rb +32 -0
  66. data/lib/paperclip/io_adapters/stringio_adapter.rb +33 -0
  67. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +42 -0
  68. data/lib/paperclip/io_adapters/uri_adapter.rb +44 -0
  69. data/lib/paperclip/locales/en.yml +18 -0
  70. data/lib/paperclip/logger.rb +21 -0
  71. data/lib/paperclip/matchers.rb +64 -0
  72. data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
  73. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +100 -0
  74. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
  75. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +96 -0
  76. data/lib/paperclip/media_type_spoof_detector.rb +89 -0
  77. data/lib/paperclip/missing_attachment_styles.rb +79 -0
  78. data/lib/paperclip/processor.rb +48 -0
  79. data/lib/paperclip/processor_helpers.rb +50 -0
  80. data/lib/paperclip/rails_environment.rb +25 -0
  81. data/lib/paperclip/railtie.rb +31 -0
  82. data/lib/paperclip/schema.rb +77 -0
  83. data/lib/paperclip/storage.rb +4 -0
  84. data/lib/paperclip/storage/database.rb +140 -0
  85. data/lib/paperclip/storage/filesystem.rb +90 -0
  86. data/lib/paperclip/storage/fog.rb +244 -0
  87. data/lib/paperclip/storage/s3.rb +442 -0
  88. data/lib/paperclip/style.rb +109 -0
  89. data/lib/paperclip/tempfile.rb +43 -0
  90. data/lib/paperclip/tempfile_factory.rb +23 -0
  91. data/lib/paperclip/thumbnail.rb +121 -0
  92. data/lib/paperclip/url_generator.rb +72 -0
  93. data/lib/paperclip/validators.rb +74 -0
  94. data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
  95. data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
  96. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
  97. data/lib/paperclip/validators/attachment_presence_validator.rb +30 -0
  98. data/lib/paperclip/validators/attachment_size_validator.rb +109 -0
  99. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
  100. data/lib/paperclip/version.rb +3 -0
  101. data/lib/tasks/paperclip.rake +127 -0
  102. data/paperclip.gemspec +54 -0
  103. data/shoulda_macros/paperclip.rb +134 -0
  104. data/spec/database.yml +4 -0
  105. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  106. data/spec/paperclip/attachment_processing_spec.rb +80 -0
  107. data/spec/paperclip/attachment_registry_spec.rb +158 -0
  108. data/spec/paperclip/attachment_spec.rb +1517 -0
  109. data/spec/paperclip/content_type_detector_spec.rb +48 -0
  110. data/spec/paperclip/file_command_content_type_detector_spec.rb +26 -0
  111. data/spec/paperclip/filename_cleaner_spec.rb +14 -0
  112. data/spec/paperclip/geometry_detector_spec.rb +39 -0
  113. data/spec/paperclip/geometry_parser_spec.rb +73 -0
  114. data/spec/paperclip/geometry_spec.rb +255 -0
  115. data/spec/paperclip/glue_spec.rb +44 -0
  116. data/spec/paperclip/has_attached_file_spec.rb +158 -0
  117. data/spec/paperclip/integration_spec.rb +668 -0
  118. data/spec/paperclip/interpolations_spec.rb +262 -0
  119. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +78 -0
  120. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +139 -0
  121. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +83 -0
  122. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  123. data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
  124. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +113 -0
  125. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  126. data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
  127. data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
  128. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
  129. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
  130. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +102 -0
  131. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  132. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +109 -0
  133. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  134. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  135. data/spec/paperclip/media_type_spoof_detector_spec.rb +70 -0
  136. data/spec/paperclip/meta_class_spec.rb +30 -0
  137. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
  138. data/spec/paperclip/paperclip_spec.rb +192 -0
  139. data/spec/paperclip/plural_cache_spec.rb +37 -0
  140. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  141. data/spec/paperclip/processor_spec.rb +26 -0
  142. data/spec/paperclip/rails_environment_spec.rb +33 -0
  143. data/spec/paperclip/rake_spec.rb +103 -0
  144. data/spec/paperclip/schema_spec.rb +248 -0
  145. data/spec/paperclip/storage/filesystem_spec.rb +79 -0
  146. data/spec/paperclip/storage/fog_spec.rb +545 -0
  147. data/spec/paperclip/storage/s3_live_spec.rb +186 -0
  148. data/spec/paperclip/storage/s3_spec.rb +1583 -0
  149. data/spec/paperclip/style_spec.rb +255 -0
  150. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  151. data/spec/paperclip/thumbnail_spec.rb +500 -0
  152. data/spec/paperclip/url_generator_spec.rb +211 -0
  153. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  154. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
  155. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
  156. data/spec/paperclip/validators/attachment_size_validator_spec.rb +235 -0
  157. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
  158. data/spec/paperclip/validators_spec.rb +164 -0
  159. data/spec/spec_helper.rb +45 -0
  160. data/spec/support/assertions.rb +78 -0
  161. data/spec/support/fake_model.rb +25 -0
  162. data/spec/support/fake_rails.rb +12 -0
  163. data/spec/support/fixtures/12k.png +0 -0
  164. data/spec/support/fixtures/50x50.png +0 -0
  165. data/spec/support/fixtures/5k.png +0 -0
  166. data/spec/support/fixtures/animated +0 -0
  167. data/spec/support/fixtures/animated.gif +0 -0
  168. data/spec/support/fixtures/animated.unknown +0 -0
  169. data/spec/support/fixtures/bad.png +1 -0
  170. data/spec/support/fixtures/empty.html +1 -0
  171. data/spec/support/fixtures/empty.xlsx +0 -0
  172. data/spec/support/fixtures/fog.yml +8 -0
  173. data/spec/support/fixtures/rotated.jpg +0 -0
  174. data/spec/support/fixtures/s3.yml +8 -0
  175. data/spec/support/fixtures/spaced file.jpg +0 -0
  176. data/spec/support/fixtures/spaced file.png +0 -0
  177. data/spec/support/fixtures/text.txt +1 -0
  178. data/spec/support/fixtures/twopage.pdf +0 -0
  179. data/spec/support/fixtures/uppercase.PNG +0 -0
  180. data/spec/support/matchers/accept.rb +5 -0
  181. data/spec/support/matchers/exist.rb +5 -0
  182. data/spec/support/matchers/have_column.rb +23 -0
  183. data/spec/support/mock_attachment.rb +22 -0
  184. data/spec/support/mock_interpolator.rb +24 -0
  185. data/spec/support/mock_url_generator_builder.rb +27 -0
  186. data/spec/support/model_reconstruction.rb +68 -0
  187. data/spec/support/reporting.rb +11 -0
  188. data/spec/support/test_data.rb +13 -0
  189. data/spec/support/version_helper.rb +9 -0
  190. metadata +713 -0
@@ -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
@@ -0,0 +1,158 @@
1
+ require 'spec_helper'
2
+
3
+ describe Paperclip::HasAttachedFile do
4
+ context '#define_on' do
5
+ it 'defines a setter on the class object' do
6
+ assert_adding_attachment('avatar').defines_method('avatar=')
7
+ end
8
+
9
+ it 'defines a getter on the class object' do
10
+ assert_adding_attachment('avatar').defines_method('avatar')
11
+ end
12
+
13
+ it 'defines a query on the class object' do
14
+ assert_adding_attachment('avatar').defines_method('avatar?')
15
+ end
16
+
17
+ it 'defines a method on the class to get all of its attachments' do
18
+ assert_adding_attachment('avatar').defines_class_method('attachment_definitions')
19
+ end
20
+
21
+ it 'flushes errors as part of validations' do
22
+ assert_adding_attachment('avatar').defines_validation
23
+ end
24
+
25
+ it 'registers the attachment with Paperclip::AttachmentRegistry' do
26
+ assert_adding_attachment('avatar').registers_attachment
27
+ end
28
+
29
+ it 'defines an after_save callback' do
30
+ assert_adding_attachment('avatar').defines_callback('after_save')
31
+ end
32
+
33
+ it 'defines a before_destroy callback' do
34
+ assert_adding_attachment('avatar').defines_callback('before_destroy')
35
+ end
36
+
37
+ it 'defines an after_commit callback' do
38
+ assert_adding_attachment('avatar').defines_callback('after_commit')
39
+ end
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
+
50
+ it 'defines the Paperclip-specific callbacks' do
51
+ assert_adding_attachment('avatar').defines_callback('define_paperclip_callbacks')
52
+ end
53
+
54
+ it 'does not define a media_type check if told not to' do
55
+ assert_adding_attachment('avatar').does_not_set_up_media_type_check_validation
56
+ end
57
+
58
+ it 'does define a media_type check if told to' do
59
+ assert_adding_attachment('avatar').sets_up_media_type_check_validation
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def assert_adding_attachment(attachment_name, options={})
66
+ AttachmentAdder.new(attachment_name, options)
67
+ end
68
+
69
+ class AttachmentAdder
70
+ include Mocha::API
71
+ include RSpec::Matchers
72
+
73
+ def initialize(attachment_name, options = {})
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
81
+ end
82
+
83
+ def defines_method(method_name)
84
+ a_class = @stubbed_class
85
+
86
+ Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {})
87
+
88
+ expect(a_class).to have_received(:define_method).with(method_name)
89
+ end
90
+
91
+ def defines_class_method(method_name)
92
+ a_class = @stubbed_class
93
+ a_class.class.stubs(:define_method)
94
+
95
+ Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {})
96
+
97
+ expect(a_class).to have_received(:extend).with(Paperclip::HasAttachedFile::ClassMethods)
98
+ end
99
+
100
+ def defines_validation
101
+ a_class = @stubbed_class
102
+
103
+ Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {})
104
+
105
+ expect(a_class).to have_received(:validates_each).with(@attachment_name)
106
+ end
107
+
108
+ def registers_attachment
109
+ a_class = @stubbed_class
110
+ Paperclip::AttachmentRegistry.stubs(:register)
111
+
112
+ Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {size: 1})
113
+
114
+ expect(Paperclip::AttachmentRegistry).to have_received(:register).with(a_class, @attachment_name, {size: 1})
115
+ end
116
+
117
+ def defines_callback(callback_name)
118
+ a_class = @stubbed_class
119
+
120
+ Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, {})
121
+
122
+ expect(a_class).to have_received(callback_name.to_sym)
123
+ end
124
+
125
+ def does_not_set_up_media_type_check_validation
126
+ a_class = stub_class
127
+
128
+ Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, { validate_media_type: false })
129
+
130
+ expect(a_class).to have_received(:validates_media_type_spoof_detection).never
131
+ end
132
+
133
+ def sets_up_media_type_check_validation
134
+ a_class = stub_class
135
+
136
+ Paperclip::HasAttachedFile.define_on(a_class, @attachment_name, { validate_media_type: true })
137
+
138
+ expect(a_class).to have_received(:validates_media_type_spoof_detection)
139
+ end
140
+
141
+ private
142
+
143
+ def stub_class
144
+ stub('class',
145
+ validates_each: nil,
146
+ define_method: nil,
147
+ after_save: nil,
148
+ before_destroy: nil,
149
+ after_commit: nil,
150
+ after_destroy: nil,
151
+ define_paperclip_callbacks: nil,
152
+ extend: nil,
153
+ name: 'Billy',
154
+ validates_media_type_spoof_detection: nil
155
+ )
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,668 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'open-uri'
4
+
5
+ describe 'Paperclip' do
6
+ context "Many models at once" do
7
+ before do
8
+ rebuild_model
9
+ @file = File.new(fixture_file("5k.png"), 'rb')
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 }
14
+ end
15
+
16
+ after { @file.close }
17
+
18
+ it "does not exceed the open file limit" do
19
+ assert_nothing_raised do
20
+ Dummy.all.each { |dummy| dummy.avatar }
21
+ end
22
+ end
23
+ end
24
+
25
+ context "An attachment" do
26
+ before do
27
+ rebuild_model styles: { thumb: "50x50#" }
28
+ @dummy = Dummy.new
29
+ @file = File.new(fixture_file("5k.png"), 'rb')
30
+ @dummy.avatar = @file
31
+ assert @dummy.save
32
+ end
33
+
34
+ after { @file.close }
35
+
36
+ it "creates its thumbnails properly" do
37
+ assert_match(/\b50x50\b/, `identify "#{@dummy.avatar.path(:thumb)}"`)
38
+ end
39
+
40
+ context 'reprocessing with unreadable original' do
41
+ before { File.chmod(0000, @dummy.avatar.path) }
42
+
43
+ it "does not raise an error" do
44
+ assert_nothing_raised do
45
+ silence_stream(STDERR) do
46
+ @dummy.avatar.reprocess!
47
+ end
48
+ end
49
+ end
50
+
51
+ it "returns false" do
52
+ silence_stream(STDERR) do
53
+ assert !@dummy.avatar.reprocess!
54
+ end
55
+ end
56
+
57
+ after { File.chmod(0644, @dummy.avatar.path) }
58
+ end
59
+
60
+ context "redefining its attachment styles" do
61
+ before do
62
+ Dummy.class_eval do
63
+ has_attached_file :avatar, styles: { thumb: "150x25#", dynamic: lambda { |a| '50x50#' } }
64
+ end
65
+ @d2 = Dummy.find(@dummy.id)
66
+ @original_timestamp = @d2.avatar_updated_at
67
+ @d2.avatar.reprocess!
68
+ @d2.save
69
+ end
70
+
71
+ it "creates its thumbnails properly" do
72
+ assert_match(/\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`)
73
+ assert_match(/\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`)
74
+ end
75
+
76
+ it "changes the timestamp" do
77
+ assert_not_equal @original_timestamp, @d2.avatar_updated_at
78
+ end
79
+ end
80
+ end
81
+
82
+ context "Attachment" do
83
+ before do
84
+ @thumb_path = "tmp/public/system/dummies/avatars/000/000/001/thumb/5k.png"
85
+ File.delete(@thumb_path) if File.exist?(@thumb_path)
86
+ rebuild_model styles: { thumb: "50x50#" }
87
+ @dummy = Dummy.new
88
+ @file = File.new(fixture_file("5k.png"), 'rb')
89
+
90
+ end
91
+
92
+ after { @file.close }
93
+
94
+ it "does not create the thumbnails upon saving when post-processing is disabled" do
95
+ @dummy.avatar.post_processing = false
96
+ @dummy.avatar = @file
97
+ assert @dummy.save
98
+ assert_file_not_exists @thumb_path
99
+ end
100
+
101
+ it "creates the thumbnails upon saving when post_processing is enabled" do
102
+ @dummy.avatar.post_processing = true
103
+ @dummy.avatar = @file
104
+ assert @dummy.save
105
+ assert_file_exists @thumb_path
106
+ end
107
+ end
108
+
109
+ context "Attachment with no generated thumbnails" do
110
+ before do
111
+ @thumb_small_path = "tmp/public/system/dummies/avatars/000/000/001/thumb_small/5k.png"
112
+ @thumb_large_path = "tmp/public/system/dummies/avatars/000/000/001/thumb_large/5k.png"
113
+ File.delete(@thumb_small_path) if File.exist?(@thumb_small_path)
114
+ File.delete(@thumb_large_path) if File.exist?(@thumb_large_path)
115
+ rebuild_model styles: { thumb_small: "50x50#", thumb_large: "60x60#" }
116
+ @dummy = Dummy.new
117
+ @file = File.new(fixture_file("5k.png"), 'rb')
118
+
119
+ @dummy.avatar.post_processing = false
120
+ @dummy.avatar = @file
121
+ assert @dummy.save
122
+ @dummy.avatar.post_processing = true
123
+ end
124
+
125
+ after { @file.close }
126
+
127
+ it "allows us to create all thumbnails in one go" do
128
+ assert_file_not_exists(@thumb_small_path)
129
+ assert_file_not_exists(@thumb_large_path)
130
+
131
+ @dummy.avatar.reprocess!
132
+
133
+ assert_file_exists(@thumb_small_path)
134
+ assert_file_exists(@thumb_large_path)
135
+ end
136
+
137
+ it "allows us to selectively create each thumbnail" do
138
+ assert_file_not_exists(@thumb_small_path)
139
+ assert_file_not_exists(@thumb_large_path)
140
+
141
+ @dummy.avatar.reprocess! :thumb_small
142
+ assert_file_exists(@thumb_small_path)
143
+ assert_file_not_exists(@thumb_large_path)
144
+
145
+ @dummy.avatar.reprocess! :thumb_large
146
+ assert_file_exists(@thumb_large_path)
147
+ end
148
+ end
149
+
150
+ context "A model that modifies its original" do
151
+ before do
152
+ rebuild_model styles: { original: "2x2#" }
153
+ @dummy = Dummy.new
154
+ @file = File.new(fixture_file("5k.png"), 'rb')
155
+ @dummy.avatar = @file
156
+ end
157
+
158
+ it "reports the file size of the processed file and not the original" do
159
+ assert_not_equal File.size(@file.path), @dummy.avatar.size
160
+ end
161
+
162
+ after { @file.close }
163
+ end
164
+
165
+ context "A model with attachments scoped under an id" do
166
+ before do
167
+ rebuild_model styles: { large: "100x100",
168
+ medium: "50x50" },
169
+ path: ":rails_root/tmp/:id/:attachments/:style.:extension"
170
+ @dummy = Dummy.new
171
+ @file = File.new(fixture_file("5k.png"), 'rb')
172
+ @dummy.avatar = @file
173
+ end
174
+
175
+ after { @file.close }
176
+
177
+ context "when saved" do
178
+ before do
179
+ @dummy.save
180
+ @saved_path = @dummy.avatar.path(:large)
181
+ end
182
+
183
+ it "has a large file in the right place" do
184
+ assert_file_exists(@dummy.avatar.path(:large))
185
+ end
186
+
187
+ context "and deleted" do
188
+ before do
189
+ @dummy.avatar.clear
190
+ @dummy.save
191
+ end
192
+
193
+ it "does not have a large file in the right place anymore" do
194
+ assert_file_not_exists(@saved_path)
195
+ end
196
+
197
+ it "does not have its next two parent directories" do
198
+ assert_file_not_exists(File.dirname(@saved_path))
199
+ assert_file_not_exists(File.dirname(File.dirname(@saved_path)))
200
+ end
201
+ end
202
+
203
+ context 'and deleted where the delete fails' do
204
+ it "does not die if an unexpected SystemCallError happens" do
205
+ FileUtils.stubs(:rmdir).raises(Errno::EPIPE)
206
+ assert_nothing_raised do
207
+ @dummy.avatar.clear
208
+ @dummy.save
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ [000,002,022].each do |umask|
216
+ context "when the umask is #{umask}" do
217
+ before do
218
+ rebuild_model
219
+ @dummy = Dummy.new
220
+ @file = File.new(fixture_file("5k.png"), 'rb')
221
+ @umask = File.umask(umask)
222
+ end
223
+
224
+ after do
225
+ File.umask @umask
226
+ @file.close
227
+ end
228
+
229
+ it "respects the current umask" do
230
+ @dummy.avatar = @file
231
+ @dummy.save
232
+ assert_equal 0666&~umask, 0666&File.stat(@dummy.avatar.path).mode
233
+ end
234
+ end
235
+ end
236
+
237
+ [0666,0664,0640].each do |perms|
238
+ context "when the perms are #{perms}" do
239
+ before do
240
+ rebuild_model override_file_permissions: perms
241
+ @dummy = Dummy.new
242
+ @file = File.new(fixture_file("5k.png"), 'rb')
243
+ end
244
+
245
+ after do
246
+ @file.close
247
+ end
248
+
249
+ it "respects the current perms" do
250
+ @dummy.avatar = @file
251
+ @dummy.save
252
+ assert_equal perms, File.stat(@dummy.avatar.path).mode & 0777
253
+ end
254
+ end
255
+ end
256
+
257
+ it "skips chmod operation, when override_file_permissions is set to false (e.g. useful when using CIFS mounts)" do
258
+ FileUtils.expects(:chmod).never
259
+
260
+ rebuild_model override_file_permissions: false
261
+ dummy = Dummy.create!
262
+ dummy.avatar = @file
263
+ dummy.save
264
+ end
265
+
266
+ context "A model with a filesystem attachment" do
267
+ before do
268
+ rebuild_model styles: { large: "300x300>",
269
+ medium: "100x100",
270
+ thumb: ["32x32#", :gif] },
271
+ default_style: :medium,
272
+ url: "/:attachment/:class/:style/:id/:basename.:extension",
273
+ path: ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
274
+ @dummy = Dummy.new
275
+ @file = File.new(fixture_file("5k.png"), 'rb')
276
+ @bad_file = File.new(fixture_file("bad.png"), 'rb')
277
+
278
+ assert @dummy.avatar = @file
279
+ assert @dummy.valid?, @dummy.errors.full_messages.join(", ")
280
+ assert @dummy.save
281
+ end
282
+
283
+ after { [@file, @bad_file].each(&:close) }
284
+
285
+ it "writes and delete its files" do
286
+ [["434x66", :original],
287
+ ["300x46", :large],
288
+ ["100x15", :medium],
289
+ ["32x32", :thumb]].each do |geo, style|
290
+ cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"]
291
+ assert_equal geo, `#{cmd}`.chomp, cmd
292
+ end
293
+
294
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
295
+
296
+ @d2 = Dummy.find(@dummy.id)
297
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp
298
+ assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp
299
+ assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp
300
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
301
+ assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
302
+
303
+ assert @dummy.valid?
304
+ assert @dummy.save
305
+
306
+ saved_paths.each do |p|
307
+ assert_file_exists(p)
308
+ end
309
+
310
+ @dummy.avatar.clear
311
+ assert_nil @dummy.avatar_file_name
312
+ assert @dummy.valid?
313
+ assert @dummy.save
314
+
315
+ saved_paths.each do |p|
316
+ assert_file_not_exists(p)
317
+ end
318
+
319
+ @d2 = Dummy.find(@dummy.id)
320
+ assert_nil @d2.avatar_file_name
321
+ end
322
+
323
+ it "works exactly the same when new as when reloaded" do
324
+ @d2 = Dummy.find(@dummy.id)
325
+
326
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
327
+ [:thumb, :medium, :large, :original].each do |style|
328
+ assert_equal @dummy.avatar.path(style), @d2.avatar.path(style)
329
+ end
330
+
331
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
332
+
333
+ @d2.avatar.clear
334
+ assert @d2.save
335
+
336
+ saved_paths.each do |p|
337
+ assert_file_not_exists(p)
338
+ end
339
+ end
340
+
341
+ it "does not abide things that don't have adapters" do
342
+ assert_raises(Paperclip::AdapterRegistry::NoHandlerError) do
343
+ @dummy.avatar = "not a file"
344
+ end
345
+ end
346
+
347
+ it "is not ok with bad files" do
348
+ @dummy.avatar = @bad_file
349
+ assert ! @dummy.valid?
350
+ end
351
+
352
+ it "knows the difference between good files, bad files, and not files when validating" do
353
+ Dummy.validates_attachment_presence :avatar
354
+ @d2 = Dummy.find(@dummy.id)
355
+ @d2.avatar = @file
356
+ assert @d2.valid?, @d2.errors.full_messages.inspect
357
+ @d2.avatar = @bad_file
358
+ assert ! @d2.valid?
359
+ end
360
+
361
+ it "is able to reload without saving and not have the file disappear" do
362
+ @dummy.avatar = @file
363
+ assert @dummy.save, @dummy.errors.full_messages.inspect
364
+ @dummy.avatar.clear
365
+ assert_nil @dummy.avatar_file_name
366
+ @dummy.reload
367
+ assert_equal "5k.png", @dummy.avatar_file_name
368
+ end
369
+
370
+ context "that is assigned its file from another Paperclip attachment" do
371
+ before do
372
+ @dummy2 = Dummy.new
373
+ @file2 = File.new(fixture_file("12k.png"), 'rb')
374
+ assert @dummy2.avatar = @file2
375
+ @dummy2.save
376
+ end
377
+
378
+ after { @file2.close }
379
+
380
+ it "works when assigned a file" do
381
+ assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
382
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
383
+
384
+ assert @dummy.avatar = @dummy2.avatar
385
+ @dummy.save
386
+ assert_equal @dummy.avatar_file_name, @dummy2.avatar_file_name
387
+ assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
388
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
389
+ end
390
+ end
391
+
392
+ end
393
+
394
+ context "A model with an attachments association and a Paperclip attachment" do
395
+ before do
396
+ Dummy.class_eval do
397
+ has_many :attachments, class_name: 'Dummy'
398
+ end
399
+
400
+ @file = File.new(fixture_file("5k.png"), 'rb')
401
+ @dummy = Dummy.new
402
+ @dummy.avatar = @file
403
+ end
404
+
405
+ after { @file.close }
406
+
407
+ it "does not error when saving" do
408
+ @dummy.save!
409
+ end
410
+ end
411
+
412
+ context "A model with an attachment with hash in file name" do
413
+ before do
414
+ @settings = { styles: { thumb: "50x50#" },
415
+ path: ":rails_root/public/system/:attachment/:id_partition/:style/:hash.:extension",
416
+ url: "/system/:attachment/:id_partition/:style/:hash.:extension",
417
+ hash_secret: "somesecret" }
418
+
419
+ rebuild_model @settings
420
+
421
+ @file = File.new(fixture_file("5k.png"), 'rb')
422
+ @dummy = Dummy.create! avatar: @file
423
+ end
424
+
425
+ after do
426
+ @file.close
427
+ end
428
+
429
+ it "is accessible" do
430
+ assert_file_exists(@dummy.avatar.path(:original))
431
+ assert_file_exists(@dummy.avatar.path(:thumb))
432
+ end
433
+
434
+ context "when new style is added" do
435
+ before do
436
+ @dummy.avatar.options[:styles][:mini] = "25x25#"
437
+ @dummy.avatar.instance_variable_set :@normalized_styles, nil
438
+ Time.stubs(now: Time.now + 10)
439
+ @dummy.avatar.reprocess!
440
+ @dummy.reload
441
+ end
442
+
443
+ it "makes all the styles accessible" do
444
+ assert_file_exists(@dummy.avatar.path(:original))
445
+ assert_file_exists(@dummy.avatar.path(:thumb))
446
+ assert_file_exists(@dummy.avatar.path(:mini))
447
+ end
448
+ end
449
+ end
450
+
451
+ if ENV['S3_BUCKET']
452
+ def s3_files_for attachment
453
+ [:thumb, :medium, :large, :original].inject({}) do |files, style|
454
+ data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
455
+ t = Tempfile.new("paperclip-test")
456
+ t.binmode
457
+ t.write(data)
458
+ t.rewind
459
+ files[style] = t
460
+ files
461
+ end
462
+ end
463
+
464
+ def s3_headers_for attachment, style
465
+ `curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head|
466
+ split_head = head.chomp.split(/\s*:\s*/, 2)
467
+ h[split_head.first.downcase] = split_head.last unless split_head.empty?
468
+ h
469
+ end
470
+ end
471
+
472
+ context "A model with an S3 attachment" do
473
+ before do
474
+ rebuild_model(
475
+ styles: {
476
+ large: "300x300>",
477
+ medium: "100x100",
478
+ thumb: ["32x32#", :gif],
479
+ custom: {
480
+ geometry: "32x32#",
481
+ s3_headers: { 'Cache-Control' => 'max-age=31557600' },
482
+ s3_metadata: { 'foo' => 'bar'}
483
+ }
484
+ },
485
+ storage: :s3,
486
+ s3_credentials: File.new(fixture_file('s3.yml')),
487
+ s3_options: { logger: Paperclip.logger },
488
+ default_style: :medium,
489
+ bucket: ENV['S3_BUCKET'],
490
+ path: ":class/:attachment/:id/:style/:basename.:extension"
491
+ )
492
+
493
+ @dummy = Dummy.new
494
+ @file = File.new(fixture_file('5k.png'), 'rb')
495
+ @bad_file = File.new(fixture_file('bad.png'), 'rb')
496
+
497
+ @dummy.avatar = @file
498
+ @dummy.valid?
499
+ @dummy.save!
500
+
501
+ @files_on_s3 = s3_files_for(@dummy.avatar)
502
+ end
503
+
504
+ after do
505
+ @file.close
506
+ @bad_file.close
507
+ @files_on_s3.values.each(&:close) if @files_on_s3
508
+ end
509
+
510
+ context 'assigning itself to a new model' do
511
+ before do
512
+ @d2 = Dummy.new
513
+ @d2.avatar = @dummy.avatar
514
+ @d2.save
515
+ end
516
+
517
+ it "has the same name as the old file" do
518
+ assert_equal @d2.avatar.original_filename, @dummy.avatar.original_filename
519
+ end
520
+ end
521
+
522
+ it "has the same contents as the original" do
523
+ assert_equal @file.read, @files_on_s3[:original].read
524
+ end
525
+
526
+ it "writes and delete its files" do
527
+ [["434x66", :original],
528
+ ["300x46", :large],
529
+ ["100x15", :medium],
530
+ ["32x32", :thumb]].each do |geo, style|
531
+ cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"]
532
+ assert_equal geo, `#{cmd}`.chomp, cmd
533
+ end
534
+
535
+ @d2 = Dummy.find(@dummy.id)
536
+ @d2_files = s3_files_for @d2.avatar
537
+ [["434x66", :original],
538
+ ["300x46", :large],
539
+ ["100x15", :medium],
540
+ ["32x32", :thumb]].each do |geo, style|
541
+ cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"]
542
+ assert_equal geo, `#{cmd}`.chomp, cmd
543
+ end
544
+
545
+ @dummy.avatar.clear
546
+ assert_nil @dummy.avatar_file_name
547
+ assert @dummy.valid?
548
+ assert @dummy.save
549
+
550
+ [:thumb, :medium, :large, :original].each do |style|
551
+ assert ! @dummy.avatar.exists?(style)
552
+ end
553
+
554
+ @d2 = Dummy.find(@dummy.id)
555
+ assert_nil @d2.avatar_file_name
556
+ end
557
+
558
+ it "works exactly the same when new as when reloaded" do
559
+ @d2 = Dummy.find(@dummy.id)
560
+
561
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
562
+
563
+ [:thumb, :medium, :large, :original].each do |style|
564
+ begin
565
+ first_file = open(@dummy.avatar.url(style))
566
+ second_file = open(@dummy.avatar.url(style))
567
+ assert_equal first_file.read, second_file.read
568
+ ensure
569
+ first_file.close if first_file
570
+ second_file.close if second_file
571
+ end
572
+ end
573
+
574
+ @d2.avatar.clear
575
+ assert @d2.save
576
+
577
+ [:thumb, :medium, :large, :original].each do |style|
578
+ assert ! @dummy.avatar.exists?(style)
579
+ end
580
+ end
581
+
582
+ it "knows the difference between good files, bad files, and nil" do
583
+ @dummy.avatar = @bad_file
584
+ assert ! @dummy.valid?
585
+ @dummy.avatar = nil
586
+ assert @dummy.valid?
587
+
588
+ Dummy.validates_attachment_presence :avatar
589
+ @d2 = Dummy.find(@dummy.id)
590
+ @d2.avatar = @file
591
+ assert @d2.valid?
592
+ @d2.avatar = @bad_file
593
+ assert ! @d2.valid?
594
+ @d2.avatar = nil
595
+ assert ! @d2.valid?
596
+ end
597
+
598
+ it "is able to reload without saving and not have the file disappear" do
599
+ @dummy.avatar = @file
600
+ assert @dummy.save
601
+ @dummy.avatar = nil
602
+ assert_nil @dummy.avatar_file_name
603
+ @dummy.reload
604
+ assert_equal "5k.png", @dummy.avatar_file_name
605
+ end
606
+
607
+ it "has the right content type" do
608
+ headers = s3_headers_for(@dummy.avatar, :original)
609
+ assert_equal 'image/png', headers['content-type']
610
+ end
611
+
612
+ it "has the right style-specific headers" do
613
+ headers = s3_headers_for(@dummy.avatar, :custom)
614
+ assert_equal 'max-age=31557600', headers['cache-control']
615
+ end
616
+
617
+ it "has the right style-specific metadata" do
618
+ headers = s3_headers_for(@dummy.avatar, :custom)
619
+ assert_equal 'bar', headers['x-amz-meta-foo']
620
+ end
621
+
622
+ context "with non-english character in the file name" do
623
+ before do
624
+ @file.stubs(:original_filename).returns("クリップ.png")
625
+ @dummy.avatar = @file
626
+ end
627
+
628
+ it "does not raise any error" do
629
+ @dummy.save!
630
+ end
631
+ end
632
+ end
633
+ end
634
+
635
+ context "Copying attachments between models" do
636
+ before do
637
+ rebuild_model
638
+ @file = File.new(fixture_file("5k.png"), 'rb')
639
+ end
640
+
641
+ after { @file.close }
642
+
643
+ it "succeeds when original attachment is a file" do
644
+ original = Dummy.new
645
+ original.avatar = @file
646
+ assert original.save
647
+
648
+ copy = Dummy.new
649
+ copy.avatar = original.avatar
650
+ assert copy.save
651
+
652
+ assert copy.avatar.present?
653
+ end
654
+
655
+ it "succeeds when original attachment is empty" do
656
+ original = Dummy.create!
657
+
658
+ copy = Dummy.new
659
+ copy.avatar = @file
660
+ assert copy.save
661
+ assert copy.avatar.present?
662
+
663
+ copy.avatar = original.avatar
664
+ assert copy.save
665
+ assert !copy.avatar.present?
666
+ end
667
+ end
668
+ end