paperclip 2.3.8 → 2.3.9

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of paperclip might be problematic. Click here for more details.

Files changed (59) hide show
  1. data/{README.rdoc → README.md} +104 -54
  2. data/lib/paperclip.rb +2 -0
  3. data/lib/paperclip/attachment.rb +67 -38
  4. data/lib/paperclip/command_line.rb +7 -1
  5. data/lib/paperclip/interpolations.rb +17 -1
  6. data/lib/paperclip/storage.rb +1 -0
  7. data/lib/paperclip/storage/fog.rb +98 -0
  8. data/lib/paperclip/storage/s3.rb +5 -5
  9. data/lib/paperclip/version.rb +1 -1
  10. data/test/attachment_test.rb +117 -0
  11. data/test/command_line_test.rb +5 -0
  12. data/test/fixtures/uppercase.PNG +0 -0
  13. data/test/fog_test.rb +107 -0
  14. data/test/helper.rb +1 -1
  15. data/test/integration_test.rb +88 -0
  16. data/test/interpolations_test.rb +17 -1
  17. data/test/paperclip_test.rb +30 -0
  18. data/test/storage_test.rb +23 -0
  19. metadata +47 -80
  20. data/lib/paperclip.rbc +0 -5202
  21. data/lib/paperclip/attachment.rbc +0 -6393
  22. data/lib/paperclip/callback_compatability.rbc +0 -1649
  23. data/lib/paperclip/command_line.rbc +0 -1952
  24. data/lib/paperclip/geometry.rbc +0 -2141
  25. data/lib/paperclip/glue.rbc +0 -685
  26. data/lib/paperclip/interpolations.rbc +0 -2205
  27. data/lib/paperclip/iostream.rbc +0 -905
  28. data/lib/paperclip/matchers.rbc +0 -267
  29. data/lib/paperclip/matchers/have_attached_file_matcher.rbc +0 -1258
  30. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rbc +0 -1660
  31. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rbc +0 -1304
  32. data/lib/paperclip/matchers/validate_attachment_size_matcher.rbc +0 -2160
  33. data/lib/paperclip/processor.rbc +0 -911
  34. data/lib/paperclip/railtie.rbc +0 -680
  35. data/lib/paperclip/storage.rbc +0 -69
  36. data/lib/paperclip/storage/filesystem.rbc +0 -1402
  37. data/lib/paperclip/storage/s3.rbc +0 -3489
  38. data/lib/paperclip/style.rbc +0 -1571
  39. data/lib/paperclip/thumbnail.rbc +0 -1648
  40. data/lib/paperclip/upfile.rbc +0 -1619
  41. data/lib/paperclip/version.rbc +0 -176
  42. data/shoulda_macros/paperclip.rbc +0 -2415
  43. data/test/attachment_test.rbc +0 -22952
  44. data/test/command_line_test.rbc +0 -4307
  45. data/test/geometry_test.rbc +0 -5181
  46. data/test/helper.rbc +0 -3971
  47. data/test/integration_test.rbc +0 -13137
  48. data/test/interpolations_test.rbc +0 -3432
  49. data/test/iostream_test.rbc +0 -1889
  50. data/test/matchers/have_attached_file_matcher_test.rbc +0 -622
  51. data/test/matchers/validate_attachment_content_type_matcher_test.rbc +0 -1174
  52. data/test/matchers/validate_attachment_presence_matcher_test.rbc +0 -622
  53. data/test/matchers/validate_attachment_size_matcher_test.rbc +0 -1658
  54. data/test/paperclip_test.rbc +0 -7407
  55. data/test/processor_test.rbc +0 -314
  56. data/test/storage_test.rbc +0 -10294
  57. data/test/style_test.rbc +0 -3752
  58. data/test/thumbnail_test.rbc +0 -6803
  59. data/test/upfile_test.rbc +0 -1635
@@ -52,7 +52,7 @@ module Paperclip
52
52
  raise PaperclipCommandLineError,
53
53
  "Interpolation of #{key} isn't allowed."
