coryodaniel-milton 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Citrusbyte, LLC
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.markdown ADDED
@@ -0,0 +1,350 @@
1
+ Milton
2
+ ======
3
+
4
+ Milton is an upload and attachment handling plugin for Rails. It is built for
5
+ extensibility and has support for resizing images and Amazon S3 built in.
6
+
7
+ Description
8
+ -----------
9
+
10
+ Milton is an upload handling plugin for Rails. It is meant to handle multiple
11
+ types of file uploads. It includes support for thumbnailing of image uploads,
12
+ and more processors can easily be added for handling other file types. It also
13
+ supports multiple file stores including disk storage and Amazon S3. Milton also
14
+ supports on-demand file processing for prototyping.
15
+
16
+ Getting Started
17
+ ---------------
18
+
19
+ You can get started with Milton with the default settings by simply calling
20
+ `is_attachment` on your ActiveRecord model you'll be handling files with. It is
21
+ expected that your underlying table will have a `filename` (string) column. If
22
+ you'd like you can also define `content_type` (string) and `size` (integer)
23
+ columns to have that info automatically stored as well.
24
+
25
+ ### Example
26
+
27
+ An `Asset` model:
28
+
29
+ class Asset < ActiveRecord::Base
30
+ is_attachment
31
+ end
32
+
33
+ Your new `Asset` form:
34
+
35
+ - form_for Asset.new, :html => { :enctype => 'multipart/form-data' } do |f|
36
+ = f.file_field :file
37
+ = f.submit 'Upload'
38
+
39
+ **Note:** don't forget to add `:html => { :enctype => 'multipart/form-data' }`
40
+
41
+ Your `AssetsController`:
42
+
43
+ class AssetsController < ApplicationController
44
+ def create
45
+ @asset = Asset.create params[:asset]
46
+ end
47
+ end
48
+
49
+ Resizing Images
50
+ ---------------
51
+
52
+ Currently Milton relies on ImageMagick (but not RMagick!). Once you have
53
+ ImageMagick installed, add the `:thumbnail` processor to your configuration
54
+ and define your processing recipes:
55
+
56
+ class Image < ActiveRecord::Base
57
+ is_attachment :recipes => { :thumb => [{ :thumbnail => { :size => '100x100', :crop => true } }] }
58
+ end
59
+
60
+ @image.path => .../000/000/000/001/milton.jpg
61
+ @image.path(:thumb) => .../000/000/000/001/milton.crop_size-100x100.jpg
62
+
63
+ ### Thumbnail Options
64
+
65
+ **Note:** currently the only options supported are `:size` and `:crop`.
66
+
67
+ `:size` takes a geometry string similar to other image-manipulation
68
+ plugins (based off ImageMagick's geometry strings).
69
+
70
+ `:size => '50x50'` will resize the larger dimension down to 50px and maintain aspect ratio (you
71
+ can use `:crop` for forced zoom/cropping).
72
+
73
+ `:size => '50x'` will resize the width to 50px and maintain aspect ratio.
74
+
75
+ `:size => 'x50'` will resize the height to 50px and maintain aspect ratio.
76
+
77
+ Then you can throw in `:crop` to get zoom/cropping functionality:
78
+
79
+ @image.path(:thumbnail => { :size => '50x50', :crop => true })
80
+
81
+ This will create a 50px x 50px version of the image regardless of the source
82
+ aspect-ratio. It will *not* distort the source image, rather it will resize the
83
+ image as close to fitting as possible without distorting, then crop off the
84
+ remainder.
85
+
86
+ By default `:crop` uses a North/Center gravity -- so the remainder will be
87
+ cropped from the bottom and equally from both sides.
88
+
89
+ **Note:** the `:size` option is required when resizing.
90
+
91
+ ### Embedding Images
92
+
93
+ `#path` will always return the full path to the image, in your views
94
+ you probably want to refer to the "public" path -- the portion of the
95
+ path from your `/public` folder up for embedding your images. For
96
+ now there is a helper method that gets attached to your model called
97
+ `#public_path` that simply gives you the path from `public` on.
98
+
99
+ @image.public_path => '/assets/000/000/001/234/milton.jpg'
100
+
101
+ As opposed to:
102
+
103
+ @image.path(:thumb) => '/var/www/site/public/assets/000/000/001/234/milton.jpg'
104
+
105
+ **Note:** if you use the `:file_system_path` option to upload your files to
106
+ somewhere outside of your `public` folder this will no longer work. You can
107
+ pass a different folder to `public_path` to use as an alternate base.
108
+
109
+ @image.public_path(:thumb, 'uploads')
110
+
111
+ Processors
112
+ ----------
113
+
114
+ Processors are defined in recipes and are loaded when Milton is loaded. In the
115
+ above example we're telling Milton to load the thumbnail processor (which comes
116
+ with Milton) and then telling it to pass
117
+ `{ :size => '100x100', :crop => true }` to the thumbnail processor in order to
118
+ create a derivative called `:thumb` for all uploaded Images.
119
+
120
+ More processors can be loaded and combined into the recipe, to be run in the
121
+ order specified (hence the array syntax), i.e.:
122
+
123
+ class Image < ActiveRecord::Base
124
+ is_attachment :recipes => {
125
+ :watermarked_thumb => [{ :watermark => 'Milton' }, { :thumbnail => { :size => '100x100', :crop => true } }]
126
+ }
127
+ end
128
+
129
+ This recipe would create a watermarked version of the originally uploaded file,
130
+ then create a 100x100, cropped thumbnail of the watermarked version.
131
+
132
+ When processors would create the same derivative they use the already created
133
+ derivative and do not recreate it, so if you had:
134
+
135
+ class Image < ActiveRecord::Base
136
+ is_attachment :processors => [ :thumbnail, :watermark ], :recipes => {
137
+ :thumb => [{ :watermark => 'Milton' }, { :thumbnail => { :size => '100x100', :crop => true } }],
138
+ :small => [{ :watermark => 'Milton' }, { :thumbnail => { :size => '250x' } }]
139
+ }
140
+ end
141
+
142
+ The watermarking would only be done once and both thumbnails would be created
143
+ from the same watermarked version of the original image.
144
+
145
+ **Note:** There is no `:watermark` processor, just an example of how processors
146
+ can be combined.
147
+
148
+ Post-processing
149
+ ---------------
150
+
151
+ Post-processing allows you to create derivatives by running processors on
152
+ demand instead of when the file is uploaded. This is particularly useful for
153
+ prototyping or early-on in development when processing options are changing
154
+ rapidly and you want to play with results immediately.
155
+
156
+ You can pass `:postprocessing => true` to `is_attachment` in order to turn on
157
+ post-processing of files. This is recommended only for prototyping as it works
158
+ by checking the existence of the requested derivative every time the derivative
159
+ is requested to determine if it should be processed or not. With disk storage
160
+ in development this can be quite fast, but when using S3 or in production mode
161
+ it is definitely not recommended.
162
+
163
+ Post-processing allows you to pass recipes to `path`, i.e.:
164
+
165
+ @image.path(:thumbnail => { :size => '100x100', :crop => true })
166
+
167
+ If the particular derivative (size of 100x100 and cropped) doesn't exist it
168
+ will be created.
169
+
170
+ **Note:** Without post-processing turned on the call to `path` above would
171
+ still return the same path, it just wouldn't create the underlying file.
172
+
173
+ Amazon S3
174
+ ---------
175
+
176
+ Milton comes with support for Amazon S3. When using S3 uploads and derivatives
177
+ are stored locally in temp files then sent to S3. To use S3 you need to pass a
178
+ few options to `is_attachment`:
179
+
180
+ class Asset < ActiveRecord::Base
181
+ is_attachment :storage => :s3, :storage_options => { :api_access_key => '123', :secret_access_key => '456', :bucket => 'assets' }
182
+ end
183
+
184
+ Where `:api_access_key` and `:secret_access_key` are your API access key and
185
+ secret access key from your Amazon AWS account and `:bucket` is the S3 bucket
186
+ you would like your files to be stored in.
187
+
188
+ When using S3 files are stored in folders according to the associated model's
189
+ ID. So in the above example the URL to a stored file might be:
190
+
191
+ @image.path => http://assets.amazonaws.com/1/milton.jpg
192
+
193
+ Storage Options
194
+ ---------------
195
+
196
+ By default Milton uses your local disk for storing files. Additional storage
197
+ methods can be used by passing the `:storage` option to `is_attachment` (as in
198
+ the S3 example above). Milton comes included with `:s3` and `:disk` storage.
199
+
200
+ Disk Storage Options
201
+ --------------------
202
+
203
+ When using disk storage (default) the following options can be passed in
204
+ `:storage_options`:
205
+
206
+ * `:root` (default `<Rails.root>/public/<table name>`) -- is the root path to where files are/will be stored on your file system. The partitioned portion of the path is then added onto this root to generate the full path. You can do some useful stuff with this like pulling your assets out of /public if you want them to be non-web-accessible.
207
+ * `:chmod` (default `0755`) -- is the mode to set on on created folders and uploaded files.
208
+
209
+ **Note:** If you're using Capistrano for deployment with disk storage remember to put your `:root` path in `shared` and link it up on deploy so you don't lose your uploads between deployments!
210
+
211
+ General Options
212
+ ---------------
213
+
214
+ * `:separator` (default `'.'`) -- is the character used to separate the options from the filename in cached derivative files (i.e. resized images). It will be stripped from the filename of any incoming file.
215
+ * `:replacement` (default `'-'`) -- is the character used to replace `:separator` after stripping it from the incoming filename.
216
+ `:tempfile_path` (default `<Rails.root>/tmp/milton`) -- is the path used for Milton's temporary storage.
217
+
218
+ Installation
219
+ ------------
220
+
221
+ ### Gem
222
+
223
+ $ gem install citrusbyte-milton --source=http://gems.github.com
224
+
225
+ ### Ruby on Rails gem plugin:
226
+
227
+ Add to your environment.rb:
228
+
229
+ config.gem "citrusbyte-milton", :lib => 'milton', :source => 'http://gems.github.com'
230
+
231
+ Then run:
232
+
233
+ $ rake gems:install
234
+
235
+ ### Ruby on Rails plugin
236
+
237
+ script/plugin install git://github.com/citrusbyte/milton.git
238
+
239
+ You will also need to install ImageMagick to use image resizing.
240
+
241
+ Dependencies
242
+ ------------
243
+
244
+ * Ruby on Rails w/ ActiveRecord
245
+ * A filesystem (hopefully this one is covered...)
246
+ * ImageMagick (only if using :thumbnail processor)
247
+ * mimetype-fu (not required, but if available will be used to recognized mime
248
+ types of incoming files)
249
+ * right_aws (if using S3 storage option)
250
+
251
+ Migrating
252
+ ---------
253
+
254
+ ### From 0.2.4
255
+
256
+ Milton is initialized with only `is_attachment` as of 0.3.0, so
257
+ `is_resizeable`, `is_uploadable`, etc... are no longer used.
258
+
259
+ `:postprocessing` is off by default and should only be used in development now.
260
+ Recipes should be used instead of post-processing -- see Processors above.
261
+
262
+ Extended Usage Examples
263
+ -----------------------
264
+
265
+ ### Basic User Avatar
266
+
267
+ class User < ActiveRecord::Base
268
+ has_one :avatar, :dependent => :destroy
269
+ end
270
+
271
+ class Avatar < ActiveRecord::Base
272
+ is_attachment :recipes => { :small => [{ :thumbnail => { :size => '100x100', :crop => true } }] }
273
+ belongs_to :user
274
+ end
275
+
276
+ Allow user to upload an avatar when creating
277
+
278
+ class UsersController < ActiveRecord::Base
279
+ def create
280
+ @user = User.new params[:user]
281
+ @user.avatar = Avatar.new(params[:avatar]) if params[:avatar] && params[:avatar][:file]
282
+
283
+ if @user.save
284
+ ...
285
+ else
286
+ ...
287
+ end
288
+
289
+ ...
290
+ end
291
+ end
292
+
293
+ Allow user to upload a new avatar, note that we don't care about updating files
294
+ in this `has_one` case, we're just gonna set a new relationship (which will
295
+ destroy the existing one)
296
+
297
+ class AvatarsController < ActiveRecord::Base
298
+ def create
299
+ @user = User.find params[:user_id]
300
+
301
+ # setting a has_one on a saved object saves the new related object
302
+ if @user.avatar = Avatar.new(params[:avatar])
303
+ ...
304
+ else
305
+ ...
306
+ end
307
+
308
+ ...
309
+ end
310
+ end
311
+
312
+ User's profile snippet (in Haml)
313
+
314
+ #profile
315
+ = image_tag(@user.avatar.public_path(:small))
316
+ = @user.name
317
+
318
+ Contributors
319
+ ------------
320
+
321
+ * Upload and file handling based on AttachmentPow by Ari Lerner (auser)
322
+ * Cropping and thumbnailing calculations based on code by Nicolás Sanguinetti (foca)
323
+ * Damian Janowski (djanowski)
324
+ * Michel Martens (soveran)
325
+
326
+ License
327
+ -------
328
+
329
+ Copyright (c) 2009 Ben Alavi for Citrusbyte
330
+
331
+ Permission is hereby granted, free of charge, to any person
332
+ obtaining a copy of this software and associated documentation
333
+ files (the "Software"), to deal in the Software without
334
+ restriction, including without limitation the rights to use,
335
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
336
+ copies of the Software, and to permit persons to whom the
337
+ Software is furnished to do so, subject to the following
338
+ conditions:
339
+
340
+ The above copyright notice and this permission notice shall be
341
+ included in all copies or substantial portions of the Software.
342
+
343
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
344
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
345
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
346
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
347
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
348
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
349
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
350
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |task|
5
+ task.libs << 'lib'
6
+ task.libs << "test"
7
+ task.pattern = 'test/**/*_test.rb'
8
+ task.verbose = false
9
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'milton'
data/lib/milton.rb ADDED
@@ -0,0 +1,111 @@
1
+ require 'milton/attachment'
2
+ require 'milton/core/tempfile'
3
+ require 'milton/core/file'
4
+
5
+ module Milton
6
+ # Raised when a file which was expected to exist appears not to exist
7
+ class MissingFileError < StandardError;end;
8
+ class SyscallFailedError < RuntimeError;end;
9
+
10
+ # Some definitions for file semantics used throughout Milton, understanding
11
+ # this will make understanding the code a bit easier and avoid ambiguity:
12
+ #
13
+ # path:
14
+ # the full path to a file or directory in the filesystem
15
+ # /var/log/apache2 or /var/log/apache2/access.log
16
+ # can also be defined as:
17
+ # path == dirname + filename
18
+ # path == dirname + basename + extension
19
+ #
20
+ # dirname:
21
+ # the directory portion of the path to a file or directory, all the chars
22
+ # up to the final /
23
+ # /var/log/apache2 => /var/log
24
+ # /var/log/apache2/ => /var/log/apache2
25
+ # /var/log/apache2/access.log => /var/log/apache2
26
+ #
27
+ # basename:
28
+ # the portion of a filename *with no extension* (ruby's "basename" may or
29
+ # may not have an extension), all the chars after the last / and before
30
+ # the last .
31
+ # /var/log/apache2 => apache2
32
+ # /var/log/apache2/ => nil
33
+ # /var/log/apache2/access.log => access
34
+ # /var/log/apache2/access.2008.log => access.2008
35
+ #
36
+ # extension:
37
+ # the extension portion of a filename w/ no preceding ., all the chars
38
+ # after the final .
39
+ # /var/log/apache2 => nil
40
+ # /var/log/apache2/ => nil
41
+ # /var/log/apache2/access.log => log
42
+ # /var/log/apache2/access.2008.log => log
43
+ #
44
+ # filename:
45
+ # the filename portion of a path w/ extension, all the chars after the
46
+ # final /
47
+ # /var/log/apache2 => apache2
48
+ # /var/log/apache2/ => nil
49
+ # /var/log/apache2/access.log => access.log
50
+ # /var/log/apache2/access.2008.log => access.2008.log
51
+ # can also be defined as:
52
+ # filename == basename + (extension ? '.' + extension : '')
53
+ #
54
+
55
+ # Gives the filename and line number of the method which called the method
56
+ # that invoked #called_by.
57
+ def called_by
58
+ caller[1].gsub(/.*\/(.*):in (.*)/, "\\1:\\2")
59
+ end
60
+ module_function :called_by
61
+
62
+ # Writes the given message to the Rails log at the info level. If given an
63
+ # invoker (just a string) it prepends the message with that. If not given
64
+ # an invoker it outputs the filename and line number which called #log.
65
+ def log(message, invoker=nil)
66
+ invoker ||= Milton.called_by
67
+ Rails.logger.info("[milton] #{invoker}: #{message}")
68
+ end
69
+ module_function :log
70
+
71
+ # Executes the given command, returning the commands output if successful
72
+ # or false if the command failed.
73
+ # Redirects stderr to log/milton.stderr.log in order to examine causes of
74
+ # failure.
75
+ def syscall(command)
76
+ begin
77
+ syscall!(command)
78
+ rescue Milton::SyscallFailedError => e
79
+ return false
80
+ end
81
+ end
82
+ module_function :syscall
83
+
84
+ def syscall!(command)
85
+ log("executing #{command}", invoker = Milton.called_by)
86
+ stdout = %x{#{command} 2>>#{File.join(Rails.root, 'log', 'milton.stderr.log')}}
87
+ unless $?.success?
88
+ e = Milton::SyscallFailedError.new(command)
89
+ log("failed to execute " + e.message, invoker)
90
+ raise e
91
+ end
92
+ stdout
93
+ end
94
+ module_function :syscall!
95
+
96
+ # Wraps +require+ on the given path in a rescue which uses the given
97
+ # message for the resulting LoadError on failure instead of the default
98
+ # one to give the user a better idea of what happened (useful for
99
+ # dynamic +require+)
100
+ def try_require(path, message)
101
+ begin
102
+ require path
103
+ rescue LoadError => e
104
+ raise LoadError.new(message + " (failed to require #{path})") if e.message =~ Regexp.new(path)
105
+ raise e
106
+ end
107
+ end
108
+ module_function :try_require
109
+ end
110
+
111
+ ActiveRecord::Base.extend Milton::Attachment