thorsson_carrierwave 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/Generators +4 -0
  2. data/History.txt +125 -0
  3. data/Manifest.txt +111 -0
  4. data/README.rdoc +528 -0
  5. data/Rakefile +39 -0
  6. data/cucumber.yml +2 -0
  7. data/features/caching.feature +28 -0
  8. data/features/download.feature +20 -0
  9. data/features/file_storage.feature +37 -0
  10. data/features/file_storage_overridden_filename.feature +38 -0
  11. data/features/file_storage_overridden_store_dir.feature +38 -0
  12. data/features/file_storage_reversing_processor.feature +43 -0
  13. data/features/fixtures/bork.txt +1 -0
  14. data/features/fixtures/monkey.txt +1 -0
  15. data/features/grid_fs_storage.feature +32 -0
  16. data/features/mount_activerecord.feature +46 -0
  17. data/features/mount_datamapper.feature +46 -0
  18. data/features/step_definitions/activerecord_steps.rb +22 -0
  19. data/features/step_definitions/caching_steps.rb +14 -0
  20. data/features/step_definitions/datamapper_steps.rb +29 -0
  21. data/features/step_definitions/download_steps.rb +4 -0
  22. data/features/step_definitions/file_steps.rb +53 -0
  23. data/features/step_definitions/general_steps.rb +85 -0
  24. data/features/step_definitions/mount_steps.rb +19 -0
  25. data/features/step_definitions/store_steps.rb +18 -0
  26. data/features/support/activerecord.rb +30 -0
  27. data/features/support/datamapper.rb +7 -0
  28. data/features/support/env.rb +22 -0
  29. data/features/versions_basics.feature +50 -0
  30. data/features/versions_nested_versions.feature +70 -0
  31. data/features/versions_overridden_filename.feature +51 -0
  32. data/features/versions_overriden_store_dir.feature +41 -0
  33. data/lib/carrierwave/compatibility/paperclip.rb +95 -0
  34. data/lib/carrierwave/core_ext/blank.rb +46 -0
  35. data/lib/carrierwave/core_ext/inheritable_attributes.rb +104 -0
  36. data/lib/carrierwave/core_ext/module_setup.rb +51 -0
  37. data/lib/carrierwave/mount.rb +359 -0
  38. data/lib/carrierwave/orm/activerecord.rb +73 -0
  39. data/lib/carrierwave/orm/datamapper.rb +27 -0
  40. data/lib/carrierwave/orm/mongoid.rb +23 -0
  41. data/lib/carrierwave/orm/mongomapper.rb +27 -0
  42. data/lib/carrierwave/orm/sequel.rb +45 -0
  43. data/lib/carrierwave/processing/image_science.rb +101 -0
  44. data/lib/carrierwave/processing/mini_magick.rb +265 -0
  45. data/lib/carrierwave/processing/rmagick.rb +282 -0
  46. data/lib/carrierwave/sanitized_file.rb +273 -0
  47. data/lib/carrierwave/storage/abstract.rb +30 -0
  48. data/lib/carrierwave/storage/cloud_files.rb +169 -0
  49. data/lib/carrierwave/storage/file.rb +48 -0
  50. data/lib/carrierwave/storage/grid_fs.rb +97 -0
  51. data/lib/carrierwave/storage/right_s3.rb +167 -0
  52. data/lib/carrierwave/storage/s3.rb +199 -0
  53. data/lib/carrierwave/test/matchers.rb +128 -0
  54. data/lib/carrierwave/uploader/cache.rb +145 -0
  55. data/lib/carrierwave/uploader/callbacks.rb +42 -0
  56. data/lib/carrierwave/uploader/configuration.rb +130 -0
  57. data/lib/carrierwave/uploader/default_url.rb +19 -0
  58. data/lib/carrierwave/uploader/download.rb +59 -0
  59. data/lib/carrierwave/uploader/extension_whitelist.rb +37 -0
  60. data/lib/carrierwave/uploader/mountable.rb +39 -0
  61. data/lib/carrierwave/uploader/processing.rb +83 -0
  62. data/lib/carrierwave/uploader/proxy.rb +62 -0
  63. data/lib/carrierwave/uploader/remove.rb +22 -0
  64. data/lib/carrierwave/uploader/store.rb +89 -0
  65. data/lib/carrierwave/uploader/url.rb +33 -0
  66. data/lib/carrierwave/uploader/versions.rb +146 -0
  67. data/lib/carrierwave/uploader.rb +44 -0
  68. data/lib/carrierwave.rb +99 -0
  69. data/merb_generators/uploader_generator.rb +22 -0
  70. data/rails_generators/uploader/USAGE +2 -0
  71. data/rails_generators/uploader/templates/uploader.rb +47 -0
  72. data/rails_generators/uploader/uploader_generator.rb +21 -0
  73. data/script/console +10 -0
  74. data/script/destroy +14 -0
  75. data/script/generate +14 -0
  76. data/spec/compatibility/paperclip_spec.rb +52 -0
  77. data/spec/fixtures/bork.txt +1 -0
  78. data/spec/fixtures/landscape.jpg +0 -0
  79. data/spec/fixtures/portrait.jpg +0 -0
  80. data/spec/fixtures/test.jpeg +1 -0
  81. data/spec/fixtures/test.jpg +1 -0
  82. data/spec/mount_spec.rb +538 -0
  83. data/spec/orm/activerecord_spec.rb +271 -0
  84. data/spec/orm/datamapper_spec.rb +168 -0
  85. data/spec/orm/mongoid_spec.rb +202 -0
  86. data/spec/orm/mongomapper_spec.rb +202 -0
  87. data/spec/orm/sequel_spec.rb +183 -0
  88. data/spec/processing/image_science_spec.rb +56 -0
  89. data/spec/processing/mini_magick_spec.rb +76 -0
  90. data/spec/processing/rmagick_spec.rb +75 -0
  91. data/spec/sanitized_file_spec.rb +623 -0
  92. data/spec/spec_helper.rb +92 -0
  93. data/spec/storage/grid_fs_spec.rb +83 -0
  94. data/spec/storage/right_s3_spec.rb +83 -0
  95. data/spec/storage/s3_spec.rb +95 -0
  96. data/spec/uploader/cache_spec.rb +209 -0
  97. data/spec/uploader/configuration_spec.rb +105 -0
  98. data/spec/uploader/default_url_spec.rb +85 -0
  99. data/spec/uploader/download_spec.rb +75 -0
  100. data/spec/uploader/extension_whitelist_spec.rb +44 -0
  101. data/spec/uploader/mountable_spec.rb +33 -0
  102. data/spec/uploader/paths_spec.rb +22 -0
  103. data/spec/uploader/processing_spec.rb +73 -0
  104. data/spec/uploader/proxy_spec.rb +54 -0
  105. data/spec/uploader/remove_spec.rb +70 -0
  106. data/spec/uploader/store_spec.rb +264 -0
  107. data/spec/uploader/url_spec.rb +102 -0
  108. data/spec/uploader/versions_spec.rb +298 -0
  109. metadata +436 -0