54
54
  end
55
- shell_quote(vars[key.to_sym])
55
+ interpolation(vars, key) || match
56
56
  end
57
57
  end
58
58
 
@@ -60,6 +60,12 @@ module Paperclip
60
60
  %w(expected_outcodes swallow_stderr)
61
61
  end
62
62
 
63
+ def interpolation(vars, key)
64
+ if vars.key?(key.to_sym)
65
+ shell_quote(vars[key.to_sym])
66
+ end
67
+ end
68
+
63
69
  def shell_quote(string)
64
70
  return "" if string.nil? or string.blank?
65
71
  if self.class.unix?
@@ -48,8 +48,18 @@ module Paperclip
48
48
  end
49
49
 
50
50
  # Returns the timestamp as defined by the <attachment>_updated_at field
51
+ # in the server default time zone unless :use_global_time_zone is set
52
+ # to false. Note that a Rails.config.time_zone change will still
53
+ # invalidate any path or URL that uses :timestamp. For a
54
+ # time_zone-agnostic timestamp, use #updated_at.
51
55
  def timestamp attachment, style_name
52
- attachment.instance_read(:updated_at).to_s
56
+ attachment.instance_read(:updated_at).in_time_zone(attachment.time_zone).to_s
57
+ end
58
+
59
+ # Returns an integer timestamp that is time zone-neutral, so that paths
60
+ # remain valid even if a server's time zone changes.
61
+ def updated_at attachment, style_name
62
+ attachment.updated_at
53
63
  end
54
64
 
55
65
  # Returns the Rails.root constant.
@@ -94,6 +104,12 @@ module Paperclip
94
104
  attachment.fingerprint
95
105
  end
96
106
 
107
+ # Returns a the attachment hash. See Paperclip::Attachment#hash for
108
+ # more details.
109
+ def hash attachment, style_name
110
+ attachment.hash(style_name)
111
+ end
112
+
97
113
  # Returns the id of the instance in a split path form. e.g. returns
98
114
  # 000/001/234 for an id of 1234.
99
115
  def id_partition attachment, style_name
@@ -1,2 +1,3 @@
1
1
  require "paperclip/storage/filesystem"
2
+ require "paperclip/storage/fog"
2
3
  require "paperclip/storage/s3"
@@ -0,0 +1,98 @@
1
+ module Paperclip
2
+ module Storage
3
+
4
+ module Fog
5
+ def self.extended base
6
+ begin
7
+ require 'fog'
8
+ rescue LoadError => e
9
+ e.message << " (You may need to install the fog gem)"
10
+ raise e
11
+ end unless defined?(Fog)
12
+
13
+ base.instance_eval do
14
+ @fog_directory = @options[:fog_directory]
15
+ @fog_credentials = @options[:fog_credentials]
16
+ @fog_host = @options[:fog_host]
17
+ @fog_public = @options[:fog_public]
18
+
19
+ @url = ':fog_public_url'
20
+ Paperclip.interpolates(:fog_public_url) do |attachment, style|
21
+ attachment.public_url(style)
22
+ end unless Paperclip::Interpolations.respond_to? :fog_public_url
23
+ end
24
+ end
25
+
26
+ def exists?(style = default_style)
27
+ if original_filename
28
+ !!directory.files.head(path(style))
29
+ else
30
+ false
31
+ end
32
+ end
33
+
34
+ def flush_writes
35
+ for style, file in @queued_for_write do
36
+ log("saving #{path(style)}")
37
+ directory.files.create(
38
+ :body => file,
39
+ :key => path(style),
40
+ :public => @fog_public
41
+ )
42
+ end
43
+ @queued_for_write = {}
44
+ end
45
+
46
+ def flush_deletes
47
+ for path in @queued_for_delete do
48
+ log("deleting #{path}")
49
+ directory.files.new(:key => path).destroy
50
+ end
51
+ @queued_for_delete = []
52
+ end
53
+
54
+ # Returns representation of the data of the file assigned to the given
55
+ # style, in the format most representative of the current storage.
56
+ def to_file(style = default_style)
57
+ if @queued_for_write[style]
58
+ @queued_for_write[style]
59
+ else
60
+ body = directory.files.get(path(style)).body
61
+ filename = path(style)
62
+ extname = File.extname(filename)
63
+ basename = File.basename(filename, extname)
64
+ file = Tempfile.new([basename, extname])
65
+ file.binmode
66
+ file.write(body)
67
+ file.rewind
68
+ file
69
+ end
70
+ end
71
+
72
+ def public_url(style = default_style)
73
+ if @fog_host
74
+ "#{@fog_host}/#{path(style)}"
75
+ else
76
+ directory.files.new(:key => path(style)).public_url
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def connection
83
+ @connection ||= ::Fog::Storage.new(@fog_credentials)
84
+ end
85
+
86
+ def directory
87
+ @directory ||= begin
88
+ connection.directories.get(@fog_directory) || connection.directories.create(
89
+ :key => @fog_directory,
90
+ :public => @fog_public
91
+ )
92
+ end
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+ end
@@ -25,7 +25,7 @@ module Paperclip
25
25
  # development versus production.
