jnicklas-carrierwave 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Generators ADDED
@@ -0,0 +1,4 @@
1
+ scope 'merb-gen' do
2
+ dir = File.join(File.dirname(__FILE__), 'lib', 'generators/')
3
+ Merb.add_generators dir + 'uploader_generator'
4
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 YOUR NAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # CarrierWave
2
+
3
+ This plugin for Merb and Rails provides a simple and extremely flexible way to upload files.
4
+
5
+ ## Getting Started
6
+
7
+ At the moment you are going to have to grab it here from github and install it yourself.
8
+
9
+ In Merb, add it as a dependency to your config/dependencies.rb:
10
+
11
+ dependency 'carrierwave'
12
+
13
+ In Rails, add it to your environment.rb:
14
+
15
+ config.gem "carrierwave"
16
+
17
+ ## Quick Start
18
+
19
+ Start off by generating an uploader:
20
+
21
+ merb-gen uploader Avatar
22
+
23
+ this should give you a file in:
24
+
25
+ app/uploaders/avatar_uploader.rb
26
+
27
+ Check out this file for some hints on how you can customize your uploader. It should look something like this:
28
+
29
+ class AvatarUploader < CarrierWave::Uploader
30
+ storage :file
31
+ end
32
+
33
+ You can use your uploader class to store and retrieve files like this:
34
+
35
+ uploader = AvatarUploader.new
36
+
37
+ uploader.store!(my_file)
38
+
39
+ uploader.retrieve_from_store!('my_file.png')
40
+
41
+ CarrierWave gives you a `store` for permanent storage, and a `cache` for temporary storage. You can use different stores, at the moment a filesystem store and an Amazon S3 store are bundled.
42
+
43
+ Most of the time you are going to want to use CarrierWave together with an ORM. It is quite simple to mount uploaders on columns in your model, so you can simply assign files and get going:
44
+
45
+ ### ActiveRecord
46
+
47
+ First require the activerecord extension:
48
+
49
+ require 'carrierwave/orm/activerecord
50
+
51
+ You don't need to do this if you are using Merb or Rails.
52
+
53
+ Open your model file, and do something like:
54
+
55
+ class User < ActiveRecord::Base
56
+ mount_uploader :avatar, AvatarUploader
57
+ end
58
+
59
+ Now you can upload files!
60
+
61
+ u = User.new
62
+ u.avatar = params[:file]
63
+ u.avatar = File.open('somewhere')
64
+ u.save!
65
+ u.avatar.url # => '/url/to/file.png'
66
+ u.avatar.current_path # => 'path/to/file.png'
67
+
68
+ ### DataMapper
69
+
70
+ First require the activerecord extension:
71
+
72
+ require 'carrierwave/orm/datamapper
73
+
74
+ You don't need to do this if you are using Merb or Rails.
75
+
76
+ Open your model file, and do something like:
77
+
78
+ class User
79
+ include DataMapper::Resource
80
+
81
+ mount_uploader :avatar, AvatarUploader
82
+ end
83
+
84
+ Now you can upload files!
85
+
86
+ u = User.new
87
+ u.avatar = params[:file]
88
+ u.avatar = File.open('somewhere')
89
+ u.save!
90
+ u.avatar.url # => '/url/to/file.png'
91
+ u.avatar.current_path # => 'path/to/file.png'
92
+
93
+ ## Changing the storage directory
94
+
95
+ In order to change where uploaded files are put, just override the `store_dir` method:
96
+
97
+ class MyUploader < CarrierWave::Uploader
98
+ def store_dir
99
+ 'public/my/upload/directory'
100
+ end
101
+ end
102
+
103
+ This works for the file storage as well as Amazon S3.
104
+
105
+ ## Adding versions
106
+
107
+ Often you'll want to add different versions of the same file. The classic example is image thumbnails. There is built in support for this:
108
+
109
+ class MyUploader < CarrierWave::Uploader
110
+ include CarrierWave::RMagick
111
+
112
+ process :resize => [800, 800]
113
+
114
+ version :thumb do
115
+ process :crop_resized => [200,200]
116
+ end
117
+
118
+ end
119
+
120
+ When this uploader is used, an uploaded image would be scaled to be no larger than 800 by 800 pixels. A version called thumb is then created, which is scaled and cropped to exactly 200 by 200 pixels. The uploader could be used like this:
121
+
122
+ uploader = AvatarUploader.new
123
+ uploader.store!(my_file) # size: 1024x768
124
+
125
+ uploader.url # => '/url/to/my_file.png' # size: 800x600
126
+ uploader.thumb.url # => '/url/to/thumb_my_file.png' # size: 200x200
127
+
128
+ One important thing to remember is that process is called *before* versions are created. This can cut down on processing cost.
129
+
130
+ ## What's in that uploader file?
131
+
132
+ The fact that uploaders are separate classes in CarrierWave is a big advantage. What this means for you is:
133
+
134
+ #### Less magic
135
+
136
+ In order to customize your uploader, all you need to do is override methods and use normal, clear and simple Ruby code. That means no `alias_method_chain`'ing to hook into the upload process, no messing around with weird extensions. The code in CarrierWave is very simple and easy because of this.
137
+
138
+ #### Easier to test
139
+
140
+ How do you test file uploads? I always found this ridiculously hard. A separate class means you can test is separately, which is nicer, easier and more maintainable.
141
+
142
+ #### More Flexible
143
+
144
+ Many of the things you can do in CarrierWave are hard, or impossible to do in other file upload plugins, and have previously required you to roll your own. Now you can get all the flexibility without having to write low level stuff.
145
+
146
+ #### Easy to extend
147
+
148
+ CarrierWave has support for a few different image manipulation libraries. These need *no* code to hook into CarrierWave, because they are simple modules. If you want to write your own manipulation library (doesn't need to be for images), you can do the same.
149
+
150
+ ## Using Amazon S3
151
+
152
+ You'll need to configure a bucket, access id and secret key like this:
153
+
154
+ CarrierWave.config[:s3][:access_key_id] = 'xxxxxx'
155
+ CarrierWave.config[:s3][:secret_access_key] = 'xxxxxx'
156
+ CarrierWave.config[:s3][:bucket] = 'name_of_bucket'
157
+
158
+ Do this in an initializer in Rails, and in a `before_app_loads` block in Merb.
159
+
160
+ And then in your uploader, set the storage to :s3
161
+
162
+ class AvatarUploader < CarrierWave::Uploader
163
+ storage :s3
164
+ end
165
+
166
+ That's it! You can still use the `CarrierWave::Uploader#url` method to return the url to the file on Amazon S3
167
+
168
+ ## Using RMagick
169
+
170
+ If you're uploading images, you'll probably want to manipulate them in some way, you might want to create thumbnail images for example. CarrierWave comes with a small library to make manipulating images with RMagick easier. It's not loaded by default so you'll need to require it:
171
+
172
+ require 'carrierwave/processing/rmagick'
173
+
174
+ You'll also need to include it in your Uploader:
175
+
176
+ class AvatarUploader < CarrierWave::Uploader
177
+ include CarrierWave::RMagick
178
+ end
179
+
180
+ The RMagick module gives you a few methods, like `CarrierWave::RMagick#crop_resized` which manipulate the image file in some way. You can set a `process` callback, which will call that method any time a file is uploaded.
181
+
182
+ class AvatarUploader < CarrierWave::Uploader
183
+ include CarrierWave::RMagick
184
+
185
+ process :crop_resized => [200, 200]
186
+ process :convert => 'png'
187
+
188
+ def filename
189
+ super + '.png'
190
+ end
191
+ end
192
+
193
+ Check out the manipulate! method, which makes it easy for you to write your own manipulation methods.
194
+
195
+ ## Using ImageScience
196
+
197
+ ImageScience works the same way as RMagick. As with RMagick you'll need to require it:
198
+
199
+ require 'carrierwave/processing/image_science'
200
+
201
+ And then include it in your model:
202
+
203
+ class AvatarUploader < CarrierWave::Uploader
204
+ include CarrierWave::ImageScience
205
+
206
+ process :crop_resized => [200, 200]
207
+ end
208
+
209
+ ## Read the source
210
+
211
+ CarrierWave is still young, but most of it is pretty well documented. Just dig in and look at the source for more in-depth explanation of what things are doing.
data/Rakefile ADDED
@@ -0,0 +1,96 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ require 'yard'
5
+ require 'spec/rake/spectask'
6
+ require 'cucumber/rake/task'
7
+
8
+ NAME = "carrierwave"
9
+ GEM_VERSION = "0.1"
10
+ AUTHOR = "Jonas Nicklas"
11
+ EMAIL = "jonas.nicklas@gmail.com"
12
+ HOMEPAGE = "http://www.example.com"
13
+ SUMMARY = "Simple and powerful uploads for Merb and Rails"
14
+
15
+ spec = Gem::Specification.new do |s|
16
+ s.rubyforge_project = 'carrierwave'
17
+ s.name = NAME
18
+ s.version = GEM_VERSION
19
+ s.platform = Gem::Platform::RUBY
20
+ s.has_rdoc = true
21
+ s.extra_rdoc_files = ["README.md", "LICENSE", 'TODO']
22
+ s.summary = SUMMARY
23
+ s.description = s.summary
24
+ s.author = AUTHOR
25
+ s.email = EMAIL
26
+ s.homepage = HOMEPAGE
27
+ s.require_path = 'lib'
28
+ s.files = %w(LICENSE Generators README.md Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
29
+
30
+ end
31
+
32
+ # Try these:
33
+ #
34
+ # rake features
35
+ # rake features PROFILE=html
36
+ Cucumber::Rake::Task.new do |t|
37
+ profile = ENV['PROFILE'] || 'default'
38
+ t.cucumber_opts = "--profile #{profile}"
39
+ end
40
+
41
+ YARD::Rake::YardocTask.new do |t|
42
+ t.files = ["README.md", "LICENSE", "TODO", 'lib/carrierwave/**/*.rb']
43
+ end
44
+
45
+ Rake::GemPackageTask.new(spec) do |pkg|
46
+ pkg.gem_spec = spec
47
+ end
48
+
49
+ desc "install the plugin locally"
50
+ task :install => [:package] do
51
+ sh %{#{sudo} gem install #{install_home} pkg/#{NAME}-#{GEM_VERSION} --no-update-sources}
52
+ end
53
+
54
+ desc "create a gemspec file"
55
+ task :make_spec do
56
+ File.open("#{NAME}.gemspec", "w") do |file|
57
+ file.puts spec.to_ruby
58
+ end
59
+ end
60
+
61
+ namespace :jruby do
62
+
63
+ desc "Run :package and install the resulting .gem with jruby"
64
+ task :install => :package do
65
+ sh %{#{sudo} jruby -S gem install #{install_home} pkg/#{NAME}-#{GEM_VERSION}.gem --no-rdoc --no-ri}
66
+ end
67
+
68
+ end
69
+
70
+ file_list = FileList['spec/**/*_spec.rb']
71
+
72
+ desc "Run all examples"
73
+ Spec::Rake::SpecTask.new('spec') do |t|
74
+ t.spec_files = file_list
75
+ end
76
+
77
+ namespace :spec do
78
+ desc "Run all examples with RCov"
79
+ Spec::Rake::SpecTask.new('rcov') do |t|
80
+ t.spec_files = file_list
81
+ t.rcov = true
82
+ t.rcov_dir = "doc/coverage"
83
+ t.rcov_opts = ['--exclude', 'spec']
84
+ end
85
+
86
+ desc "Generate an html report"
87
+ Spec::Rake::SpecTask.new('report') do |t|
88
+ t.spec_files = file_list
89
+ t.spec_opts = ["--format", "html:doc/reports/specs.html"]
90
+ t.fail_on_error = false
91
+ end
92
+
93
+ end
94
+
95
+ desc 'Default: run unit tests.'
96
+ task :default => 'spec'
data/TODO ADDED
File without changes
@@ -0,0 +1,93 @@
1
+ module CarrierWave
2
+
3
+ module Mount
4
+ module Extension
5
+
6
+ private
7
+
8
+ def uploaders
9
+ @uploaders ||= {}
10
+ end
11
+
12
+ def store_uploader!(column)
13
+ if uploaders[column]
14
+ uploaders[column].store!
15
+ write_uploader(column, uploaders[column].identifier)
16
+ end
17
+ end
18
+
19
+ def get_uploader(column)
20
+ return uploaders[column] if uploaders[column]
21
+
22
+ identifier = read_uploader(column)
23
+
24
+ unless identifier.blank?
25
+ uploaders[column] ||= self.class.uploaders[column].new(self, column)
26
+ uploaders[column].retrieve_from_store!(identifier)
27
+ uploaders[column]
28
+ end
29
+ end
30
+
31
+ def set_uploader(column, new_file)
32
+ new_file = CarrierWave::SanitizedFile.new(new_file)
33
+
34
+ unless new_file.empty?
35
+ uploaders[column] ||= self.class.uploaders[column].new(self, column)
36
+ uploaders[column].cache!(new_file)
37
+ end
38
+ end
39
+
40
+ def get_uploader_cache(column)
41
+ uploaders[column].cache_name if uploaders[column]
42
+ end
43
+
44
+ def set_uploader_cache(column, cache_name)
45
+ unless cache_name.blank?
46
+ uploaders[column] ||= self.class.uploaders[column].new(self, column)
47
+ uploaders[column].retrieve_from_cache(cache_name)
48
+ end
49
+ end
50
+
51
+ end # Extension
52
+
53
+ def uploaders
54
+ @uploaders ||= {}
55
+ end
56
+
57
+ def mount_uploader(column, uploader=nil, &block)
58
+ unless uploader
59
+ uploader = Class.new(CarrierWave::Uploader)
60
+ uploader.class_eval(&block)
61
+ end
62
+
63
+ uploaders[column.to_sym] = uploader
64
+
65
+ include CarrierWave::Mount::Extension
66
+
67
+ class_eval <<-EOF, __FILE__, __LINE__+1
68
+ def #{column} # def image
69
+ get_uploader(:#{column}) # get_uploader(:image)
70
+ end # end
71
+ #
72
+ def #{column}=(new_file) # def image=(new_file)
73
+ set_uploader(:#{column}, new_file) # set_uploader(:image, new_file)
74
+ end # end
75
+ #
76
+ def #{column}_cache # def image_cache
77
+ get_uploader_cache(:#{column}) # get_uploader_cache(:image)
78
+ end # end
79
+ #
80
+ def #{column}_cache=(cache_name) # def image_cache=(cache_name)
81
+ set_uploader_cache(:#{column}, cache_name) # set_uploader_cache(:image, cache_name)
82
+ end # end
83
+ #
84
+ def store_#{column}! # def store_image!
85
+ store_uploader!(:#{column}) # store_uploader!(:image)
86
+ end # end
87
+ EOF
88
+
89
+ after_mount(column, uploader) if respond_to?(:after_mount)
90
+ end
91
+
92
+ end # Mount
93
+ end # CarrierWave
@@ -0,0 +1,20 @@
1
+ require 'activerecord'
2
+
3
+ module CarrierWave
4
+ module ActiveRecord
5
+
6
+ include CarrierWave::Mount
7
+
8
+ def after_mount(column, uploader)
9
+ alias_method :read_uploader, :read_attribute
10
+ alias_method :write_uploader, :write_attribute
11
+
12
+ before_save do |record|
13
+ record.send("store_#{column}!")
14
+ end
15
+ end
16
+
17
+ end # ActiveRecord
18
+ end # CarrierWave
19
+
20
+ ActiveRecord::Base.send(:extend, CarrierWave::ActiveRecord)
@@ -0,0 +1,20 @@
1
+ require 'dm-core'
2
+
3
+ module CarrierWave
4
+ module DataMapper
5
+
6
+ include CarrierWave::Mount
7
+
8
+ def after_mount(column, uploader)
9
+ alias_method :read_uploader, :attribute_get
10
+ alias_method :write_uploader, :attribute_set
11
+
12
+ before :save do
13
+ send("store_#{column}!")
14
+ end
15
+ end
16
+
17
+ end # DataMapper
18
+ end # CarrierWave
19
+
20
+ DataMapper::Model.send(:include, CarrierWave::DataMapper)
@@ -0,0 +1,70 @@
1
+ require "image_science"
2
+
3
+ module CarrierWave
4
+ module ImageScience
5
+
6
+ # Resize the image so that it will not exceed the dimensions passed
7
+ # via geometry, geometry should be a string, formatted like '200x100' where
8
+ # the first number is the height and the second is the width
9
+ def resize!( geometry )
10
+ ::ImageScience.with_image(self.current_path) do |img|
11
+ width, height = extract_dimensions(img.width, img.height, geometry)
12
+ img.resize( width, height ) do |file|
13
+ file.save( self.current_path )
14
+ end
15
+ end
16
+ end
17
+
18
+ # Resize and crop the image so that it will have the exact dimensions passed
19
+ # via geometry, geometry should be a string, formatted like '200x100' where
20
+ # the first number is the height and the second is the width
21
+ def crop_resized!( geometry )
22
+ ::ImageScience.with_image(self.current_path) do |img|
23
+ new_width, new_height = geometry.split('x').map{|i| i.to_i }
24
+
25
+ width, height = extract_dimensions_for_crop(img.width, img.height, geometry)
26
+ x_offset, y_offset = extract_placement_for_crop(width, height, geometry)
27
+
28
+ img.resize( width, height ) do |i2|
29
+
30
+ i2.with_crop( x_offset, y_offset, new_width + x_offset, new_height + y_offset) do |file|
31
+ file.save( self.current_path )
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def extract_dimensions(width, height, new_geometry, type = :resize)
40
+ new_width, new_height = convert_geometry(new_geometry)
41
+
42
+ aspect_ratio = width.to_f / height.to_f
43
+ new_aspect_ratio = new_width / new_height
44
+
45
+ if (new_aspect_ratio > aspect_ratio) ^ ( type == :crop ) # Image is too wide, the caret is the XOR operator
46
+ new_width, new_height = [ (new_height * aspect_ratio), new_height]
47
+ else #Image is too narrow
48
+ new_width, new_height = [ new_width, (new_width / aspect_ratio)]
49
+ end
50
+
51
+ [new_width, new_height].collect! { |v| v.round }
52
+ end
53
+
54
+ def extract_dimensions_for_crop(width, height, new_geometry)
55
+ extract_dimensions(width, height, new_geometry, :crop)
56
+ end
57
+
58
+ def extract_placement_for_crop(width, height, new_geometry)
59
+ new_width, new_height = convert_geometry(new_geometry)
60
+ x_offset = (width / 2.0) - (new_width / 2.0)
61
+ y_offset = (height / 2.0) - (new_height / 2.0)
62
+ [x_offset, y_offset].collect! { |v| v.round }
63
+ end
64
+
65
+ def convert_geometry(geometry)
66
+ geometry.split('x').map{|i| i.to_f }
67
+ end
68
+
69
+ end # ImageScience
70
+ end # CarrierWave