paperclip-hacked 2.3.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/LICENSE +26 -0
  2. data/README.rdoc +179 -0
  3. data/Rakefile +103 -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/paperclip.rb +357 -0
  9. data/lib/paperclip/attachmenta.rb +334 -0
  10. data/lib/paperclip/callback_compatability.rb +33 -0
  11. data/lib/paperclip/geometry.rb +115 -0
  12. data/lib/paperclip/interpolations.rb +108 -0
  13. data/lib/paperclip/iostream.rb +59 -0
  14. data/lib/paperclip/matchers.rb +33 -0
  15. data/lib/paperclip/matchers/have_attached_file_matcher.rb +57 -0
  16. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +75 -0
  17. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +55 -0
  18. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +96 -0
  19. data/lib/paperclip/processor.rb +49 -0
  20. data/lib/paperclip/storage.rb +247 -0
  21. data/lib/paperclip/thumbnail.rb +75 -0
  22. data/lib/paperclip/upfile.rb +50 -0
  23. data/shoulda_macros/paperclip.rb +117 -0
  24. data/tasks/paperclip_tasks.rake +79 -0
  25. data/test/attachment_test.rb +764 -0
  26. data/test/database.yml +4 -0
  27. data/test/fixtures/12k.png +0 -0
  28. data/test/fixtures/50x50.png +0 -0
  29. data/test/fixtures/5k.png +0 -0
  30. data/test/fixtures/bad.png +1 -0
  31. data/test/fixtures/s3.yml +8 -0
  32. data/test/fixtures/text.txt +0 -0
  33. data/test/fixtures/twopage.pdf +0 -0
  34. data/test/geometry_test.rb +177 -0
  35. data/test/helper.rb +128 -0
  36. data/test/integration_test.rb +483 -0
  37. data/test/interpolations_test.rb +124 -0
  38. data/test/iostream_test.rb +78 -0
  39. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  40. data/test/matchers/validate_attachment_content_type_matcher_test.rb +37 -0
  41. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  42. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  43. data/test/paperclip_test.rb +298 -0
  44. data/test/processor_test.rb +10 -0
  45. data/test/storage_test.rb +330 -0
  46. data/test/thumbnail_test.rb +227 -0
  47. data/test/upfile_test.rb +36 -0
  48. metadata +185 -0