26
26
  # * +s3_permissions+: This is a String that should be one of the "canned" access
27
27
  # policies that S3 provides (more information can be found here:
28
- # http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html#RESTCannedAccessPolicies)
28
+ # http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAccessPolicy.html)
29
29
  # The default for Paperclip is :public_read.
30
30
  # * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
31
31
  # 'http' or 'https'. Defaults to 'http' when your :s3_permissions are :public_read (the
@@ -63,7 +63,7 @@ module Paperclip
63
63
  rescue LoadError => e
64
64
  e.message << " (You may need to install the aws-s3 gem)"
65
65
  raise e
66
- end
66
+ end unless defined?(AWS::S3)
67
67
 
68
68
  base.instance_eval do
69
69
  @s3_credentials = parse_credentials(@options[:s3_credentials])
@@ -85,13 +85,13 @@ module Paperclip
85
85
  end
86
86
  Paperclip.interpolates(:s3_alias_url) do |attachment, style|
87
87
  "#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
88
- end
88
+ end unless Paperclip::Interpolations.respond_to? :s3_alias_url
89
89
  Paperclip.interpolates(:s3_path_url) do |attachment, style|
90
90
  "#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
91
- end
91
+ end unless Paperclip::Interpolations.respond_to? :s3_path_url
92
92
  Paperclip.interpolates(:s3_domain_url) do |attachment, style|
93
93
  "#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
94
- end
94
+ end unless Paperclip::Interpolations.respond_to? :s3_domain_url
95
95
  end
96
96
 
97
97
  def expiring_url(time = 3600)
@@ -1,3 +1,3 @@
1
1
  module Paperclip
2
- VERSION = "2.3.8" unless defined? Paperclip::VERSION
2
+ VERSION = "2.3.9" unless defined? Paperclip::VERSION
3
3
  end
@@ -97,6 +97,83 @@ class AttachmentTest < Test::Unit::TestCase
97
97
  end
98
98
  end
99
99
 
