samlown-carrierwave 0.4.5

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 (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
+