samlown-carrierwave 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/Generators +4 -0
  2. data/History.txt +125 -0
  3. data/Manifest.txt +110 -0
  4. data/README.rdoc +524 -0
  5. data/Rakefile +39 -0
  6. data/carrierwave.gemspec +85 -0
  7. data/cucumber.yml +2 -0
  8. data/features/caching.feature +28 -0
  9. data/features/download.feature +20 -0
  10. data/features/file_storage.feature +37 -0
  11. data/features/file_storage_overridden_filename.feature +38 -0
  12. data/features/file_storage_overridden_store_dir.feature +38 -0
  13. data/features/file_storage_reversing_processor.feature +43 -0
  14. data/features/fixtures/bork.txt +1 -0
  15. data/features/fixtures/monkey.txt +1 -0
  16. data/features/grid_fs_storage.feature +32 -0
  17. data/features/mount_activerecord.feature +46 -0
  18. data/features/mount_datamapper.feature +46 -0
  19. data/features/step_definitions/activerecord_steps.rb +22 -0
  20. data/features/step_definitions/caching_steps.rb +14 -0
  21. data/features/step_definitions/datamapper_steps.rb +29 -0
  22. data/features/step_definitions/download_steps.rb +4 -0
  23. data/features/step_definitions/file_steps.rb +53 -0
  24. data/features/step_definitions/general_steps.rb +85 -0
  25. data/features/step_definitions/mount_steps.rb +19 -0
  26. data/features/step_definitions/store_steps.rb +18 -0
  27. data/features/support/activerecord.rb +30 -0
  28. data/features/support/datamapper.rb +7 -0
  29. data/features/support/env.rb +22 -0
  30. data/features/versions_basics.feature +50 -0
  31. data/features/versions_nested_versions.feature +70 -0
  32. data/features/versions_overridden_filename.feature +51 -0
  33. data/features/versions_overriden_store_dir.feature +41 -0
  34. data/lib/carrierwave.rb +98 -0
  35. data/lib/carrierwave/compatibility/paperclip.rb +95 -0
  36. data/lib/carrierwave/core_ext/blank.rb +46 -0
  37. data/lib/carrierwave/core_ext/file.rb +11 -0
  38. data/lib/carrierwave/core_ext/inheritable_attributes.rb +108 -0
  39. data/lib/carrierwave/core_ext/module_setup.rb +51 -0
  40. data/lib/carrierwave/mount.rb +359 -0
  41. data/lib/carrierwave/orm/activerecord.rb +73 -0
  42. data/lib/carrierwave/orm/datamapper.rb +27 -0
  43. data/lib/carrierwave/orm/mongoid.rb +23 -0
  44. data/lib/carrierwave/orm/mongomapper.rb +27 -0
  45. data/lib/carrierwave/orm/sequel.rb +45 -0
  46. data/lib/carrierwave/processing/image_science.rb +101 -0
  47. data/lib/carrierwave/processing/mini_magick.rb +265 -0
  48. data/lib/carrierwave/processing/rmagick.rb +282 -0
  49. data/lib/carrierwave/sanitized_file.rb +273 -0
  50. data/lib/carrierwave/storage/abstract.rb +30 -0
  51. data/lib/carrierwave/storage/cloud_files.rb +169 -0
  52. data/lib/carrierwave/storage/file.rb +48 -0
  53. data/lib/carrierwave/storage/grid_fs.rb +97 -0
  54. data/lib/carrierwave/storage/right_s3.rb +3 -0
  55. data/lib/carrierwave/storage/s3.rb +206 -0
  56. data/lib/carrierwave/test/matchers.rb +128 -0
  57. data/lib/carrierwave/uploader.rb +44 -0
  58. data/lib/carrierwave/uploader/cache.rb +145 -0
  59. data/lib/carrierwave/uploader/callbacks.rb +42 -0
  60. data/lib/carrierwave/uploader/configuration.rb +132 -0
  61. data/lib/carrierwave/uploader/default_url.rb +19 -0
  62. data/lib/carrierwave/uploader/download.rb +59 -0
  63. data/lib/carrierwave/uploader/extension_whitelist.rb +37 -0
  64. data/lib/carrierwave/uploader/mountable.rb +39 -0
  65. data/lib/carrierwave/uploader/processing.rb +83 -0
  66. data/lib/carrierwave/uploader/proxy.rb +62 -0
  67. data/lib/carrierwave/uploader/remove.rb +22 -0
  68. data/lib/carrierwave/uploader/store.rb +89 -0
  69. data/lib/carrierwave/uploader/url.rb +33 -0
  70. data/lib/carrierwave/uploader/versions.rb +146 -0
  71. data/merb_generators/uploader_generator.rb +22 -0
  72. data/rails_generators/uploader/USAGE +2 -0
  73. data/rails_generators/uploader/templates/uploader.rb +47 -0
  74. data/rails_generators/uploader/uploader_generator.rb +21 -0
  75. data/script/console +10 -0
  76. data/script/destroy +14 -0
  77. data/script/generate +14 -0
  78. data/spec/compatibility/paperclip_spec.rb +52 -0
  79. data/spec/fixtures/bork.txt +1 -0
  80. data/spec/fixtures/landscape.jpg +0 -0
  81. data/spec/fixtures/portrait.jpg +0 -0
  82. data/spec/fixtures/test.jpeg +1 -0
  83. data/spec/fixtures/test.jpg +1 -0
  84. data/spec/mount_spec.rb +538 -0
  85. data/spec/orm/activerecord_spec.rb +271 -0
  86. data/spec/orm/datamapper_spec.rb +168 -0
  87. data/spec/orm/mongoid_spec.rb +202 -0
  88. data/spec/orm/mongomapper_spec.rb +202 -0
  89. data/spec/orm/sequel_spec.rb +183 -0
  90. data/spec/processing/image_science_spec.rb +56 -0
  91. data/spec/processing/mini_magick_spec.rb +76 -0
  92. data/spec/processing/rmagick_spec.rb +75 -0
  93. data/spec/sanitized_file_spec.rb +623 -0
  94. data/spec/spec_helper.rb +92 -0
  95. data/spec/storage/cloudfiles_spec.rb +78 -0
  96. data/spec/storage/grid_fs_spec.rb +83 -0
  97. data/spec/storage/s3_spec.rb +118 -0
  98. data/spec/uploader/cache_spec.rb +209 -0
  99. data/spec/uploader/configuration_spec.rb +105 -0
  100. data/spec/uploader/default_url_spec.rb +85 -0
  101. data/spec/uploader/download_spec.rb +75 -0
  102. data/spec/uploader/extension_whitelist_spec.rb +44 -0
  103. data/spec/uploader/mountable_spec.rb +33 -0
  104. data/spec/uploader/paths_spec.rb +22 -0
  105. data/spec/uploader/processing_spec.rb +73 -0
  106. data/spec/uploader/proxy_spec.rb +54 -0
  107. data/spec/uploader/remove_spec.rb +70 -0
  108. data/spec/uploader/store_spec.rb +264 -0
  109. data/spec/uploader/url_spec.rb +102 -0
  110. data/spec/uploader/versions_spec.rb +298 -0
  111. metadata +433 -0