100
+ context "An attachment with :timestamp interpolations" do
101
+ setup do
102
+ @file = StringIO.new("...")
103
+ @zone = 'UTC'
104
+ Time.stubs(:zone).returns(@zone)
105
+ @zone_default = 'Eastern Time (US & Canada)'
106
+ Time.stubs(:zone_default).returns(@zone_default)
107
+ end
108
+
109
+ context "using default time zone" do
110
+ setup do
111
+ rebuild_model :path => ":timestamp", :use_default_time_zone => true
112
+ @dummy = Dummy.new
113
+ @dummy.avatar = @file
114
+ end
115
+
116
+ should "return a time in the default zone" do
117
+ assert_equal @dummy.avatar_updated_at.in_time_zone(@zone_default).to_s, @dummy.avatar.path
118
+ end
119
+ end
120
+
121
+ context "using per-thread time zone" do
122
+ setup do
123
+ rebuild_model :path => ":timestamp", :use_default_time_zone => false
124
+ @dummy = Dummy.new
125
+ @dummy.avatar = @file
126
+ end
127
+
128
+ should "return a time in the per-thread zone" do
129
+ assert_equal @dummy.avatar_updated_at.in_time_zone(@zone).to_s, @dummy.avatar.path
130
+ end
131
+ end
132
+ end
133
+
134
+ context "An attachment with :hash interpolations" do
135
+ setup do
136
+ @file = StringIO.new("...")
137
+ end
138
+
139
+ should "raise if no secret is provided" do
140
+ @attachment = attachment :path => ":hash"
141
+ @attachment.assign @file
142
+
143
+ assert_raise ArgumentError do
144
+ @attachment.path
145
+ end
146
+ end
147
+
148
+ context "when secret is set" do
149
+ setup do
150
+ @attachment = attachment :path => ":hash", :hash_secret => "w00t"
151
+ @attachment.stubs(:instance_read).with(:updated_at).returns(Time.at(1234567890))
152
+ @attachment.stubs(:instance_read).with(:file_name).returns("bla.txt")
153
+ @attachment.instance.id = 1234
154
+ @attachment.assign @file
155
+ end
156
+
157
+ should "interpolate the hash data" do
158
+ @attachment.expects(:interpolate).with(@attachment.options[:hash_data],anything).returns("interpolated_stuff")
159
+ @attachment.hash
160
+ end
161
+
162
+ should "result in the correct interpolation" do
163
+ assert_equal "fake_models/avatars/1234/original/1234567890", @attachment.send(:interpolate,@attachment.options[:hash_data])
164
+ end
165
+
166
+ should "result in a correct hash" do
167
+ assert_equal "d22b617d1bf10016aa7d046d16427ae203f39fce", @attachment.path
168
+ end
169
+
170
+ should "generate a hash digest with the correct style" do
171
+ OpenSSL::HMAC.expects(:hexdigest).with(anything, anything, "fake_models/avatars/1234/medium/1234567890")
172
+ @attachment.path("medium")
173
+ end
174
+ end
175
+ end
176
+
100
177
  context "An attachment with a :rails_env interpolation" do
101
178
  setup do
102
179
  @rails_env = "blah"
@@ -487,6 +564,46 @@ class AttachmentTest < Test::Unit::TestCase
487
564
  end
488
565
  end
489
566
 
567
+ context "Attachment with uppercase extension and a default style" do
568
+ setup do
569
+ @old_defaults = Paperclip::Attachment.default_options.dup
570
+ Paperclip::Attachment.default_options.merge!({
571
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
572
+ })
573
+ FileUtils.rm_rf("tmp")
574
+ rebuild_model
575
+ @instance = Dummy.new
576
+ @instance.stubs(:id).returns 123
577
+
578
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "uppercase.PNG"), 'rb')
579
+
580
+ styles = {:styles => { :large => ["400x400", :jpg],
581
+ :medium => ["100x100", :jpg],
582
+ :small => ["32x32#", :jpg]},
583
+ :default_style => :small}
584
+ @attachment = Paperclip::Attachment.new(:avatar,
585
+ @instance,
586
+ styles)
587
+ now = Time.now
588
+ Time.stubs(:now).returns(now)
589
+ @attachment.assign(@file)
590
+ @attachment.save
591
+ end
592
+
593
+ teardown do
594
+ @file.close
595
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
596
+ end
597
+
598
+ should "should have matching to_s and url methods" do
599
+ file = @attachment.to_file
600
+ assert file
601
+ assert_match @attachment.to_s, @attachment.url
602
+ assert_match @attachment.to_s(:small), @attachment.url(:small)
603
+ file.close
604
+ end
605
+ end
606
+
490
607
  context "An attachment" do
491
608
  setup do
492
609
  @old_defaults = Paperclip::Attachment.default_options.dup
