durran-carrierwave 0.3.2.3

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 (91) hide show
  1. data/Generators +4 -0
  2. data/History.txt +66 -0
  3. data/LICENSE +8 -0
  4. data/Manifest.txt +89 -0
  5. data/README.rdoc +342 -0
  6. data/Rakefile +30 -0
  7. data/carrierwave.gemspec +57 -0
  8. data/cucumber.yml +2 -0
  9. data/features/caching.feature +28 -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/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/file_steps.rb +42 -0
  22. data/features/step_definitions/general_steps.rb +80 -0
  23. data/features/step_definitions/mount_steps.rb +19 -0
  24. data/features/step_definitions/store_steps.rb +18 -0
  25. data/features/support/activerecord.rb +30 -0
  26. data/features/support/datamapper.rb +7 -0
  27. data/features/support/env.rb +35 -0
  28. data/features/versions_basics.feature +50 -0
  29. data/features/versions_nested_versions.feature +70 -0
  30. data/features/versions_overridden_filename.feature +51 -0
  31. data/features/versions_overriden_store_dir.feature +41 -0
  32. data/lib/carrierwave.rb +145 -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 +332 -0
  38. data/lib/carrierwave/orm/activerecord.rb +73 -0
  39. data/lib/carrierwave/orm/datamapper.rb +27 -0
  40. data/lib/carrierwave/orm/mongomapper.rb +27 -0
  41. data/lib/carrierwave/orm/sequel.rb +57 -0
  42. data/lib/carrierwave/processing/image_science.rb +72 -0
  43. data/lib/carrierwave/processing/rmagick.rb +286 -0
  44. data/lib/carrierwave/sanitized_file.rb +272 -0
  45. data/lib/carrierwave/storage/abstract.rb +32 -0
  46. data/lib/carrierwave/storage/file.rb +50 -0
  47. data/lib/carrierwave/storage/s3.rb +215 -0
  48. data/lib/carrierwave/test/matchers.rb +114 -0
  49. data/lib/carrierwave/uploader.rb +43 -0
  50. data/lib/carrierwave/uploader/cache.rb +116 -0
  51. data/lib/carrierwave/uploader/callbacks.rb +42 -0
  52. data/lib/carrierwave/uploader/default_path.rb +23 -0
  53. data/lib/carrierwave/uploader/extension_whitelist.rb +37 -0
  54. data/lib/carrierwave/uploader/mountable.rb +39 -0
  55. data/lib/carrierwave/uploader/paths.rb +27 -0
  56. data/lib/carrierwave/uploader/processing.rb +81 -0
  57. data/lib/carrierwave/uploader/proxy.rb +62 -0
  58. data/lib/carrierwave/uploader/remove.rb +23 -0
  59. data/lib/carrierwave/uploader/store.rb +156 -0
  60. data/lib/carrierwave/uploader/url.rb +24 -0
  61. data/lib/carrierwave/uploader/versions.rb +147 -0
  62. data/lib/generators/uploader_generator.rb +22 -0
  63. data/rails_generators/uploader/USAGE +2 -0
  64. data/rails_generators/uploader/templates/uploader.rb +47 -0
  65. data/rails_generators/uploader/uploader_generator.rb +21 -0
  66. data/script/console +10 -0
  67. data/script/destroy +14 -0
  68. data/script/generate +14 -0
  69. data/spec/compatibility/paperclip_spec.rb +43 -0
  70. data/spec/fixtures/bork.txt +1 -0
  71. data/spec/fixtures/test.jpeg +1 -0
  72. data/spec/fixtures/test.jpg +1 -0
  73. data/spec/mount_spec.rb +517 -0
  74. data/spec/orm/activerecord_spec.rb +271 -0
  75. data/spec/orm/datamapper_spec.rb +161 -0
  76. data/spec/orm/mongomapper_spec.rb +184 -0
  77. data/spec/orm/sequel_spec.rb +192 -0
  78. data/spec/sanitized_file_spec.rb +612 -0
  79. data/spec/spec_helper.rb +99 -0
  80. data/spec/uploader/cache_spec.rb +196 -0
  81. data/spec/uploader/default_path_spec.rb +68 -0
  82. data/spec/uploader/extension_whitelist_spec.rb +44 -0
  83. data/spec/uploader/mountable_spec.rb +33 -0
  84. data/spec/uploader/paths_spec.rb +22 -0
  85. data/spec/uploader/processing_spec.rb +62 -0
  86. data/spec/uploader/proxy_spec.rb +54 -0
  87. data/spec/uploader/remove_spec.rb +70 -0
  88. data/spec/uploader/store_spec.rb +274 -0
  89. data/spec/uploader/url_spec.rb +87 -0
  90. data/spec/uploader/versions_spec.rb +306 -0
  91. metadata +228 -0
