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 +20 -0
- data/README.markdown +350 -0
- data/Rakefile +9 -0
- data/init.rb +1 -0
- data/lib/milton.rb +111 -0
- data/lib/milton/attachment.rb +202 -0
- data/lib/milton/core/file.rb +22 -0
- data/lib/milton/core/tempfile.rb +44 -0
- data/lib/milton/derivatives/derivative.rb +106 -0
- data/lib/milton/derivatives/thumbnail.rb +39 -0
- data/lib/milton/derivatives/thumbnail/crop_calculator.rb +64 -0
- data/lib/milton/derivatives/thumbnail/image.rb +49 -0
- data/lib/milton/storage/disk_file.rb +84 -0
- data/lib/milton/storage/s3_file.rb +103 -0
- data/lib/milton/storage/stored_file.rb +46 -0
- data/lib/milton/uploading.rb +82 -0
- data/test/fixtures/big-milton.jpg +0 -0
- data/test/fixtures/milton.jpg +0 -0
- data/test/fixtures/mini-milton.jpg +0 -0
- data/test/fixtures/unsanitary .milton.jpg +0 -0
- data/test/milton/attachment_test.rb +359 -0
- data/test/milton/milton_test.rb +21 -0
- data/test/milton/resizing_test.rb +70 -0
- data/test/s3_helper.rb +59 -0
- data/test/schema.rb +13 -0
- data/test/test_helper.rb +78 -0
- metadata +87 -0
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
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
|