@@ -6,6 +6,11 @@ class CommandLineTest < Test::Unit::TestCase
6
6
  File.stubs(:exist?).with("/dev/null").returns(true)
7
7
  end
8
8
 
9
+ should "allow colons in parameters" do
10
+ cmd = Paperclip::CommandLine.new("convert", "'a.jpg' -resize 175x220> -size 175x220 xc:black +swap -gravity center -composite 'b.jpg'", :swallow_stderr => false)
11
+ assert_equal "convert 'a.jpg' -resize 175x220> -size 175x220 xc:black +swap -gravity center -composite 'b.jpg'", cmd.command
12
+ end
13
+
9
14
  should "take a command and parameters and produce a shell command for bash" do
10
15
  cmd = Paperclip::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
11
16
  assert_equal "convert a.jpg b.png", cmd.command
@@ -0,0 +1,107 @@
1
+ require './test/helper'
2
+ require 'fog'
3
+
4
+ Fog.mock!
5
+
6
+ class FogTest < Test::Unit::TestCase
7
+ context "" do
8
+
9
+ setup do
10
+ @fog_directory = 'papercliptests'
11
+
12
+ @credentials = {
13
+ :provider => 'AWS',
14
+ :aws_access_key_id => 'ID',
15
+ :aws_secret_access_key => 'SECRET'
16
+ }
17
+
18
+ @connection = Fog::Storage.new(@credentials)
19
+
20
+ rebuild_model(
21
+ :fog_directory => @fog_directory,
22
+ :fog_credentials => @credentials,
23
+ :fog_host => nil,
24
+ :fog_public => true,
25
+ :path => ":attachment/:basename.:extension",
26
+ :storage => :fog
27
+ )
28
+ end
29
+
30
+ should "be extended by the Fog module" do
31
+ assert Dummy.new.avatar.is_a?(Paperclip::Storage::Fog)
32
+ end
33
+
34
+ context "when assigned" do
35
+ setup do
36
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
37
+ @dummy = Dummy.new
38
+ @dummy.avatar = @file
39
+ end
40
+
41
+ teardown do
42
+ @file.close
43
+ directory = @connection.directories.new(:key => @fog_directory)
44
+ directory.files.each {|file| file.destroy}
45
+ directory.destroy
46
+ end
47
+
48
+ context "without a bucket" do
49
+ should "succeed" do
50
+ assert @dummy.save
51
+ end
52
+ end
53
+
54
+ context "with a bucket" do
55
+ setup do
56
+ @connection.directories.create(:key => @fog_directory)
57
+ end
58
+
59
+ should "succeed" do
60
+ assert @dummy.save
61
+ end
62
+ end
63
+
64
+ context "without a fog_host" do
65
+ setup do
66
+ rebuild_model(
67
+ :fog_directory => @fog_directory,
68
+ :fog_credentials => @credentials,
69
+ :fog_host => nil,
70
+ :fog_public => true,
71
+ :path => ":attachment/:basename.:extension",
72
+ :storage => :fog
73
+ )
74
+ @dummy = Dummy.new
75
+ @dummy.avatar = StringIO.new('.')
76
+ @dummy.save
77
+ end
78
+
79
+ should "provide a public url" do
80
+ assert !@dummy.avatar.url.nil?
81
+ end
82
+ end
83
+
84
+ context "with a fog_host" do
85
+ setup do
86
+ rebuild_model(
87
+ :fog_directory => @fog_directory,
88
+ :fog_credentials => @credentials,
89
+ :fog_host => 'http://example.com',
90
+ :fog_public => true,
91
+ :path => ":attachment/:basename.:extension",
92
+ :storage => :fog
93
+ )
94
+ @dummy = Dummy.new
95
+ @dummy.avatar = StringIO.new('.')
96
+ @dummy.save
97
+ end
98
+
99
+ should "provide a public url" do
100
+ assert @dummy.avatar.url =~ /^http:\/\/example\.com\/avatars\/stringio\.txt\?\d*$/
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+ end