@@ -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,44 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWave
4
+
5
+ ##
6
+ # See CarrierWave::Uploader::Base
7
+ #
8
+ module Uploader
9
+
10
+ ##
11
+ # An uploader is a class that allows you to easily handle the caching and storage of
12
+ # uploaded files. Please refer to the README for configuration options.
13
+ #
14
+ # Once you have an uploader you can use it in isolation:
15
+ #
16
+ # my_uploader = MyUploader.new
17
+ # my_uploader.cache!(File.open(path_to_file))
18
+ # my_uploader.retrieve_from_store!('monkey.png')
19
+ #
20
+ # Alternatively, you can mount it on an ORM or other persistence layer, with
21
+ # +CarrierWave::Mount#mount_uploader+. There are extensions for activerecord and datamapper
22
+ # these are *very* simple (they are only a dozen lines of code), so adding your own should
23
+ # be trivial.
24
+ #
25
+ class Base
26
+ attr_reader :file
27
+
28
+ use CarrierWave::Uploader::Callbacks
29
+ use CarrierWave::Uploader::Proxy
30
+ use CarrierWave::Uploader::Url
31
+ use CarrierWave::Uploader::Mountable
32
+ use CarrierWave::Uploader::Cache
33
+ use CarrierWave::Uploader::Store
34
+ use CarrierWave::Uploader::Download
35
+ use CarrierWave::Uploader::Remove
36
+ use CarrierWave::Uploader::ExtensionWhitelist
37
+ use CarrierWave::Uploader::Processing
38
+ use CarrierWave::Uploader::Versions
39
+ use CarrierWave::Uploader::DefaultUrl
40
+ use CarrierWave::Uploader::Configuration
41
+ end # Base
42
+
43
+ end # Uploader
44
+ 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
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWave
4
+ module Uploader
5
+ module Callbacks
6
+
7
+ setup do
8
+ extlib_inheritable_accessor :_before_callbacks, :_after_callbacks
9
+ end
10
+
11
+ def with_callbacks(kind, *args)
12
+ self.class._before_callbacks_for(kind).each { |callback| self.send(callback, *args) }
13
+ yield
14
+ self.class._after_callbacks_for(kind).each { |callback| self.send(callback, *args) }
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ def _before_callbacks_for(kind) #:nodoc:
20
+ self._before_callbacks ||= {}
21
+ self._before_callbacks[kind] ||= []
22
+ self._before_callbacks[kind]
23
+ end
24
+
25
+ def _after_callbacks_for(kind) #:nodoc:
26
+ self._after_callbacks ||= {}
27
+ self._after_callbacks[kind] ||= []
28
+ self._after_callbacks[kind]
29
+ end
30
+
31
+ def before(kind, callback)
32
+ _before_callbacks_for(kind) << callback
33
+ end
34
+
35
+ def after(kind, callback)
36
+ _after_callbacks_for(kind) << callback
37
+ end
38
+ end # ClassMethods
39
+
40
+ end # Callbacks
41
+ end # Uploader
42
+ end # CarrierWave
@@ -0,0 +1,132 @@
1
+ module CarrierWave
2
+
3
+ module Uploader
4
+ module Configuration
5
+ setup do
6
+ add_config :root
7
+ add_config :permissions
8
+ add_config :storage_engines
9
+ add_config :s3_access # for old aws/s3
10
+ add_config :s3_access_policy # for aws
11
+ add_config :s3_bucket
12
+ add_config :s3_access_key_id
13
+ add_config :s3_secret_access_key
14
+ add_config :s3_cnamed
15
+ add_config :s3_headers
16
+ add_config :s3_multi_thread
17
+ add_config :cloud_files_username
18
+ add_config :cloud_files_api_key
19
+ add_config :cloud_files_container
20
+ add_config :grid_fs_database
21
+ add_config :grid_fs_host
22
+ add_config :grid_fs_port
23
+ add_config :grid_fs_username
24
+ add_config :grid_fs_password
25
+ add_config :grid_fs_access_url
26
+ add_config :store_dir
27
+ add_config :cache_dir
28
+ add_config :enable_processing
29
+
30
+ # Mounting
31
+ add_config :ignore_integrity_errors
32
+ add_config :ignore_processing_errors
33
+ add_config :validate_integrity
34
+ add_config :validate_processing
35
+ add_config :mount_on
36
+
37
+ configure do |config|
38
+ config.permissions = 0644
39
+ config.storage_engines = {
40
+ :file => "CarrierWave::Storage::File",
41
+ :s3 => "CarrierWave::Storage::S3",
42
+ :grid_fs => "CarrierWave::Storage::GridFS",
43
+ :right_s3 => "CarrierWave::Storage::RightS3",
44
+ :cloud_files => "CarrierWave::Storage::CloudFiles"
45
+ }
46
+ config.storage = :file
47
+ #config.s3_access = :public_read
48
+ #config.s3_access_policy = 'public-read' # Now set in library
49
+ config.s3_headers = {}
50
+ config.s3_multi_thread = true
51
+ config.grid_fs_database = 'carrierwave'
52
+ config.grid_fs_host = 'localhost'
53
+ config.grid_fs_port = 27017
54
+ config.store_dir = 'uploads'
55
+ config.cache_dir = 'uploads/tmp'
56
+ config.ignore_integrity_errors = true
57
+ config.ignore_processing_errors = true
58
+ config.validate_integrity = true
59
+ config.validate_processing = true
60
+ config.root = CarrierWave.root
61
+ config.enable_processing = true
62
+ end
63
+ end
64
+
65
+ module ClassMethods
66
+
67
+ ##
68
+ # Sets the storage engine to be used when storing files with this uploader.
69
+ # Can be any class that implements a #store!(CarrierWave::SanitizedFile) and a #retrieve!
70
+ # method. See lib/carrierwave/storage/file.rb for an example. Storage engines should
71
+ # be added to CarrierWave::Uploader::Base.storage_engines so they can be referred
72
+ # to by a symbol, which should be more convenient
73
+ #
74
+ # If no argument is given, it will simply return the currently used storage engine.
75
+ #
76
+ # === Parameters
77
+ #
78
+ # [storage (Symbol, Class)] The storage engine to use for this uploader
79
+ #
80
+ # === Returns
81
+ #
82
+ # [Class] the storage engine to be used with this uploader
83
+ #
84
+ # === Examples
85
+ #
86
+ # storage :file
87
+ # storage CarrierWave::Storage::File
88
+ # storage MyCustomStorageEngine
89
+ #
90
+ def storage(storage = nil)
91
+ if storage.is_a?(Symbol)
92
+ @storage = eval(storage_engines[storage])
93
+ elsif storage
94
+ @storage = storage
95
+ elsif @storage.nil?
96
+ # Get the storage from the superclass if there is one
97
+ @storage = superclass.storage rescue nil
98
+ end
99
+ return @storage
100
+ end
101
+ alias_method :storage=, :storage
102
+
103
+
104
+ def add_config(name)
105
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
106
+ def self.#{name}(value=nil)
107
+ @#{name} = value if value
108
+ return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
109
+ name = superclass.#{name}
110
+ return nil if name.nil? && !instance_variable_defined?("@#{name}")
111
+ @#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
112
+ end
113
+
114
+ def self.#{name}=(value)
115
+ @#{name} = value
116
+ end
117
+
118
+ def #{name}
119
+ self.class.#{name}
120
+ end
121
+ RUBY
122
+ end
123
+
124
+ def configure
125
+ yield self
126
+ end
127
+ end
128
+
129
+ end
130
+ end
131
+ end
132
+