@@ -0,0 +1,108 @@
1
+ module Paperclip
2
+ # This module contains all the methods that are available for interpolation
3
+ # in paths and urls. To add your own (or override an existing one), you
4
+ # can either open this module and define it, or call the
5
+ # Paperclip.interpolates method.
6
+ module Interpolations
7
+ extend self
8
+
9
+ # Hash assignment of interpolations. Included only for compatability,
10
+ # and is not intended for normal use.
11
+ def self.[]= name, block
12
+ define_method(name, &block)
13
+ end
14
+
15
+ # Hash access of interpolations. Included only for compatability,
16
+ # and is not intended for normal use.
17
+ def self.[] name
18
+ method(name)
19
+ end
20
+
21
+ # Returns a sorted list of all interpolations.
22
+ def self.all
23
+ self.instance_methods(false).sort
24
+ end
25
+
26
+ # Perform the actual interpolation. Takes the pattern to interpolate
27
+ # and the arguments to pass, which are the attachment and style name.
28
+ def self.interpolate pattern, *args
29
+ all.reverse.inject( pattern.dup ) do |result, tag|
30
+ result.gsub(/:#{tag}/) do |match|
31
+ send( tag, *args )
32
+ end
33
+ end
34
+ end
35
+
36
+ # Returns the filename, the same way as ":basename.:extension" would.
37
+ def filename attachment, style_name
38
+ "#{basename(attachment, style_name)}.#{extension(attachment, style_name)}"
39
+ end
40
+
41
+ # Returns the interpolated URL. Will raise an error if the url itself
42
+ # contains ":url" to prevent infinite recursion. This interpolation
43
+ # is used in the default :path to ease default specifications.
44
+ def url attachment, style_name
45
+ raise InfiniteInterpolationError if attachment.options[:url].include?(":url")
46
+ attachment.url(style_name, false)
47
+ end
48
+
49
+ # Returns the timestamp as defined by the <attachment>_updated_at field
50
+ def timestamp attachment, style_name
51
+ attachment.instance_read(:updated_at).to_s
52
+ end
53
+
54
+ # Returns the RAILS_ROOT constant.
55
+ def rails_root attachment, style_name
56
+ RAILS_ROOT
57
+ end
58
+
59
+ # Returns the RAILS_ENV constant.
60
+ def rails_env attachment, style_name
61
+ RAILS_ENV
62
+ end
63
+
64
+ # Returns the underscored, pluralized version of the class name.
65
+ # e.g. "users" for the User class.
66
+ # NOTE: The arguments need to be optional, because some tools fetch
67
+ # all class names. Calling #class will return the expected class.
68
+ def class attachment = nil, style_name = nil
69
+ return super() if attachment.nil? && style_name.nil?
70
+ attachment.instance.class.to_s.underscore.pluralize
71
+ end
72
+
73
+ # Returns the basename of the file. e.g. "file" for "file.jpg"
74
+ def basename attachment, style_name
75
+ attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "")
76
+ end
77
+
78
+ # Returns the extension of the file. e.g. "jpg" for "file.jpg"
79
+ # If the style has a format defined, it will return the format instead
80
+ # of the actual extension.
81
+ def extension attachment, style_name
82
+ ((style = attachment.styles[style_name]) && style[:format]) ||
83
+ File.extname(attachment.original_filename).gsub(/^\.+/, "")
84
+ end
85
+
86
+ # Returns the id of the instance.
87
+ def id attachment, style_name
88
+ attachment.instance.id
89
+ end
90
+
91
+ # Returns the id of the instance in a split path form. e.g. returns
92
+ # 000/001/234 for an id of 1234.
93
+ def id_partition attachment, style_name
94
+ ("%09d" % attachment.instance.id).scan(/\d{3}/).join("/")
95
+ end
96
+
97
+ # Returns the pluralized form of the attachment name. e.g.
98
+ # "avatars" for an attachment of :avatar
99
+ def attachment attachment, style_name
100
+ attachment.name.to_s.downcase.pluralize
101
+ end
102
+
103
+ # Returns the style, or the default style if nil is supplied.
104
+ def style attachment, style_name
105
+ style_name || attachment.default_style
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,59 @@
1
+ # Provides method that can be included on File-type objects (IO, StringIO, Tempfile, etc) to allow stream copying
2
+ # and Tempfile conversion.
3
+ module IOStream
4
+
5
+ # Returns a Tempfile containing the contents of the readable object.
6
+ def to_tempfile
7
+ name = respond_to?(:original_filename) ? original_filename : (respond_to?(:path) ? path : "stream")
8
+ tempfile = Paperclip::Tempfile.new(File.basename(name))
9
+ tempfile.binmode
10
+ self.stream_to(tempfile)
11
+ end
12
+
13
+ # Copies one read-able object from one place to another in blocks, obviating the need to load
14
+ # the whole thing into memory. Defaults to 8k blocks. If this module is included in both
15
+ # StringIO and Tempfile, then either can have its data copied anywhere else without typing
16
+ # worries or memory overhead worries. Returns a File if a String is passed in as the destination
17
+ # and returns the IO or Tempfile as passed in if one is sent as the destination.
18
+ def stream_to path_or_file, in_blocks_of = 8192
19
+ dstio = case path_or_file
20
+ when String then File.new(path_or_file, "wb+")
21
+ when IO then path_or_file
22
+ when Tempfile then path_or_file
23
+ end
24
+ buffer = ""
25
+ self.rewind
26
+ while self.read(in_blocks_of, buffer) do
27
+ dstio.write(buffer)
28
+ end
29
+ dstio.rewind
30
+ dstio
31
+ end
32
+ end
33
+
34
+ class IO #:nodoc:
35
+ include IOStream
36
+ end
37
+
38
+ %w( Tempfile StringIO ).each do |klass|
39
+ if Object.const_defined? klass
40
+ Object.const_get(klass).class_eval do
41
+ include IOStream
42
+ end
43
+ end
44
+ end
45
+
46
+ # Corrects a bug in Windows when asking for Tempfile size.
47
+ if defined? Tempfile
48
+ class Tempfile
49
+ def size
50
+ if @tmpfile
51
+ @tmpfile.fsync
52
+ @tmpfile.flush
53
+ @tmpfile.stat.size
54
+ else
55
+ 0
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,33 @@
1
+ require 'paperclip/matchers/have_attached_file_matcher'
2
+ require 'paperclip/matchers/validate_attachment_presence_matcher'
3
+ require 'paperclip/matchers/validate_attachment_content_type_matcher'
4
+ require 'paperclip/matchers/validate_attachment_size_matcher'
5
+
6
+ module Paperclip
7
+ module Shoulda
8
+ # Provides rspec-compatible matchers for testing Paperclip attachments.
9
+ #
10
+ # In spec_helper.rb, you'll need to require the matchers:
11
+ #
12
+ # require "paperclip/matchers"
13
+ #
14
+ # And include the module:
15
+ #
16
+ # Spec::Runner.configure do |config|
17
+ # config.include Paperclip::Shoulda::Matchers
18
+ # end
19
+ #
20
+ # Example:
21
+ # describe User do
22
+ # it { should have_attached_file(:avatar) }
23
+ # it { should validate_attachment_presence(:avatar) }
24
+ # it { should validate_attachment_content_type(:avatar).
25
+ # allowing('image/png', 'image/gif').
26
+ # rejecting('text/plain', 'text/xml') }
27
+ # it { should validate_attachment_size(:avatar).
28
+ # less_than(2.megabytes) }
29
+ # end
30
+ module Matchers
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,57 @@
1
+ module Paperclip
2
+ module Shoulda
3
+ module Matchers
4
+ # Ensures that the given instance or class has an attachment with the
5
+ # given name.
6
+ #
7
+ # Example:
8
+ # describe User do
9
+ # it { should have_attached_file(:avatar) }
10
+ # end
11
+ def have_attached_file name
12
+ HaveAttachedFileMatcher.new(name)
13
+ end
14
+
15
+ class HaveAttachedFileMatcher
16
+ def initialize attachment_name
17
+ @attachment_name = attachment_name
18
+ end
19
+
20
+ def matches? subject
21
+ @subject = subject
22
+ @subject = @subject.class unless Class === @subject
23
+ responds? && has_column? && included?
24
+ end
25
+
26
+ def failure_message
27
+ "Should have an attachment named #{@attachment_name}"
28
+ end
29
+
30
+ def negative_failure_message
31
+ "Should not have an attachment named #{@attachment_name}"
32
+ end
33
+
34
+ def description
35
+ "have an attachment named #{@attachment_name}"
36
+ end
37
+
38
+ protected
39
+
40
+ def responds?
41
+ methods = @subject.instance_methods.map(&:to_s)
42
+ methods.include?("#{@attachment_name}") &&
43
+ methods.include?("#{@attachment_name}=") &&
44
+ methods.include?("#{@attachment_name}?")
45
+ end
46
+
47
+ def has_column?
48
+ @subject.column_names.include?("#{@attachment_name}_file_name")
49
+ end
50
+
51
+ def included?
52
+ @subject.ancestors.include?(Paperclip::InstanceMethods)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,75 @@
1
+ module Paperclip
2
+ module Shoulda
3
+ module Matchers
4
+ # Ensures that the given instance or class validates the content type of
5
+ # the given attachment as specified.
6
+ #
7
+ # Example:
8
+ # describe User do
9
+ # it { should validate_attachment_content_type(:icon).
10
+ # allowing('image/png', 'image/gif').
11
+ # rejecting('text/plain', 'text/xml') }
12
+ # end
13
+ def validate_attachment_content_type name
14
+ ValidateAttachmentContentTypeMatcher.new(name)
15
+ end
16
+
17
+ class ValidateAttachmentContentTypeMatcher
18
+ def initialize attachment_name
19
+ @attachment_name = attachment_name
20
+ end
21
+
22
+ def allowing *types
23
+ @allowed_types = types.flatten
24
+ self
25
+ end
26
+
27
+ def rejecting *types
28
+ @rejected_types = types.flatten
29
+ self
30
+ end
31
+
32
+ def matches? subject
33
+ @subject = subject
34
+ @subject = @subject.class unless Class === @subject
35
+ @allowed_types && @rejected_types &&
36
+ allowed_types_allowed? && rejected_types_rejected?
37
+ end
38
+
39
+ def failure_message
40
+ "Content types #{@allowed_types.join(", ")} should be accepted" +
41
+ " and #{@rejected_types.join(", ")} rejected by #{@attachment_name}"
42
+ end
43
+
44
+ def negative_failure_message
45
+ "Content types #{@allowed_types.join(", ")} should be rejected" +
46
+ " and #{@rejected_types.join(", ")} accepted by #{@attachment_name}"
47
+ end
48
+
49
+ def description
50
+ "validate the content types allowed on attachment #{@attachment_name}"
51
+ end
52
+
53
+ protected
54
+
55
+ def allow_types?(types)
56
+ types.all? do |type|
57
+ file = StringIO.new(".")
58
+ file.content_type = type
59
+ (subject = @subject.new).attachment_for(@attachment_name).assign(file)
60
+ subject.valid? && subject.errors.on(:"#{@attachment_name}_content_type").blank?
61
+ end
62
+ end
63
+
64
+ def allowed_types_allowed?
65
+ allow_types?(@allowed_types)
66
+ end
67
+
68
+ def rejected_types_rejected?
69
+ not allow_types?(@rejected_types)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+
@@ -0,0 +1,55 @@
1
+ module Paperclip
2
+ module Shoulda
3
+ module Matchers
4
+ # Ensures that the given instance or class validates the presence of the
5
+ # given attachment.
6
+ #
7
+ # describe User do
8
+ # it { should validate_attachment_presence(:avatar) }
9
+ # end
10
+ def validate_attachment_presence name
11
+ ValidateAttachmentPresenceMatcher.new(name)
12
+ end
13
+
14
+ class ValidateAttachmentPresenceMatcher
15
+ def initialize attachment_name
16
+ @attachment_name = attachment_name
17
+ end
18
+
19
+ def matches? subject
20
+ @subject = subject
21
+ @subject = @subject.class unless Class === @subject
22
+ error_when_not_valid? && no_error_when_valid?
23
+ end
24
+
25
+ def failure_message
26
+ "Attachment #{@attachment_name} should be required"
27
+ end
28
+
29
+ def negative_failure_message
30
+ "Attachment #{@attachment_name} should not be required"
31
+ end
32
+
33
+ def description
34
+ "require presence of attachment #{@attachment_name}"
35
+ end
36
+
37
+ protected
38
+
39
+ def error_when_not_valid?
40
+ (subject = @subject.new).send(@attachment_name).assign(nil)
41
+ subject.valid?
42
+ not subject.errors.on(:"#{@attachment_name}_file_name").blank?
43
+ end
44
+
45
+ def no_error_when_valid?
46
+ @file = StringIO.new(".")
47
+ (subject = @subject.new).send(@attachment_name).assign(@file)
48
+ subject.valid?
49
+ subject.errors.on(:"#{@attachment_name}_file_name").blank?
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,96 @@
1
+ module Paperclip
2
+ module Shoulda
3
+ module Matchers
4
+ # Ensures that the given instance or class validates the size of the
5
+ # given attachment as specified.
6
+ #
7
+ # Examples:
8
+ # it { should validate_attachment_size(:avatar).
9
+ # less_than(2.megabytes) }
10
+ # it { should validate_attachment_size(:icon).
11
+ # greater_than(1024) }
12
+ # it { should validate_attachment_size(:icon).
13
+ # in(0..100) }
14
+ def validate_attachment_size name
15
+ ValidateAttachmentSizeMatcher.new(name)
16
+ end
17
+
18
+ class ValidateAttachmentSizeMatcher
19
+ def initialize attachment_name
20
+ @attachment_name = attachment_name
21
+ @low, @high = 0, (1.0/0)
22
+ end
23
+
24
+ def less_than size
25
+ @high = size
26
+ self
27
+ end
28
+
29
+ def greater_than size
30
+ @low = size
31
+ self
32
+ end
33
+
34
+ def in range
35
+ @low, @high = range.first, range.last
36
+ self
37
+ end
38
+
39
+ def matches? subject
40
+ @subject = subject
41
+ @subject = @subject.class unless Class === @subject
42
+ lower_than_low? && higher_than_low? && lower_than_high? && higher_than_high?
43
+ end
44
+
45
+ def failure_message
46
+ "Attachment #{@attachment_name} must be between #{@low} and #{@high} bytes"
47
+ end
48
+
49
+ def negative_failure_message
50
+ "Attachment #{@attachment_name} cannot be between #{@low} and #{@high} bytes"
51
+ end
52
+
53
+ def description
54
+ "validate the size of attachment #{@attachment_name}"
55
+ end
56
+
57
+ protected
58
+
59
+ def override_method object, method, &replacement
60
+ (class << object; self; end).class_eval do
61
+ define_method(method, &replacement)
62
+ end
63
+ end
64
+
65
+ def passes_validation_with_size(new_size)
66
+ file = StringIO.new(".")
67
+ override_method(file, :size){ new_size }
68
+ override_method(file, :to_tempfile){ file }
69
+
70
+ (subject = @subject.new).send(@attachment_name).assign(file)
71
+ subject.valid?
72
+ subject.errors.on(:"#{@attachment_name}_file_size").blank?
73
+ end
74
+
75
+ def lower_than_low?
76
+ not passes_validation_with_size(@low - 1)
77
+ end
78
+
79
+ def higher_than_low?
80
+ passes_validation_with_size(@low + 1)
81
+ end
82
+
83
+ def lower_than_high?
84
+ return true if @high == (1.0/0)
85
+ passes_validation_with_size(@high - 1)
86
+ end
87
+
88
+ def higher_than_high?
89
+ return true if @high == (1.0/0)
90
+ not passes_validation_with_size(@high + 1)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+