@@ -0,0 +1,199 @@
1
+ # encoding: utf-8
2
+ require 'aws/s3'
3
+
4
+ module CarrierWave
5
+ module Storage
6
+
7
+ ##
8
+ # Uploads things to Amazon S3 webservices. It requies the aws/s3 gem. In order for
9
+ # CarrierWave to connect to Amazon S3, you'll need to specify an access key id, secret key
10
+ # and bucket:
11
+ #
12
+ # CarrierWave.configure do |config|
13
+ # config.s3_access_key_id = "xxxxxx"
14
+ # config.s3_secret_access_key = "xxxxxx"
15
+ # config.s3_bucket = "my_bucket_name"
16
+ # end
17
+ #
18
+ # You can also set the access policy for the uploaded files:
19
+ #
20
+ # CarrierWave.configure do |config|
21
+ # config.s3_access = :public
22
+ # end
23
+ #
24
+ # Possible values are the 'canned access control policies' provided in the aws/s3 gem,
25
+ # they are:
26
+ #
27
+ # [:private] No one else has any access rights.
28
+ # [:public_read] The anonymous principal is granted READ access.
29
+ # If this policy is used on an object, it can be read from a
30
+ # browser with no authentication.
31
+ # [:public_read_write] The anonymous principal is granted READ and WRITE access.
32
+ # [:authenticated_read] Any principal authenticated as a registered Amazon S3 user
33
+ # is granted READ access.
34
+ #
35
+ # The default is :public_read, it should work in most cases.
36
+ #
37
+ # You can assign HTTP headers to be used when S3 serves your files:
38
+ #
39
+ # CarrierWave.configure do |config|
40
+ # config.s3_headers = {"Content-Disposition" => "attachment; filename=foo.jpg;"}
41
+ # end
42
+ #
43
+ # You can also set the headers dynamically by overriding the s3_headers method:
44
+ #
45
+ # class MyUploader < CarrierWave::Uploader::Base
46
+ # def s3_headers
47
+ # { "Expires" => 1.year.from_how.httpdate }
48
+ # end
49
+ # end
50
+ #
51
+ # You can change the generated url to a cnamed domain by setting the cnamed config:
52
+ #
53
+ # CarrierWave.configure do |config|
54
+ # config.s3_cnamed = true
55
+ # config.s3_bucket = 'bucketname.domain.tld'
56
+ # end
57
+ #
58
+ # Now the resulting url will be
59
+ #
60
+ # http://bucketname.domain.tld/path/to/file
61
+ #
62
+ # instead of
63
+ #
64
+ # http://s3.amazonaws.com/bucketname.domain.tld/path/to/file
65
+ #
66
+ class S3 < Abstract
67
+
68
+ class File
69
+
70
+ def initialize(uploader, path)
71
+ @uploader = uploader
72
+ @path = path
73
+ end
74
+
75
+ ##
76
+ # Returns the current path of the file on S3
77
+ #
78
+ # === Returns
79
+ #
80
+ # [String] A path
81
+ #
82
+ def path
83
+ @path
84
+ end
85
+
86
+ ##
87
+ # Reads the contents of the file from S3
88
+ #
89
+ # === Returns
90
+ #
91
+ # [String] contents of the file
92
+ #
93
+ def read
94
+ AWS::S3::S3Object.value @path, @uploader.s3_bucket
95
+ end
96
+
97
+ ##
98
+ # Remove the file from Amazon S3
99
+ #
100
+ def delete
101
+ AWS::S3::S3Object.delete @path, @uploader.s3_bucket
102
+ end
103
+
104
+ ##
105
+ # Returns the url on Amazon's S3 service
106
+ #
107
+ # === Returns
108
+ #
109
+ # [String] file's url
110
+ #
111
+ def url
112
+ if @uploader.s3_cnamed
113
+ ["http://", @uploader.s3_bucket, "/", @path].compact.join
114
+ else
115
+ ["http://s3.amazonaws.com/", @uploader.s3_bucket, "/", @path].compact.join
116
+ end
117
+ end
118
+
119
+ def about
120
+ s3_object.about
121
+ end
122
+
123
+ def metadata
124
+ s3_object.metadata
125
+ end
126
+
127
+ def content_type
128
+ s3_object.content_type
129
+ end
130
+
131
+ def content_type=(new_content_type)
132
+ s3_object.content_type = new_content_type
133
+ end
134
+
135
+ def content_disposition
136
+ s3_object.content_disposition
137
+ end
138
+
139
+ def content_disposition=(new_disposition)
140
+ s3_object.content_disposition = new_disposition
141
+ end
142
+
143
+ def store
144
+ s3_object.store
145
+ end
146
+
147
+ def s3_object
148
+ @s3_object ||= AWS::S3::S3Object.find(@path, @uploader.s3_bucket)
149
+ end
150
+
151
+ end
152
+
153
+ ##
154
+ # Store the file on S3
155
+ #
156
+ # === Parameters
157
+ #
158
+ # [file (CarrierWave::Storage::S3::File)] the file to store
159
+ #
160
+ # === Returns
161
+ #
162
+ # [CarrierWave::Storage::S3] the stored file
163
+ #
164
+ def store!(file)
165
+ connect!(uploader)
166
+ s3_options = {:access => uploader.s3_access, :content_type => file.content_type}
167
+ s3_options.merge!(uploader.s3_headers)
168
+ AWS::S3::S3Object.store(uploader.store_path, file.read, uploader.s3_bucket, s3_options)
169
+ CarrierWave::Storage::S3::File.new(uploader, uploader.store_path)
170
+ end
171
+
172
+ # Do something to retrieve the file
173
+ #
174
+ # @param [CarrierWave::Uploader] uploader an uploader object
175
+ # @param [String] identifier uniquely identifies the file
176
+ #
177
+ # [identifier (String)] uniquely identifies the file
178
+ #
179
+ # === Returns
180
+ #
181
+ # [CarrierWave::Storage::S3::File] the stored file
182
+ #
183
+ def retrieve!(identifier)
184
+ connect!(uploader)
185
+ CarrierWave::Storage::S3::File.new(uploader, uploader.store_path(identifier))
186
+ end
187
+
188
+ private
189
+
190
+ def connect!(uploader)
191
+ AWS::S3::Base.establish_connection!(
192
+ :access_key_id => uploader.s3_access_key_id,
193
+ :secret_access_key => uploader.s3_secret_access_key
194
+ )
195
+ end
196
+
197
+ end # S3
198
+ end # Storage
199
+ end # CarrierWave
@@ -0,0 +1,128 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWave
4
+ module Test
5
+
6
+ ##
7
+ # These are some matchers that can be used in RSpec specs, to simplify the testing
8
+ # of uploaders.
9
+ #
10
+ module Matchers
11
+
12
+ class BeIdenticalTo # :nodoc:
13
+ def initialize(expected)
14
+ @expected = expected
15
+ end
16
+ def matches?(actual)
17
+ @actual = actual
18
+ FileUtils.identical?(@actual, @expected)
19
+ end
20
+ def failure_message
21
+ "expected #{@actual.inspect} to be identical to #{@expected.inspect}"
22
+ end
23
+ def negative_failure_message
24
+ "expected #{@actual.inspect} to not be identical to #{@expected.inspect}"
25
+ end
26
+ end
27
+
28
+ def be_identical_to(expected)
29
+ BeIdenticalTo.new(expected)
30
+ end
31
+
32
+ class HavePermissions # :nodoc:
33
+ def initialize(expected)
34
+ @expected = expected
35
+ end
36
+
37
+ def matches?(actual)
38
+ @actual = actual
39
+ # Satisfy expectation here. Return false or raise an error if it's not met.
40
+ (File.stat(@actual.path).mode & 0777) == @expected
41
+ end
42
+
43
+ def failure_message
44
+ "expected #{@actual.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0777).to_s(8)}"
45
+ end
46
+
47
+ def negative_failure_message
48
+ "expected #{@actual.inspect} not to have permissions #{@expected.to_s(8)}, but it did"
49
+ end
50
+ end
51
+
52
+ def have_permissions(expected)
53
+ HavePermissions.new(expected)
54
+ end
55
+
56
+ class BeNoLargerThan # :nodoc:
57
+ def initialize(width, height)
58
+ @width, @height = width, height
59
+ end
60
+
61
+ def matches?(actual)
62
+ @actual = actual
63
+ # Satisfy expectation here. Return false or raise an error if it's not met.
64
+ img = ::Magick::Image.read(@actual.current_path).first
65
+ @actual_width = img.columns
66
+ @actual_height = img.rows
67
+ @actual_width <= @width && @actual_height <= @height
68
+ end
69
+
70
+ def failure_message
71
+ "expected #{@actual.current_path.inspect} to be no larger than #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
72
+ end
73
+
74
+ def negative_failure_message
75
+ "expected #{@actual.current_path.inspect} to be larger than #{@width} by #{@height}, but it wasn't."
76
+ end
77
+ end
78
+
79
+ def be_no_larger_than(width, height)
80
+ load_rmagick
81
+ BeNoLargerThan.new(width, height)
82
+ end
83
+
84
+ class HaveDimensions # :nodoc:
85
+ def initialize(width, height)
86
+ @width, @height = width, height
87
+ end
88
+
89
+ def matches?(actual)
90
+ @actual = actual
91
+ # Satisfy expectation here. Return false or raise an error if it's not met.
92
+ img = ::Magick::Image.read(@actual.current_path).first
93
+ @actual_width = img.columns
94
+ @actual_height = img.rows
95
+ @actual_width == @width && @actual_height == @height
96
+ end
97
+
98
+ def failure_message
99
+ "expected #{@actual.current_path.inspect} to have an exact size of #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
100
+ end
101
+
102
+ def negative_failure_message
103
+ "expected #{@actual.current_path.inspect} not to have an exact size of #{@width} by #{@height}, but it did."
104
+ end
105
+ end
106
+
107
+ def have_dimensions(width, height)
108
+ load_rmagick
109
+ HaveDimensions.new(width, height)
110
+ end
111
+
112
+ private
113
+
114
+ def load_rmagick
115
+ unless defined? Magick
116
+ begin
117
+ require 'rmagick'
118
+ rescue LoadError
119
+ require 'RMagick'
120
+ rescue LoadError
121
+ puts "WARNING: Failed to require rmagick, image processing may fail!"
122
+ end
123
+ end
124
+ end
125
+
126
+ end # SpecHelper
127
+ end # Test
128
+ end # CarrierWave
@@ -0,0 +1,145 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWave
4
+
5
+ class FormNotMultipart < UploadError
6
+ def message
7
+ "You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.\n\n If this is a file upload, please check that your upload form is multipart encoded."
8
+ end
9
+ end
10
+
11
+ ##
12
+ # Generates a unique cache id for use in the caching system
13
+ #
14
+ # === Returns
15
+ #
16
+ # [String] a cache id in the format YYYYMMDD-HHMM-PID-RND
17
+ #
18
+ def self.generate_cache_id
19
+ Time.now.strftime('%Y%m%d-%H%M') + '-' + Process.pid.to_s + '-' + ("%04d" % rand(9999))
20
+ end
21
+
22
+ module Uploader
23
+ module Cache
24
+
25
+ depends_on CarrierWave::Uploader::Callbacks
26
+ depends_on CarrierWave::Uploader::Configuration
27
+
28
+ module ClassMethods
29
+
30
+ ##
31
+ # Removes cached files which are older than one day. You could call this method
32
+ # from a rake task to clean out old cached files.
33
+ #
34
+ # You can call this method directly on the module like this:
35
+ #
36
+ # CarrierWave.clean_cached_files!
37
+ #
38
+ # === Note
39
+ #
40
+ # This only works as long as you haven't done anything funky with your cache_dir.
41
+ # It's recommended that you keen cache files in one place only.
42
+ #
43
+ def clean_cached_files!
44
+ Dir.glob(File.expand_path(File.join(cache_dir, '*'), CarrierWave.root)).each do |dir|
45
+ time = dir.scan(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})/).first.map { |t| t.to_i }
46
+ time = Time.utc(*time)
47
+ if time < (Time.now - (60*60*24))
48
+ FileUtils.rm_rf(dir)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ ##
55
+ # Returns true if the uploader has been cached
56
+ #
57
+ # === Returns
58
+ #
59
+ # [Bool] whether the current file is cached
60
+ #
61
+ def cached?
62
+ @cache_id
63
+ end
64
+
65
+ ##
66
+ # Returns a String which uniquely identifies the currently cached file for later retrieval
67
+ #
68
+ # === Returns
69
+ #
70
+ # [String] a cache name, in the format YYYYMMDD-HHMM-PID-RND/filename.txt
71
+ #
72
+ def cache_name
73
+ File.join(cache_id, full_original_filename) if cache_id and original_filename
74
+ end
75
+
76
+ ##
77
+ # Caches the given file. Calls process! to trigger any process callbacks.
78
+ #
79
+ # === Parameters
80
+ #
81
+ # [new_file (File, IOString, Tempfile)] any kind of file object
82
+ #
83
+ # === Raises
84
+ #
85
+ # [CarrierWave::FormNotMultipart] if the assigned parameter is a string
86
+ #
87
+ def cache!(new_file)
88
+ new_file = CarrierWave::SanitizedFile.new(new_file)
89
+ raise CarrierWave::FormNotMultipart if new_file.is_path?
90
+
91
+ unless new_file.empty?
92
+ with_callbacks(:cache, new_file) do
93
+ self.cache_id = CarrierWave.generate_cache_id unless cache_id
94
+
95
+ @filename = new_file.filename
96
+ self.original_filename = new_file.filename
97
+
98
+ @file = new_file.copy_to(cache_path, permissions)
99
+ end
100
+ end
101
+ end
102
+
103
+ ##
104
+ # Retrieves the file with the given cache_name from the cache.
105
+ #
106
+ # === Parameters
107
+ #
108
+ # [cache_name (String)] uniquely identifies a cache file
109
+ #
110
+ # === Raises
111
+ #
112
+ # [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
113
+ #
114
+ def retrieve_from_cache!(cache_name)
115
+ with_callbacks(:retrieve_from_cache, cache_name) do
116
+ self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
117
+ @filename = original_filename
118
+ @file = CarrierWave::SanitizedFile.new(cache_path)
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def cache_path
125
+ File.expand_path(File.join(cache_dir, cache_name), root)
126
+ end
127
+
128
+ attr_reader :cache_id, :original_filename
129
+
130
+ # We can override the full_original_filename method in other modules
131
+ alias_method :full_original_filename, :original_filename
132
+
133
+ def cache_id=(cache_id)
134
+ raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}\z/
135
+ @cache_id = cache_id
136
+ end
137
+
138
+ def original_filename=(filename)
139
+ raise CarrierWave::InvalidParameter, "invalid filename" unless filename =~ /\A[a-z0-9\.\-\+_]+\z/i
140
+ @original_filename = filename
141
+ end
142
+
143
+ end # Cache
144
+ end # Uploader
145
+ end # CarrierWave