path-paperclip 2.3.3

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 (57) hide show
  1. data/LICENSE +26 -0
  2. data/README.rdoc +198 -0
  3. data/Rakefile +76 -0
  4. data/generators/paperclip/USAGE +5 -0
  5. data/generators/paperclip/paperclip_generator.rb +27 -0
  6. data/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  7. data/init.rb +1 -0
  8. data/lib/generators/paperclip/USAGE +8 -0
  9. data/lib/generators/paperclip/paperclip_generator.rb +31 -0
  10. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  11. data/lib/paperclip.rb +440 -0
  12. data/lib/paperclip/attachment.rb +401 -0
  13. data/lib/paperclip/callback_compatability.rb +61 -0
  14. data/lib/paperclip/geometry.rb +150 -0
  15. data/lib/paperclip/interpolations.rb +113 -0
  16. data/lib/paperclip/iostream.rb +59 -0
  17. data/lib/paperclip/matchers.rb +33 -0
  18. data/lib/paperclip/matchers/have_attached_file_matcher.rb +57 -0
  19. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +75 -0
  20. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +54 -0
  21. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +95 -0
  22. data/lib/paperclip/processor.rb +49 -0
  23. data/lib/paperclip/railtie.rb +20 -0
  24. data/lib/paperclip/storage.rb +258 -0
  25. data/lib/paperclip/style.rb +90 -0
  26. data/lib/paperclip/thumbnail.rb +78 -0
  27. data/lib/paperclip/upfile.rb +52 -0
  28. data/lib/paperclip/version.rb +3 -0
  29. data/lib/tasks/paperclip.rake +95 -0
  30. data/rails/init.rb +2 -0
  31. data/shoulda_macros/paperclip.rb +119 -0
  32. data/test/attachment_test.rb +796 -0
  33. data/test/database.yml +4 -0
  34. data/test/fixtures/12k.png +0 -0
  35. data/test/fixtures/50x50.png +0 -0
  36. data/test/fixtures/5k.png +0 -0
  37. data/test/fixtures/bad.png +1 -0
  38. data/test/fixtures/ceedub.gif +0 -0
  39. data/test/fixtures/s3.yml +8 -0
  40. data/test/fixtures/text.txt +1 -0
  41. data/test/fixtures/twopage.pdf +0 -0
  42. data/test/geometry_test.rb +177 -0
  43. data/test/helper.rb +152 -0
  44. data/test/integration_test.rb +610 -0
  45. data/test/interpolations_test.rb +135 -0
  46. data/test/iostream_test.rb +78 -0
  47. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  48. data/test/matchers/validate_attachment_content_type_matcher_test.rb +54 -0
  49. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  50. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  51. data/test/paperclip_test.rb +389 -0
  52. data/test/processor_test.rb +10 -0
  53. data/test/storage_test.rb +407 -0
  54. data/test/style_test.rb +141 -0
  55. data/test/thumbnail_test.rb +227 -0
  56. data/test/upfile_test.rb +36 -0
  57. metadata +221 -0