@@ -0,0 +1,145 @@
1
+ # encoding: utf-8
2
+
3
+ require 'fileutils'
4
+ require 'carrierwave/core_ext/blank'
5
+ require 'carrierwave/core_ext/module_setup'
6
+ require 'carrierwave/core_ext/inheritable_attributes'
7
+
8
+ module CarrierWave
9
+
10
+ VERSION = "0.3.2.3"
11
+
12
+ class << self
13
+ attr_accessor :config, :logger
14
+
15
+ def logger
16
+ return @logger if @logger
17
+ require 'logger'
18
+ @logger = Logger.new(STDOUT)
19
+ end
20
+
21
+ ##
22
+ # Generates a unique cache id for use in the caching system
23
+ #
24
+ # === Returns
25
+ #
26
+ # [String] a cache id in the format YYYYMMDD-HHMM-PID-RND
27
+ #
28
+ def generate_cache_id
29
+ Time.now.strftime('%Y%m%d-%H%M') + '-' + Process.pid.to_s + '-' + ("%04d" % rand(9999))
30
+ end
31
+ end
32
+
33
+ class UploadError < StandardError; end
34
+ class NoFileError < UploadError; end
35
+ class FormNotMultipart < UploadError
36
+ def message
37
+ "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."
38
+ end
39
+ end
40
+ class IntegrityError < UploadError; end
41
+ class InvalidParameter < UploadError; end
42
+ # Should be used by methods used as process callbacks.
43
+ class ProcessingError < UploadError; end
44
+
45
+ autoload :SanitizedFile, 'carrierwave/sanitized_file'
46
+ autoload :Mount, 'carrierwave/mount'
47
+ autoload :RMagick, 'carrierwave/processing/rmagick'
48
+ autoload :ImageScience, 'carrierwave/processing/image_science'
49
+
50
+ module Storage
51
+ autoload :Abstract, 'carrierwave/storage/abstract'
52
+ autoload :File, 'carrierwave/storage/file'
53
+ autoload :S3, 'carrierwave/storage/s3'
54
+ end
55
+
56
+ module Uploader
57
+ autoload :Base, 'carrierwave/uploader'
58
+ autoload :Cache, 'carrierwave/uploader/cache'
59
+ autoload :Store, 'carrierwave/uploader/store'
60
+ autoload :Callbacks, 'carrierwave/uploader/callbacks'
61
+ autoload :Processing, 'carrierwave/uploader/processing'
62
+ autoload :Versions, 'carrierwave/uploader/versions'
63
+ autoload :Remove, 'carrierwave/uploader/remove'
64
+ autoload :Paths, 'carrierwave/uploader/paths'
65
+ autoload :ExtensionWhitelist, 'carrierwave/uploader/extension_whitelist'
66
+ autoload :DefaultPath, 'carrierwave/uploader/default_path'
67
+ autoload :Proxy, 'carrierwave/uploader/proxy'
68
+ autoload :Url, 'carrierwave/uploader/url'
69
+ autoload :Mountable, 'carrierwave/uploader/mountable'
70
+ end
71
+
72
+ module Compatibility
73
+ autoload :Paperclip, 'carrierwave/compatibility/paperclip'
74
+ end
75
+
76
+ module Test
77
+ autoload :Matchers, 'carrierwave/test/matchers'
78
+ end
79
+
80
+ end
81
+
82
+ CarrierWave.config = {
83
+ :permissions => 0644,
84
+ :storage => :file,
85
+ :use_cache => true,
86
+ :storage_engines => {
87
+ :file => "CarrierWave::Storage::File",
88
+ :s3 => "CarrierWave::Storage::S3"
89
+ },
90
+ :s3 => {
91
+ :access => :public_read
92
+ },
93
+ :store_dir => 'uploads',
94
+ :cache_dir => 'uploads/tmp',
95
+ :cache_to_cache_dir => true,
96
+ :mount => {
97
+ :ignore_integrity_errors => true,
98
+ :ignore_processing_errors => true,
99
+ :validate_integrity => true,
100
+ :validate_processing => true
101
+ }
102
+ }
103
+
104
+ if defined?(Merb::Plugins)
105
+ CarrierWave.config[:root] = Merb.root
106
+ CarrierWave.config[:public] = Merb.dir_for(:public)
107
+
108
+ Merb::BootLoader.before_app_loads do
109
+ # Set logger
110
+ CarrierWave.logger ||= Merb.logger
111
+ # Setup path for uploaders and load all of them before classes are loaded
112
+ Merb.push_path(:uploaders, Merb.root / 'app' / 'uploaders', '*.rb')
113
+ Dir.glob(File.join(Merb.load_paths[:uploaders])).each {|f| require f }
114
+ end
115
+
116
+ orm_path = File.dirname(__FILE__) / 'carrierwave' / 'orm' / Merb.orm
117
+ require orm_path if File.exist?(orm_path + '.rb')
118
+
119
+ Merb.add_generators File.dirname(__FILE__) / 'generators' / 'uploader_generator'
120
+
121
+ elsif defined?(Rails)
122
+ begin
123
+ CarrierWave.logger = Rails.logger
124
+ rescue
125
+ # Rails < 2.1
126
+ CarrierWave.logger = RAILS_DEFAULT_LOGGER
127
+ end
128
+ CarrierWave.config[:root] = Rails.root
129
+ CarrierWave.config[:public] = File.join(Rails.root, 'public')
130
+
131
+ require File.join(File.dirname(__FILE__), "carrierwave", "orm", 'activerecord')
132
+
133
+ ActiveSupport::Dependencies.load_paths << File.join(Rails.root, "app", "uploaders")
134
+
135
+ elsif defined?(Sinatra)
136
+
137
+ CarrierWave.config[:root] = Sinatra::Application.root
138
+ CarrierWave.config[:public] = Sinatra::Application.public
139
+
140
+ end
141
+
142
+ # MongoMapper is framework agnostic so we could need this in any environment.
143
+ if defined?(MongoMapper)
144
+ require File.join(File.dirname(__FILE__), "carrierwave", "orm", "mongomapper")
145
+ end
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWave
4
+ module Compatibility
5
+
6
+ ##
7
+ # Mix this module into an Uploader to make it mimic Paperclip's storage paths
8
+ # This will make your Uploader use the same default storage path as paperclip
9
+ # does. If you need to override it, you can override the +paperclip_path+ method
10
+ # and provide a Paperclip style path:
11
+ #
12
+ # class MyUploader < CarrierWave::Uploader::Base
13
+ # include CarrierWave::Compatibility::Paperclip
14
+ #
15
+ # def paperclip_path
16
+ # ":rails_root/public/uploads/:id/:attachment/:style_:basename.:extension"
17
+ # end
18
+ # end
19
+ #
20
+ # ---
21
+ #
22
+ # This file contains code taken from Paperclip
23
+ #
24
+ # LICENSE
25
+ #
26
+ # The MIT License
27
+ #
28
+ # Copyright (c) 2008 Jon Yurek and thoughtbot, inc.
29
+ #
30
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
31
+ # of this software and associated documentation files (the "Software"), to deal
32
+ # in the Software without restriction, including without limitation the rights
33
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34
+ # copies of the Software, and to permit persons to whom the Software is
35
+ # furnished to do so, subject to the following conditions:
36
+ #
37
+ # The above copyright notice and this permission notice shall be included in
38
+ # all copies or substantial portions of the Software.
39
+ #
40
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
46
+ # THE SOFTWARE.
47
+ #
48
+ module Paperclip
49
+
50
+ def store_path(for_file=filename)
51
+ path = paperclip_path
52
+ path ||= File.join(*[store_dir, paperclip_style.to_s, for_file].compact)
53
+ interpolate_paperclip_path(path, for_file)
54
+ end
55
+
56
+ def store_dir
57
+ ":rails_root/public/system/:attachment/:id"
58
+ end
59
+
60
+ def paperclip_default_style
61
+ :original
62
+ end
63
+
64
+ def paperclip_path
65
+ end
66
+
67
+ def paperclip_style
68
+ version_name || paperclip_default_style
69
+ end
70
+
71
+ private
72
+
73
+ def interpolate_paperclip_path(path, filename)
74
+ mappings.inject(path) do |agg, pair|
75
+ agg.gsub(":#{pair[0]}", pair[1].call(self, filename).to_s)
76
+ end
77
+ end
78
+
79
+ def mappings
80
+ {
81
+ :rails_root => lambda{|u, f| CarrierWave.config[:root] },
82
+ :rails_env => lambda{|u, f| CarrierWave.config[:env] },
83
+ :class => lambda{|u, f| u.model.class.name.underscore.pluralize},
84
+ :id => lambda{|u, f| u.model.id },
85
+ :id_partition => lambda{|u, f| ("%09d" % u.model.id).scan(/\d{3}/).join("/")},
86
+ :attachment => lambda{|u, f| u.mounted_as.to_s.downcase.pluralize },
87
+ :style => lambda{|u, f| u.paperclip_style },
88
+ :basename => lambda{|u, f| f.gsub(/#{File.extname(f)}$/, "") },
89
+ :extension => lambda{|u, f| File.extname(f).gsub(/^\.+/, "")}
90
+ }
91
+ end
92
+
93
+ end # Paperclip
94
+ end # Compatibility
95
+ end # CarrierWave
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ unless "".respond_to?(:blank?)
4
+ # blank? methods for several different class types
5
+ class Object
6
+ # Returns true if the object is nil or empty (if applicable)
7
+ def blank?
8
+ nil? || (respond_to?(:empty?) && empty?)
9
+ end
10
+ end # class Object
11
+
12
+ class Numeric
13
+ # Numerics can't be blank
14
+ def blank?
15
+ false
16
+ end
17
+ end # class Numeric
18
+
19
+ class NilClass
20
+ # Nils are always blank
21
+ def blank?
22
+ true
23
+ end
24
+ end # class NilClass
25
+
26
+ class TrueClass
27
+ # True is not blank.
28
+ def blank?
29
+ false
30
+ end
31
+ end # class TrueClass
32
+
33
+ class FalseClass
34
+ # False is always blank.
35
+ def blank?
36
+ true
37
+ end
38
+ end # class FalseClass
39
+
40
+ class String
41
+ # Strips out whitespace then tests if the string is empty.
42
+ def blank?
43
+ strip.empty?
44
+ end
45
+ end # class String
46
+ end
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+
3
+ # Stolen from Rails 3
4
+
5
+ # Copyright (c) 2005-2009 David Heinemeier Hansson
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ # Retain for backward compatibility. Methods are now included in Class.
27
+ class Class
28
+ # Defines class-level inheritable attribute reader. Attributes are available to subclasses,
29
+ # each subclass has a copy of parent's attribute.
30
+ #
31
+ # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
32
+ # @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
33
+ #
34
+ # @api public
35
+ #
36
+ # @todo Do we want to block instance_reader via :instance_reader => false
37
+ # @todo It would be preferable that we do something with a Hash passed in
38
+ # (error out or do the same as other methods above) instead of silently
39
+ # moving on). In particular, this makes the return value of this function
40
+ # less useful.
41
+ def extlib_inheritable_reader(*ivars)
42
+ instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
43
+
44
+ ivars.each do |ivar|
45
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
46
+ def self.#{ivar}
47
+ return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
48
+ ivar = superclass.#{ivar}
49
+ return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
50
+ @#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) ? ivar.dup : ivar
51
+ end
52
+ RUBY
53
+ unless instance_reader == false
54
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
55
+ def #{ivar}
56
+ self.class.#{ivar}
57
+ end
58
+ RUBY
59
+ end
60
+ end
61
+ end
62
+
63
+ # Defines class-level inheritable attribute writer. Attributes are available to subclasses,
64
+ # each subclass has a copy of parent's attribute.
65
+ #
66
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
67
+ # define inheritable writer for.
68
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
69
+ # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
70
+ #
71
+ # @api public
72
+ #
73
+ # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
74
+ # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
75
+ def extlib_inheritable_writer(*ivars)
76
+ instance_writer = ivars.pop[:writer] if ivars.last.is_a?(Hash)
77
+ ivars.each do |ivar|
78
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
79
+ def self.#{ivar}=(obj)
80
+ @#{ivar} = obj
81
+ end
82
+ RUBY
83
+ unless instance_writer == false
84
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
85
+ def #{ivar}=(obj) self.class.#{ivar} = obj end
86
+ RUBY
87
+ end
88
+ end
89
+ end
90
+
91
+ # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
92
+ # each subclass has a copy of parent's attribute.
93
+ #
94
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
95
+ # define inheritable accessor for.
96
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
97
+ # @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
98
+ #
99
+ # @api public
100
+ def extlib_inheritable_accessor(*syms)
101
+ extlib_inheritable_reader(*syms)
102
+ extlib_inheritable_writer(*syms)
103
+ end
104
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ # Stolen from Rails 3
4
+
5
+ # Copyright (c) 2005-2009 David Heinemeier Hansson
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ class Module
27
+ attr_accessor :_setup_block
28
+ attr_accessor :_dependencies
29
+
30
+ def setup(&blk)
31
+ @_setup_block = blk
32
+ end
33
+
34
+ def use(mod)
35
+ return if self < mod
36
+
37
+ (mod._dependencies || []).each do |dep|
38
+ use dep
39
+ end
40
+ # raise "Circular dependencies" if self < mod
41
+ include mod
42
+ extend mod.const_get("ClassMethods") if mod.const_defined?("ClassMethods")
43
+ class_eval(&mod._setup_block) if mod._setup_block
44
+ end
45
+
46
+ def depends_on(mod)
47
+ return if self < mod
48
+ @_dependencies ||= []
49
+ @_dependencies << mod
50
+ end
51
+ end
@@ -0,0 +1,332 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWave
4
+
5
+ ##
6
+ # If a Class is extended with this module, it gains the mount_uploader
7
+ # method, which is used for mapping attributes to uploaders and allowing
8
+ # easy assignment.
9
+ #
10
+ # You can use mount_uploader with pretty much any class, however it is
11
+ # intended to be used with some kind of persistent storage, like an ORM.
12
+ # If you want to persist the uploaded files in a particular Class, it
13
+ # needs to implement a `read_uploader` and a `write_uploader` method.
14
+ #
15
+ module Mount
16
+
17
+ ##
18
+ # === Returns
19
+ #
20
+ # [Hash{Symbol => CarrierWave}] what uploaders are mounted on which columns
21
+ #
22
+ def uploaders
23
+ @uploaders ||= {}
24
+ @uploaders = superclass.uploaders.merge(@uploaders)
25
+ rescue NoMethodError
26
+ @uploaders
27
+ end
28
+
29
+ ##
30
+ # === Returns
31
+ #
32
+ # [Hash{Symbol => Hash}] options for mounted uploaders
33
+ #
34
+ def uploader_options
35
+ @uploader_options ||= {}
36
+ @uploader_options = superclass.uploader_options.merge(@uploader_options)
37
+ rescue NoMethodError
38
+ @uploader_options
39
+ end
40
+
41
+ ##
42
+ # Mounts the given uploader on the given column. This means that assigning
43
+ # and reading from the column will upload and retrieve files. Supposing
44
+ # that a User class has an uploader mounted on image, you can assign and
45
+ # retrieve files like this:
46
+ #
47
+ # @user.image # => <Uploader>
48
+ # @user.image = some_file_object
49
+ #
50
+ # @user.store_image!
51
+ #
52
+ # @user.image.url # => '/some_url.png'
53
+ #
54
+ # It is also possible (but not recommended) to ommit the uploader, which
55
+ # will create an anonymous uploader class. Passing a block to this method
56
+ # makes it possible to customize it. This can be convenient for brevity,
57
+ # but if there is any significatnt logic in the uploader, you should do
58
+ # the right thing and have it in its own file.
59
+ #
60
+ # === Added instance methods
61
+ #
62
+ # Supposing a class has used +mount_uploader+ to mount an uploader on a column
63
+ # named +image+, in that case the following methods will be added to the class:
64
+ #
65
+ # [image] Returns an instance of the uploader only if anything has been uploaded
66
+ # [image=] Caches the given file
67
+ #
68
+ # [image_url] Returns the url to the uploaded file
69
+ #
70
+ # [image_cache] Returns a string that identifies the cache location of the file
71
+ # [image_cache=] Retrieves the file from the cache based on the given cache name
72
+ #
73
+ # [image_uploader] Returns an instance of the uploader
74
+ # [image_uploader=] Sets the uploader (be careful!)
75
+ #
76
+ # [remove_image] An attribute reader that can be used with a checkbox to mark a file for removal
77
+ # [remove_image=] An attribute writer that can be used with a checkbox to mark a file for removal
78
+ # [remove_image?] Whether the file should be removed when store_image! is called.
79
+ #
80
+ # [store_image!] Stores a file that has been assigned with +image=+
81
+ # [remove_image!] Removes the uploaded file from the filesystem.
82
+ #
83
+ # [image_integrity_error] Returns an error object if the last file to be assigned caused an integrty error
84
+ # [image_processing_error] Returns an error object if the last file to be assigned caused a processing error
85
+ #
86
+ # [write_image_identifier] Uses the write_uploader method to set the identifier.
87
+ #
88
+ # === Parameters
89
+ #
90
+ # [column (Symbol)] the attribute to mount this uploader on
91
+ # [uploader (CarrierWave::Uploader)] the uploader class to mount
92
+ # [options (Hash{Symbol => Object})] a set of options
93
+ # [&block (Proc)] customize anonymous uploaders
94
+ #
95
+ # === Options
96
+ #
97
+ # [:mount_on => Symbol] if the name of the column to be serialized to differs you can override it using this option
98
+ # [:ignore_integrity_errors => Boolean] if set to true, integrity errors will result in caching failing silently
99
+ # [:ignore_processing_errors => Boolean] if set to true, processing errors will result in caching failing silently
100
+ #
101
+ # === Examples
102
+ #
103
+ # Mounting uploaders on different columns.
104
+ #
105
+ # class Song
106
+ # mount_uploader :lyrics, LyricsUploader
107
+ # mount_uploader :alternative_lyrics, LyricsUploader
108
+ # mount_uploader :file, SongUploader
109
+ # end
110
+ #
111
+ # This will add an anonymous uploader with only the default settings:
112
+ #
113
+ # class Data
114
+ # mount_uploader :csv
115
+ # end
116
+ #
117
+ # this will add an anonymous uploader overriding the store_dir:
118
+ #
119
+ # class Product
120
+ # mount_uploader :blueprint do
121
+ # def store_dir
122
+ # 'blueprints'
123
+ # end
124
+ # end
125
+ # end
126
+ #
127
+ def mount_uploader(column, uploader=nil, options={}, &block)
128
+ unless uploader
129
+ uploader = Class.new(CarrierWave::Uploader::Base)
130
+ uploader.class_eval(&block)
131
+ end
132
+
133
+ uploaders[column.to_sym] = uploader
134
+ uploader_options[column.to_sym] = CarrierWave.config[:mount].merge(options)
135
+
136
+ include CarrierWave::Mount::Extension
137
+
138
+ # Make sure to write over accessors directly defined on the class.
139
+ # Simply super to the included module below.
140
+ class_eval <<-RUBY, __FILE__, __LINE__+1
141
+ def #{column}; super; end
142
+ def #{column}=(new_file); super; end
143
+ RUBY
144
+
145
+ # Mixing this in as a Module instead of class_evaling directly, so we
146
+ # can maintain the ability to super to any of these methods from within
147
+ # the class.
148
+ mod = Module.new
149
+ include mod
150
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
151
+
152
+ def #{column}
153
+ _mounter(:#{column}).uploader
154
+ end
155
+
156
+ def #{column}=(new_file)
157
+ _mounter(:#{column}).cache(new_file)
158
+ end
159
+
160
+ def #{column}?
161
+ !_mounter(:#{column}).blank?
162
+ end
163
+
164
+ def #{column}_url(*args)
165
+ _mounter(:#{column}).url(*args)
166
+ end
167
+
168
+ def #{column}_uploader
169
+ _mounter(:#{column}).uploader
170
+ end
171
+
172
+ def #{column}_uploader=(uploader)
173
+ _mounter(:#{column}).uploader = uploader
174
+ end
175
+
176
+ def #{column}_cache
177
+ _mounter(:#{column}).cache_name
178
+ end
179
+
180
+ def #{column}_cache=(cache_name)
181
+ _mounter(:#{column}).cache_name = cache_name
182
+ end
183
+
184
+ def remove_#{column}
185
+ _mounter(:#{column}).remove
186
+ end
187
+
188
+ def remove_#{column}!
189
+ _mounter(:#{column}).remove!
190
+ end
191
+
192
+ def remove_#{column}=(value)
193
+ _mounter(:#{column}).remove = value
194
+ end
195
+
196
+ def remove_#{column}?
197
+ _mounter(:#{column}).remove?
198
+ end
199
+
200
+ def store_#{column}!
201
+ _mounter(:#{column}).store!
202
+ end
203
+
204
+ def #{column}_integrity_error
205
+ _mounter(:#{column}).integrity_error
206
+ end
207
+
208
+ def #{column}_processing_error
209
+ _mounter(:#{column}).processing_error
210
+ end
211
+
212
+ def write_#{column}_identifier
213
+ _mounter(:#{column}).write_identifier
214
+ end
215
+
216
+ RUBY
217
+
218
+ end
219
+
220
+ module Extension
221
+
222
+ ##
223
+ # overwrite this to read from a serialized attribute
224
+ #
225
+ def read_uploader(column); end
226
+
227
+ ##
228
+ # overwrite this to write to a serialized attribute
229
+ #
230
+ def write_uploader(column, identifier); end
231
+
232
+ private
233
+
234
+ def _mounter(column)
235
+ @_mounters ||= {}
236
+ @_mounters[column] ||= Mounter.new(self, column)
237
+ end
238
+
239
+ end # Extension
240
+
241
+ # this is an internal class, used by CarrierWave::Mount so that
242
+ # we don't pollute the model with a lot of methods.
243
+ class Mounter #:nodoc:
244
+
245
+ attr_reader :column, :record, :options
246
+
247
+ attr_accessor :uploader, :integrity_error, :processing_error, :remove
248
+
249
+ def initialize(record, column, options={})
250
+ @record = record
251
+ @column = column
252
+ @options = record.class.uploader_options[column]
253
+ end
254
+
255
+ def write_identifier
256
+ if remove?
257
+ record.write_uploader(serialization_column, '')
258
+ elsif not uploader.identifier.blank?
259
+ record.write_uploader(serialization_column, uploader.identifier)
260
+ end
261
+ end
262
+
263
+ def identifier
264
+ record.read_uploader(serialization_column)
265
+ end
266
+
267
+ def uploader
268
+ @uploader ||= record.class.uploaders[column].new(record, column)
269
+
270
+ if @uploader.blank? and not identifier.blank?
271
+ @uploader.retrieve_from_store!(identifier)
272
+ end
273
+ return @uploader
274
+ end
275
+
276
+ def cache(new_file)
277
+ uploader.cache!(new_file)
278
+ self.integrity_error = nil
279
+ self.processing_error = nil
280
+ rescue CarrierWave::IntegrityError => e
281
+ self.integrity_error = e
282
+ raise e unless options[:ignore_integrity_errors]
283
+ rescue CarrierWave::ProcessingError => e
284
+ self.processing_error = e
285
+ raise e unless options[:ignore_processing_errors]
286
+ end
287
+
288
+ def cache_name
289
+ uploader.cache_name
290
+ end
291
+
292
+ def cache_name=(cache_name)
293
+ uploader.retrieve_from_cache!(cache_name) unless uploader.cached?
294
+ rescue CarrierWave::InvalidParameter
295
+ end
296
+
297
+ def store!
298
+ unless uploader.blank?
299
+ if remove?
300
+ uploader.remove!
301
+ else
302
+ uploader.store!
303
+ end
304
+ end
305
+ end
306
+
307
+ def url(*args)
308
+ uploader.url(*args)
309
+ end
310
+
311
+ def blank?
312
+ uploader.blank?
313
+ end
314
+
315
+ def remove?
316
+ !remove.blank? and remove !~ /\A0|false$\z/
317
+ end
318
+
319
+ def remove!
320
+ uploader.remove!
321
+ end
322
+
323
+ private
324
+
325
+ def serialization_column
326
+ options[:mount_on] || column
327
+ end
328
+
329
+ end # Mounter
330
+
331
+ end # Mount
332
+ end # CarrierWave