data/test/database.yml ADDED
@@ -0,0 +1,4 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: ":memory:"
4
+
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ DA∑¨øC‰ (corrupted image)
Binary file
@@ -0,0 +1,8 @@
1
+ development:
2
+ key: 54321
3
+ production:
4
+ key: 12345
5
+ test:
6
+ bucket: <%= ENV['S3_BUCKET'] %>
7
+ access_key_id: <%= ENV['S3_KEY'] %>
8
+ secret_access_key: <%= ENV['S3_SECRET'] %>
@@ -0,0 +1 @@
1
+ ASCII text.
Binary file
@@ -0,0 +1,177 @@
1
+ require 'test/helper'
2
+
3
+ class GeometryTest < Test::Unit::TestCase
4
+ context "Paperclip::Geometry" do
5
+ should "correctly report its given dimensions" do
6
+ assert @geo = Paperclip::Geometry.new(1024, 768)
7
+ assert_equal 1024, @geo.width
8
+ assert_equal 768, @geo.height
9
+ end
10
+
11
+ should "set height to 0 if height dimension is missing" do
12
+ assert @geo = Paperclip::Geometry.new(1024)
13
+ assert_equal 1024, @geo.width
14
+ assert_equal 0, @geo.height
15
+ end
16
+
17
+ should "set width to 0 if width dimension is missing" do
18
+ assert @geo = Paperclip::Geometry.new(nil, 768)
19
+ assert_equal 0, @geo.width
20
+ assert_equal 768, @geo.height
21
+ end
22
+
23
+ should "be generated from a WxH-formatted string" do
24
+ assert @geo = Paperclip::Geometry.parse("800x600")
25
+ assert_equal 800, @geo.width
26
+ assert_equal 600, @geo.height
27
+ end
28
+
29
+ should "be generated from a xH-formatted string" do
30
+ assert @geo = Paperclip::Geometry.parse("x600")
31
+ assert_equal 0, @geo.width
32
+ assert_equal 600, @geo.height
33
+ end
34
+
35
+ should "be generated from a Wx-formatted string" do
36
+ assert @geo = Paperclip::Geometry.parse("800x")
37
+ assert_equal 800, @geo.width
38
+ assert_equal 0, @geo.height
39
+ end
40
+
41
+ should "be generated from a W-formatted string" do
42
+ assert @geo = Paperclip::Geometry.parse("800")
43
+ assert_equal 800, @geo.width
44
+ assert_equal 0, @geo.height
45
+ end
46
+
47
+ should "ensure the modifier is nil if not present" do
48
+ assert @geo = Paperclip::Geometry.parse("123x456")
49
+ assert_nil @geo.modifier
50
+ end
51
+
52
+ should "treat x and X the same in geometries" do
53
+ @lower = Paperclip::Geometry.parse("123x456")
54
+ @upper = Paperclip::Geometry.parse("123X456")
55
+ assert_equal 123, @lower.width
56
+ assert_equal 123, @upper.width
57
+ assert_equal 456, @lower.height
58
+ assert_equal 456, @upper.height
59
+ end
60
+
61
+ ['>', '<', '#', '@', '%', '^', '!', nil].each do |mod|
62
+ should "ensure the modifier #{mod.inspect} is preserved" do
63
+ assert @geo = Paperclip::Geometry.parse("123x456#{mod}")
64
+ assert_equal mod, @geo.modifier
65
+ assert_equal "123x456#{mod}", @geo.to_s
66
+ end
67
+ end
68
+
69
+ ['>', '<', '#', '@', '%', '^', '!', nil].each do |mod|
70
+ should "ensure the modifier #{mod.inspect} is preserved with no height" do
71
+ assert @geo = Paperclip::Geometry.parse("123x#{mod}")
72
+ assert_equal mod, @geo.modifier
73
+ assert_equal "123#{mod}", @geo.to_s
74
+ end
75
+ end
76
+
77
+ should "make sure the modifier gets passed during transformation_to" do
78
+ assert @src = Paperclip::Geometry.parse("123x456")
79
+ assert @dst = Paperclip::Geometry.parse("123x456>")
80
+ assert_equal ["123x456>", nil], @src.transformation_to(@dst)
81
+ end
82
+
83
+ should "generate correct ImageMagick formatting string for W-formatted string" do
84
+ assert @geo = Paperclip::Geometry.parse("800")
85
+ assert_equal "800", @geo.to_s
86
+ end
87
+
88
+ should "generate correct ImageMagick formatting string for Wx-formatted string" do
89
+ assert @geo = Paperclip::Geometry.parse("800x")
90
+ assert_equal "800", @geo.to_s
91
+ end
92
+
93
+ should "generate correct ImageMagick formatting string for xH-formatted string" do
94
+ assert @geo = Paperclip::Geometry.parse("x600")
95
+ assert_equal "x600", @geo.to_s
96
+ end
97
+
98
+ should "generate correct ImageMagick formatting string for WxH-formatted string" do
99
+ assert @geo = Paperclip::Geometry.parse("800x600")
100
+ assert_equal "800x600", @geo.to_s
101
+ end
102
+
103
+ should "be generated from a file" do
104
+ file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
105
+ file = File.new(file, 'rb')
106
+ assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
107
+ assert @geo.height > 0
108
+ assert @geo.width > 0
109
+ end
110
+
111
+ should "be generated from a file path" do
112
+ file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
113
+ assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
114
+ assert @geo.height > 0
115
+ assert @geo.width > 0
116
+ end
117
+
118
+ should "not generate from a bad file" do
119
+ file = "/home/This File Does Not Exist.omg"
120
+ assert_raise(Paperclip::NotIdentifiedByImageMagickError){ @geo = Paperclip::Geometry.from_file(file) }
121
+ end
122
+
123
+ [['vertical', 900, 1440, true, false, false, 1440, 900, 0.625],
124
+ ['horizontal', 1024, 768, false, true, false, 1024, 768, 1.3333],
125
+ ['square', 100, 100, false, false, true, 100, 100, 1]].each do |args|
126
+ context "performing calculations on a #{args[0]} viewport" do
127
+ setup do
128
+ @geo = Paperclip::Geometry.new(args[1], args[2])
129
+ end
130
+
131
+ should "#{args[3] ? "" : "not"} be vertical" do
132
+ assert_equal args[3], @geo.vertical?
133
+ end
134
+
135
+ should "#{args[4] ? "" : "not"} be horizontal" do
136
+ assert_equal args[4], @geo.horizontal?
137
+ end
138
+
139
+ should "#{args[5] ? "" : "not"} be square" do
140
+ assert_equal args[5], @geo.square?
141
+ end
142
+
143
+ should "report that #{args[6]} is the larger dimension" do
144
+ assert_equal args[6], @geo.larger
145
+ end
146
+
147
+ should "report that #{args[7]} is the smaller dimension" do
148
+ assert_equal args[7], @geo.smaller
149
+ end
150
+
151
+ should "have an aspect ratio of #{args[8]}" do
152
+ assert_in_delta args[8], @geo.aspect, 0.0001
153
+ end
154
+ end
155
+ end
156
+
157
+ [[ [1000, 100], [64, 64], "x64", "64x64+288+0" ],
158
+ [ [100, 1000], [50, 950], "x950", "50x950+22+0" ],
159
+ [ [100, 1000], [50, 25], "50x", "50x25+0+237" ]]. each do |args|
160
+ context "of #{args[0].inspect} and given a Geometry #{args[1].inspect} and sent transform_to" do
161
+ setup do
162
+ @geo = Paperclip::Geometry.new(*args[0])
163
+ @dst = Paperclip::Geometry.new(*args[1])
164
+ @scale, @crop = @geo.transformation_to @dst, true
165
+ end
166
+
167
+ should "be able to return the correct scaling transformation geometry #{args[2]}" do
168
+ assert_equal args[2], @scale
169
+ end
170
+
171
+ should "be able to return the correct crop transformation geometry #{args[3]}" do
172
+ assert_equal args[3], @crop
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,152 @@
1
+ require 'rubygems'
2
+ require 'tempfile'
3
+ require 'test/unit'
4
+
5
+ require 'shoulda'
6
+ require 'mocha'
7
+
8
+ case ENV['RAILS_VERSION']
9
+ when '2.1' then
10
+ gem 'activerecord', '~>2.1.0'
11
+ gem 'activesupport', '~>2.1.0'
12
+ gem 'actionpack', '~>2.1.0'
13
+ when '3.0' then
14
+ gem 'activerecord', '~>3.0.0'
15
+ gem 'activesupport', '~>3.0.0'
16
+ gem 'actionpack', '~>3.0.0'
17
+ else
18
+ gem 'activerecord', '~>2.3.0'
19
+ gem 'activesupport', '~>2.3.0'
20
+ gem 'actionpack', '~>2.3.0'
21
+ end
22
+
23
+ require 'active_record'
24
+ require 'active_record/version'
25
+ require 'active_support'
26
+ require 'action_pack'
27
+
28
+ puts "Testing against version #{ActiveRecord::VERSION::STRING}"
29
+
30
+ begin
31
+ require 'ruby-debug'
32
+ rescue LoadError => e
33
+ puts "debugger disabled"
34
+ end
35
+
36
+ ROOT = File.join(File.dirname(__FILE__), '..')
37
+
38
+ def silence_warnings
39
+ old_verbose, $VERBOSE = $VERBOSE, nil
40
+ yield
41
+ ensure
42
+ $VERBOSE = old_verbose
43
+ end
44
+
45
+ class Test::Unit::TestCase
46
+ def setup
47
+ silence_warnings do
48
+ Object.const_set(:Rails, stub('Rails', :root => ROOT, :env => 'test'))
49
+ end
50
+ end
51
+ end
52
+
53
+ $LOAD_PATH << File.join(ROOT, 'lib')
54
+ $LOAD_PATH << File.join(ROOT, 'lib', 'paperclip')
55
+
56
+ require File.join(ROOT, 'lib', 'paperclip.rb')
57
+
58
+ require 'shoulda_macros/paperclip'
59
+
60
+ FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
61
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
62
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
63
+ ActiveRecord::Base.establish_connection(config['test'])
64
+
65
+ def reset_class class_name
66
+ ActiveRecord::Base.send(:include, Paperclip)
67
+ Object.send(:remove_const, class_name) rescue nil
68
+ klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
69
+ klass.class_eval{ include Paperclip }
70
+ klass
71
+ end
72
+
73
+ def reset_table table_name, &block
74
+ block ||= lambda { |table| true }
75
+ ActiveRecord::Base.connection.create_table :dummies, {:force => true}, &block
76
+ end
77
+
78
+ def modify_table table_name, &block
79
+ ActiveRecord::Base.connection.change_table :dummies, &block
80
+ end
81
+
82
+ def rebuild_model options = {}
83
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
84
+ table.column :other, :string
85
+ table.column :avatar_file_name, :string
86
+ table.column :avatar_content_type, :string
87
+ table.column :avatar_file_size, :integer
88
+ table.column :avatar_updated_at, :datetime
89
+ if options.delete(:with_dimensions)
90
+ table.column :avatar_width, :integer
91
+ table.column :avatar_height, :integer
92
+ end
93
+ end
94
+ rebuild_class options
95
+ end
96
+
97
+ def rebuild_class options = {}
98
+ ActiveRecord::Base.send(:include, Paperclip)
99
+ Object.send(:remove_const, "Dummy") rescue nil
100
+ Object.const_set("Dummy", Class.new(ActiveRecord::Base))
101
+ Dummy.class_eval do
102
+ include Paperclip
103
+ has_attached_file :avatar, options
104
+ end
105
+ end
106
+
107
+ class FakeModel
108
+ attr_accessor :avatar_file_name,
109
+ :avatar_file_size,
110
+ :avatar_last_updated,
111
+ :avatar_content_type,
112
+ :id
113
+
114
+ def errors
115
+ @errors ||= []
116
+ end
117
+
118
+ def run_paperclip_callbacks name, *args
119
+ end
120
+
121
+ end
122
+
123
+ def attachment options
124
+ Paperclip::Attachment.new(:avatar, FakeModel.new, options)
125
+ end
126
+
127
+ def silence_warnings
128
+ old_verbose, $VERBOSE = $VERBOSE, nil
129
+ yield
130
+ ensure
131
+ $VERBOSE = old_verbose
132
+ end
133
+
134
+ def should_accept_dummy_class
135
+ should "accept the class" do
136
+ assert_accepts @matcher, @dummy_class
137
+ end
138
+
139
+ should "accept an instance of that class" do
140
+ assert_accepts @matcher, @dummy_class.new
141
+ end
142
+ end
143
+
144
+ def should_reject_dummy_class
145
+ should "reject the class" do
146
+ assert_rejects @matcher, @dummy_class
147
+ end
148
+
149
+ should "reject an instance of that class" do
150
+ assert_rejects @matcher, @dummy_class.new
151
+ end
152
+ end
@@ -0,0 +1,610 @@
1
+ require 'test/helper'
2
+
3
+ class IntegrationTest < Test::Unit::TestCase
4
+ context "Many models at once" do
5
+ setup do
6
+ rebuild_model
7
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
8
+ 300.times do |i|
9
+ Dummy.create! :avatar => @file
10
+ end
11
+ end
12
+
13
+ should "not exceed the open file limit" do
14
+ assert_nothing_raised do
15
+ dummies = Dummy.find(:all)
16
+ dummies.each { |dummy| dummy.avatar }
17
+ end
18
+ end
19
+ end
20
+
21
+ context "An attachment" do
22
+ setup do
23
+ rebuild_model :styles => { :thumb => "50x50#" }
24
+ @dummy = Dummy.new
25
+ @file = File.new(File.join(File.dirname(__FILE__),
26
+ "fixtures",
27
+ "5k.png"), 'rb')
28
+ @dummy.avatar = @file
29
+ assert @dummy.save
30
+ end
31
+
32
+ teardown { @file.close }
33
+
34
+ should "create its thumbnails properly" do
35
+ assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
36
+ end
37
+
38
+ context "redefining its attachment styles" do
39
+ setup do
40
+ Dummy.class_eval do
41
+ has_attached_file :avatar, :styles => { :thumb => "150x25#" }
42
+ has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } }
43
+ end
44
+ @d2 = Dummy.find(@dummy.id)
45
+ @d2.avatar.reprocess!
46
+ @d2.save
47
+ end
48
+
49
+ should "create its thumbnails properly" do
50
+ assert_match /\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
51
+ assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`
52
+ end
53
+ end
54
+ end
55
+
56
+ context "A model that modifies its original" do
57
+ setup do
58
+ rebuild_model :styles => { :original => "2x2#" }
59
+ @dummy = Dummy.new
60
+ @file = File.new(File.join(File.dirname(__FILE__),
61
+ "fixtures",
62
+ "5k.png"), 'rb')
63
+ @dummy.avatar = @file
64
+ end
65
+
66
+ should "report the file size of the processed file and not the original" do
67
+ assert_not_equal @file.size, @dummy.avatar.size
68
+ end
69
+
70
+ teardown { @file.close }
71
+ end
72
+
73
+ context "A model with attachments scoped under an id" do
74
+ setup do
75
+ rebuild_model :styles => { :large => "100x100",
76
+ :medium => "50x50" },
77
+ :path => ":rails_root/tmp/:id/:attachments/:style.:extension"
78
+ @dummy = Dummy.new
79
+ @file = File.new(File.join(File.dirname(__FILE__),
80
+ "fixtures",
81
+ "5k.png"), 'rb')
82
+ @dummy.avatar = @file
83
+ end
84
+
85
+ teardown { @file.close }
86
+
87
+ context "when saved" do
88
+ setup do
89
+ @dummy.save
90
+ @saved_path = @dummy.avatar.path(:large)
91
+ end
92
+
93
+ should "have a large file in the right place" do
94
+ assert File.exists?(@dummy.avatar.path(:large))
95
+ end
96
+
97
+ context "and deleted" do
98
+ setup do
99
+ @dummy.avatar.clear
100
+ @dummy.save
101
+ end
102
+
103
+ should "not have a large file in the right place anymore" do
104
+ assert ! File.exists?(@saved_path)
105
+ end
106
+
107
+ should "not have its next two parent directories" do
108
+ assert ! File.exists?(File.dirname(@saved_path))
109
+ assert ! File.exists?(File.dirname(File.dirname(@saved_path)))
110
+ end
111
+
112
+ before_should "not die if an unexpected SystemCallError happens" do
113
+ FileUtils.stubs(:rmdir).raises(Errno::EPIPE)
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ context "A model with no attachment validation" do
120
+ setup do
121
+ rebuild_model :styles => { :large => "300x300>",
122
+ :medium => "100x100",
123
+ :thumb => ["32x32#", :gif] },
124
+ :default_style => :medium,
125
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
126
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
127
+ @dummy = Dummy.new
128
+ end
129
+
130
+ should "have its definition return false when asked about whiny_thumbnails" do
131
+ assert ! Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
132
+ end
133
+
134
+ context "when validates_attachment_thumbnails is called" do
135
+ setup do
136
+ Dummy.validates_attachment_thumbnails :avatar
137
+ end
138
+
139
+ should "have its definition return true when asked about whiny_thumbnails" do
140
+ assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
141
+ end
142
+ end
143
+
144
+ context "redefined to have attachment validations" do
145
+ setup do
146
+ rebuild_model :styles => { :large => "300x300>",
147
+ :medium => "100x100",
148
+ :thumb => ["32x32#", :gif] },
149
+ :whiny_thumbnails => true,
150
+ :default_style => :medium,
151
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
152
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
153
+ end
154
+
155
+ should "have its definition return true when asked about whiny_thumbnails" do
156
+ assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
157
+ end
158
+ end
159
+ end
160
+
161
+ context "A model with no convert_options setting" do
162
+ setup do
163
+ rebuild_model :styles => { :large => "300x300>",
164
+ :medium => "100x100",
165
+ :thumb => ["32x32#", :gif] },
166
+ :default_style => :medium,
167
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
168
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
169
+ @dummy = Dummy.new
170
+ end
171
+
172
+ should "have its definition return nil when asked about convert_options" do
173
+ assert ! Dummy.attachment_definitions[:avatar][:convert_options]
174
+ end
175
+
176
+ context "redefined to have convert_options setting" do
177
+ setup do
178
+ rebuild_model :styles => { :large => "300x300>",
179
+ :medium => "100x100",
180
+ :thumb => ["32x32#", :gif] },
181
+ :convert_options => "-strip -depth 8",
182
+ :default_style => :medium,
183
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
184
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
185
+ end
186
+
187
+ should "have its definition return convert_options value when asked about convert_options" do
188
+ assert_equal "-strip -depth 8", Dummy.attachment_definitions[:avatar][:convert_options]
189
+ end
190
+ end
191
+ end
192
+
193
+ context "A model with no dimension columns" do
194
+ setup do
195
+ rebuild_model
196
+ @dummy = Dummy.new
197
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"))
198
+ end
199
+
200
+ should "not break on image uploads" do
201
+ assert_nothing_raised do
202
+ assert @dummy.avatar = @file
203
+ assert @dummy.save
204
+ end
205
+ end
206
+
207
+ should "return nil when asked for the width/height" do
208
+ @dummy.avatar = @file
209
+ @dummy.save
210
+ assert_nil @dummy.avatar.width
211
+ assert_nil @dummy.avatar.height
212
+ end
213
+ end
214
+
215
+ context "A model with dimension columns" do
216
+ setup do
217
+ rebuild_model :with_dimensions => true
218
+ @dummy = Dummy.new
219
+ @image_file = File.new(File.join(FIXTURES_DIR, "50x50.png"))
220
+ @image_file2 = File.new(File.join(FIXTURES_DIR, "5k.png"))
221
+ @text_file = File.new(File.join(FIXTURES_DIR, "text.txt"))
222
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"))
223
+ end
224
+
225
+ should "return nil when asked for the width/height of a non-image upload" do
226
+ @dummy.avatar = @text_file
227
+ @dummy.save
228
+ assert_nil @dummy.avatar.width
229
+ assert_nil @dummy.avatar.height
230
+ end
231
+
232
+ should "assign dimensions for image uploads" do
233
+ assert @dummy.avatar = @image_file
234
+ assert @dummy.save
235
+ assert_equal 50, @dummy.avatar.width
236
+ assert_equal 50, @dummy.avatar.height
237
+ end
238
+
239
+ should "not break when a bad image is uploaded" do
240
+ assert_nothing_raised do
241
+ @dummy.avatar = @bad_file
242
+ @dummy.save
243
+ end
244
+ end
245
+
246
+ should "return nil width/height when a bad image is uploaded" do
247
+ assert_nothing_raised do
248
+ @dummy.avatar = @bad_file
249
+ @dummy.save
250
+ end
251
+ assert_nil @dummy.avatar.width
252
+ assert_nil @dummy.avatar.height
253
+ end
254
+
255
+ should "change dimensions if changing image upload" do
256
+ @dummy.avatar = @image_file
257
+ @dummy.save
258
+ old_size = `identify -format "%wx%h" #{@dummy.avatar.to_file(:original).path}`.chomp
259
+ assert_equal old_size, "#{@dummy.avatar.width}x#{@dummy.avatar.height}"
260
+ @dummy.avatar = @image_file2
261
+ @dummy.save
262
+ new_size = `identify -format "%wx%h" #{@dummy.avatar.to_file(:original).path}`.chomp
263
+ assert_equal new_size, "#{@dummy.avatar.width}x#{@dummy.avatar.height}"
264
+ assert_not_equal old_size, new_size # sanity check
265
+ end
266
+
267
+ should "unassign dimensions if changing image upload to non-image" do
268
+ @dummy.avatar = @image_file
269
+ @dummy.save
270
+ @dummy.avatar = @text_file
271
+ @dummy.save
272
+ assert_nil @dummy.avatar.width
273
+ assert_nil @dummy.avatar.height
274
+ end
275
+ end
276
+
277
+ context "A model with dimension columns and custom sizes" do
278
+ setup do
279
+ rebuild_model :with_dimensions => true,
280
+ :styles => { :large => "40x30>",
281
+ :medium => "20x20",
282
+ :thumb => ["5x5#", :gif] },
283
+ :default_style => :medium
284
+
285
+ @dummy = Dummy.new
286
+ @file = File.new(File.join(FIXTURES_DIR, "50x50.png"))
287
+ end
288
+
289
+ should "return the default style dimensions" do
290
+ @dummy.avatar = @file
291
+ @dummy.save!
292
+ assert_equal 20, @dummy.avatar.width
293
+ assert_equal 20, @dummy.avatar.height
294
+ end
295
+
296
+ should "return other style dimensions when asked" do
297
+ @dummy.avatar = @file
298
+ @dummy.save!
299
+
300
+ assert_equal 5, @dummy.avatar.width(:thumb)
301
+ assert_equal 5, @dummy.avatar.height(:thumb)
302
+
303
+ assert_equal 30, @dummy.avatar.width(:large)
304
+ assert_equal 30, @dummy.avatar.height(:large)
305
+ end
306
+ end
307
+
308
+ context "A model with a filesystem attachment" do
309
+ setup do
310
+ rebuild_model :styles => { :large => "300x300>",
311
+ :medium => "100x100",
312
+ :thumb => ["32x32#", :gif] },
313
+ :whiny_thumbnails => true,
314
+ :default_style => :medium,
315
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
316
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
317
+ @dummy = Dummy.new
318
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
319
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
320
+ @text_file = File.new(File.join(FIXTURES_DIR, "text.txt"), 'rb')
321
+
322
+ assert @dummy.avatar = @file
323
+ assert @dummy.valid?
324
+ assert @dummy.save
325
+ end
326
+
327
+ should "write and delete its files" do
328
+ [["434x66", :original],
329
+ ["300x46", :large],
330
+ ["100x15", :medium],
331
+ ["32x32", :thumb]].each do |geo, style|
332
+ cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"]
333
+ assert_equal geo, `#{cmd}`.chomp, cmd
334
+ end
335
+
336
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
337
+
338
+ @d2 = Dummy.find(@dummy.id)
339
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp
340
+ assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp
341
+ assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp
342
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
343
+ assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
344
+
345
+ @dummy.avatar = "not a valid file but not nil"
346
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
347
+ assert @dummy.valid?
348
+ assert @dummy.save
349
+
350
+ saved_paths.each do |p|
351
+ assert File.exists?(p)
352
+ end
353
+
354
+ @dummy.avatar.clear
355
+ assert_nil @dummy.avatar_file_name
356
+ assert @dummy.valid?
357
+ assert @dummy.save
358
+
359
+ saved_paths.each do |p|
360
+ assert ! File.exists?(p)
361
+ end
362
+
363
+ @d2 = Dummy.find(@dummy.id)
364
+ assert_nil @d2.avatar_file_name
365
+ end
366
+
367
+ should "work exactly the same when new as when reloaded" do
368
+ @d2 = Dummy.find(@dummy.id)
369
+
370
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
371
+ [:thumb, :medium, :large, :original].each do |style|
372
+ assert_equal @dummy.avatar.path(style), @d2.avatar.path(style)
373
+ end
374
+
375
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
376
+
377
+ @d2.avatar.clear
378
+ assert @d2.save
379
+
380
+ saved_paths.each do |p|
381
+ assert ! File.exists?(p)
382
+ end
383
+ end
384
+
385
+ should "know the difference between good files, bad files, and not files" do
386
+ expected = @dummy.avatar.to_file
387
+ @dummy.avatar = "not a file"
388
+ assert @dummy.valid?
389
+ assert_equal expected.path, @dummy.avatar.path
390
+ expected.close
391
+
392
+ @dummy.avatar = @bad_file
393
+ assert ! @dummy.valid?
394
+ end
395
+
396
+ should "properly determine #image?" do
397
+ @dummy.avatar = @file
398
+ assert_equal true, @dummy.avatar.image?
399
+
400
+ @dummy.avatar = @text_file
401
+ assert_equal false, @dummy.avatar.image?
402
+
403
+ @dummy.avatar = "not a file"
404
+ assert_equal false, @dummy.avatar.image?
405
+ end
406
+
407
+ should "know the difference between good files, bad files, and not files when validating" do
408
+ Dummy.validates_attachment_presence :avatar
409
+ @d2 = Dummy.find(@dummy.id)
410
+ @d2.avatar = @file
411
+ assert @d2.valid?, @d2.errors.full_messages.inspect
412
+ @d2.avatar = @bad_file
413
+ assert ! @d2.valid?
414
+ end
415
+
416
+ should "be able to reload without saving and not have the file disappear" do
417
+ @dummy.avatar = @file
418
+ assert @dummy.save
419
+ @dummy.avatar.clear
420
+ assert_nil @dummy.avatar_file_name
421
+ @dummy.reload
422
+ assert_equal "5k.png", @dummy.avatar_file_name
423
+ end
424
+
425
+ context "that is assigned its file from another Paperclip attachment" do
426
+ setup do
427
+ @dummy2 = Dummy.new
428
+ @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"), 'rb')
429
+ assert @dummy2.avatar = @file2
430
+ @dummy2.save
431
+ end
432
+
433
+ should "work when assigned a file" do
434
+ assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
435
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
436
+
437
+ assert @dummy.avatar = @dummy2.avatar
438
+ @dummy.save
439
+ assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
440
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
441
+ end
442
+ end
443
+
444
+ end
445
+
446
+ context "A model with an attachments association and a Paperclip attachment" do
447
+ setup do
448
+ Dummy.class_eval do
449
+ has_many :attachments, :class_name => 'Dummy'
450
+ end
451
+
452
+ @dummy = Dummy.new
453
+ @dummy.avatar = File.new(File.join(File.dirname(__FILE__),
454
+ "fixtures",
455
+ "5k.png"), 'rb')
456
+ end
457
+
458
+ should "should not error when saving" do
459
+ assert_nothing_raised do
460
+ @dummy.save!
461
+ end
462
+ end
463
+ end
464
+
465
+ if ENV['S3_TEST_BUCKET']
466
+ def s3_files_for attachment
467
+ [:thumb, :medium, :large, :original].inject({}) do |files, style|
468
+ data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
469
+ t = Tempfile.new("paperclip-test")
470
+ t.binmode
471
+ t.write(data)
472
+ t.rewind
473
+ files[style] = t
474
+ files
475
+ end
476
+ end
477
+
478
+ def s3_headers_for attachment, style
479
+ `curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head|
480
+ split_head = head.chomp.split(/\s*:\s*/, 2)
481
+ h[split_head.first.downcase] = split_head.last unless split_head.empty?
482
+ h
483
+ end
484
+ end
485
+
486
+ context "A model with an S3 attachment" do
487
+ setup do
488
+ rebuild_model :styles => { :large => "300x300>",
489
+ :medium => "100x100",
490
+ :thumb => ["32x32#", :gif] },
491
+ :storage => :s3,
492
+ :whiny_thumbnails => true,
493
+ # :s3_options => {:logger => Logger.new(StringIO.new)},
494
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")),
495
+ :default_style => :medium,
496
+ :bucket => ENV['S3_TEST_BUCKET'],
497
+ :path => ":class/:attachment/:id/:style/:basename.:extension"
498
+ @dummy = Dummy.new
499
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
500
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
501
+
502
+ assert @dummy.avatar = @file
503
+ assert @dummy.valid?
504
+ assert @dummy.save
505
+
506
+ @files_on_s3 = s3_files_for @dummy.avatar
507
+ end
508
+
509
+ should "have the same contents as the original" do
510
+ @file.rewind
511
+ assert_equal @file.read, @files_on_s3[:original].read
512
+ end
513
+
514
+ should "write and delete its files" do
515
+ [["434x66", :original],
516
+ ["300x46", :large],
517
+ ["100x15", :medium],
518
+ ["32x32", :thumb]].each do |geo, style|
519
+ cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"]
520
+ assert_equal geo, `#{cmd}`.chomp, cmd
521
+ end
522
+
523
+ @d2 = Dummy.find(@dummy.id)
524
+ @d2_files = s3_files_for @d2.avatar
525
+ [["434x66", :original],
526
+ ["300x46", :large],
527
+ ["100x15", :medium],
528
+ ["32x32", :thumb]].each do |geo, style|
529
+ cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"]
530
+ assert_equal geo, `#{cmd}`.chomp, cmd
531
+ end
532
+
533
+ @dummy.avatar = "not a valid file but not nil"
534
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
535
+ assert @dummy.valid?
536
+ assert @dummy.save
537
+
538
+ [:thumb, :medium, :large, :original].each do |style|
539
+ assert @dummy.avatar.exists?(style)
540
+ end
541
+
542
+ @dummy.avatar.clear
543
+ assert_nil @dummy.avatar_file_name
544
+ assert @dummy.valid?
545
+ assert @dummy.save
546
+
547
+ [:thumb, :medium, :large, :original].each do |style|
548
+ assert ! @dummy.avatar.exists?(style)
549
+ end
550
+
551
+ @d2 = Dummy.find(@dummy.id)
552
+ assert_nil @d2.avatar_file_name
553
+ end
554
+
555
+ should "work exactly the same when new as when reloaded" do
556
+ @d2 = Dummy.find(@dummy.id)
557
+
558
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
559
+ [:thumb, :medium, :large, :original].each do |style|
560
+ assert_equal @dummy.avatar.to_file(style).read, @d2.avatar.to_file(style).read
561
+ end
562
+
563
+ saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
564
+
565
+ @d2.avatar.clear
566
+ assert @d2.save
567
+
568
+ [:thumb, :medium, :large, :original].each do |style|
569
+ assert ! @dummy.avatar.exists?(style)
570
+ end
571
+ end
572
+
573
+ should "know the difference between good files, bad files, not files, and nil" do
574
+ expected = @dummy.avatar.to_file
575
+ @dummy.avatar = "not a file"
576
+ assert @dummy.valid?
577
+ assert_equal expected.read, @dummy.avatar.to_file.read
578
+
579
+ @dummy.avatar = @bad_file
580
+ assert ! @dummy.valid?
581
+ @dummy.avatar = nil
582
+ assert @dummy.valid?
583
+
584
+ Dummy.validates_attachment_presence :avatar
585
+ @d2 = Dummy.find(@dummy.id)
586
+ @d2.avatar = @file
587
+ assert @d2.valid?
588
+ @d2.avatar = @bad_file
589
+ assert ! @d2.valid?
590
+ @d2.avatar = nil
591
+ assert ! @d2.valid?
592
+ end
593
+
594
+ should "be able to reload without saving and not have the file disappear" do
595
+ @dummy.avatar = @file
596
+ assert @dummy.save
597
+ @dummy.avatar = nil
598
+ assert_nil @dummy.avatar_file_name
599
+ @dummy.reload
600
+ assert_equal "5k.png", @dummy.avatar_file_name
601
+ end
602
+
603
+ should "have the right content type" do
604
+ headers = s3_headers_for(@dummy.avatar, :original)
605
+ assert_equal 'image/png', headers['content-type']
606
+ end
607
+ end
608
+ end
609
+ end
610
+