oahu-dragonfly 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/.yardopts +24 -0
- data/Gemfile +30 -0
- data/History.md +323 -0
- data/LICENSE +20 -0
- data/README.md +88 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/config.ru +14 -0
- data/docs.watchr +1 -0
- data/dragonfly.gemspec +297 -0
- data/extra_docs/Analysers.md +66 -0
- data/extra_docs/Caching.md +23 -0
- data/extra_docs/Configuration.md +124 -0
- data/extra_docs/Couch.md +49 -0
- data/extra_docs/DataStorage.md +153 -0
- data/extra_docs/Encoding.md +67 -0
- data/extra_docs/GeneralUsage.md +121 -0
- data/extra_docs/Generators.md +60 -0
- data/extra_docs/Heroku.md +50 -0
- data/extra_docs/ImageMagick.md +125 -0
- data/extra_docs/Index.md +33 -0
- data/extra_docs/MimeTypes.md +40 -0
- data/extra_docs/Models.md +272 -0
- data/extra_docs/Mongo.md +45 -0
- data/extra_docs/Processing.md +77 -0
- data/extra_docs/Rack.md +52 -0
- data/extra_docs/Rails2.md +57 -0
- data/extra_docs/Rails3.md +62 -0
- data/extra_docs/Sinatra.md +25 -0
- data/extra_docs/URLs.md +169 -0
- data/features/images.feature +47 -0
- data/features/no_processing.feature +14 -0
- data/features/rails_3.0.5.feature +8 -0
- data/features/steps/common_steps.rb +8 -0
- data/features/steps/dragonfly_steps.rb +66 -0
- data/features/steps/rails_steps.rb +28 -0
- data/features/support/env.rb +13 -0
- data/features/support/setup.rb +32 -0
- data/fixtures/rails_3.0.5/files/app/models/album.rb +7 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/show.html.erb +6 -0
- data/fixtures/rails_3.0.5/files/config/initializers/dragonfly.rb +4 -0
- data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
- data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
- data/fixtures/rails_3.0.5/files/features/step_definitions/image_steps.rb +25 -0
- data/fixtures/rails_3.0.5/files/features/support/paths.rb +17 -0
- data/fixtures/rails_3.0.5/files/features/text_images.feature +7 -0
- data/fixtures/rails_3.0.5/template.rb +20 -0
- data/irbrc.rb +18 -0
- data/lib/dragonfly.rb +55 -0
- data/lib/dragonfly/active_model_extensions.rb +13 -0
- data/lib/dragonfly/active_model_extensions/attachment.rb +250 -0
- data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +148 -0
- data/lib/dragonfly/active_model_extensions/class_methods.rb +95 -0
- data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
- data/lib/dragonfly/active_model_extensions/validations.rb +41 -0
- data/lib/dragonfly/analyser.rb +58 -0
- data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
- data/lib/dragonfly/analysis/image_magick_analyser.rb +6 -0
- data/lib/dragonfly/app.rb +172 -0
- data/lib/dragonfly/config/heroku.rb +19 -0
- data/lib/dragonfly/config/image_magick.rb +6 -0
- data/lib/dragonfly/config/rails.rb +20 -0
- data/lib/dragonfly/configurable.rb +207 -0
- data/lib/dragonfly/core_ext/array.rb +7 -0
- data/lib/dragonfly/core_ext/hash.rb +7 -0
- data/lib/dragonfly/core_ext/object.rb +12 -0
- data/lib/dragonfly/core_ext/string.rb +9 -0
- data/lib/dragonfly/core_ext/symbol.rb +9 -0
- data/lib/dragonfly/data_storage.rb +9 -0
- data/lib/dragonfly/data_storage/couch_data_store.rb +64 -0
- data/lib/dragonfly/data_storage/file_data_store.rb +141 -0
- data/lib/dragonfly/data_storage/mongo_data_store.rb +86 -0
- data/lib/dragonfly/data_storage/s3data_store.rb +145 -0
- data/lib/dragonfly/encoder.rb +13 -0
- data/lib/dragonfly/encoding/image_magick_encoder.rb +6 -0
- data/lib/dragonfly/function_manager.rb +71 -0
- data/lib/dragonfly/generation/image_magick_generator.rb +6 -0
- data/lib/dragonfly/generator.rb +9 -0
- data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
- data/lib/dragonfly/image_magick/analyser.rb +51 -0
- data/lib/dragonfly/image_magick/config.rb +41 -0
- data/lib/dragonfly/image_magick/encoder.rb +57 -0
- data/lib/dragonfly/image_magick/generator.rb +145 -0
- data/lib/dragonfly/image_magick/processor.rb +99 -0
- data/lib/dragonfly/image_magick/utils.rb +72 -0
- data/lib/dragonfly/image_magick_utils.rb +4 -0
- data/lib/dragonfly/job.rb +451 -0
- data/lib/dragonfly/job_builder.rb +39 -0
- data/lib/dragonfly/job_definitions.rb +26 -0
- data/lib/dragonfly/job_endpoint.rb +15 -0
- data/lib/dragonfly/loggable.rb +28 -0
- data/lib/dragonfly/middleware.rb +20 -0
- data/lib/dragonfly/processing/image_magick_processor.rb +6 -0
- data/lib/dragonfly/processor.rb +9 -0
- data/lib/dragonfly/rails/images.rb +27 -0
- data/lib/dragonfly/response.rb +97 -0
- data/lib/dragonfly/routed_endpoint.rb +40 -0
- data/lib/dragonfly/serializer.rb +32 -0
- data/lib/dragonfly/server.rb +113 -0
- data/lib/dragonfly/simple_cache.rb +23 -0
- data/lib/dragonfly/temp_object.rb +175 -0
- data/lib/dragonfly/url_mapper.rb +78 -0
- data/samples/beach.png +0 -0
- data/samples/egg.png +0 -0
- data/samples/round.gif +0 -0
- data/samples/sample.docx +0 -0
- data/samples/taj.jpg +0 -0
- data/spec/dragonfly/active_model_extensions/model_spec.rb +1426 -0
- data/spec/dragonfly/active_model_extensions/spec_helper.rb +91 -0
- data/spec/dragonfly/analyser_spec.rb +123 -0
- data/spec/dragonfly/analysis/file_command_analyser_spec.rb +48 -0
- data/spec/dragonfly/app_spec.rb +135 -0
- data/spec/dragonfly/configurable_spec.rb +461 -0
- data/spec/dragonfly/core_ext/array_spec.rb +19 -0
- data/spec/dragonfly/core_ext/hash_spec.rb +19 -0
- data/spec/dragonfly/core_ext/string_spec.rb +17 -0
- data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
- data/spec/dragonfly/data_storage/couch_data_store_spec.rb +76 -0
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +296 -0
- data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +57 -0
- data/spec/dragonfly/data_storage/s3_data_store_spec.rb +258 -0
- data/spec/dragonfly/data_storage/shared_data_store_examples.rb +77 -0
- data/spec/dragonfly/function_manager_spec.rb +154 -0
- data/spec/dragonfly/hash_with_css_style_keys_spec.rb +24 -0
- data/spec/dragonfly/image_magick/analyser_spec.rb +64 -0
- data/spec/dragonfly/image_magick/encoder_spec.rb +41 -0
- data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
- data/spec/dragonfly/image_magick/processor_spec.rb +233 -0
- data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
- data/spec/dragonfly/job_builder_spec.rb +37 -0
- data/spec/dragonfly/job_definitions_spec.rb +35 -0
- data/spec/dragonfly/job_endpoint_spec.rb +173 -0
- data/spec/dragonfly/job_spec.rb +1046 -0
- data/spec/dragonfly/loggable_spec.rb +80 -0
- data/spec/dragonfly/middleware_spec.rb +47 -0
- data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
- data/spec/dragonfly/serializer_spec.rb +61 -0
- data/spec/dragonfly/server_spec.rb +278 -0
- data/spec/dragonfly/simple_cache_spec.rb +27 -0
- data/spec/dragonfly/temp_object_spec.rb +306 -0
- data/spec/dragonfly/url_mapper_spec.rb +126 -0
- data/spec/functional/deprecations_spec.rb +51 -0
- data/spec/functional/image_magick_app_spec.rb +27 -0
- data/spec/functional/model_urls_spec.rb +85 -0
- data/spec/functional/remote_on_the_fly_spec.rb +51 -0
- data/spec/functional/to_response_spec.rb +31 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/argument_matchers.rb +19 -0
- data/spec/support/image_matchers.rb +47 -0
- data/spec/support/simple_matchers.rb +53 -0
- data/yard/handlers/configurable_attr_handler.rb +38 -0
- data/yard/setup.rb +15 -0
- data/yard/templates/default/fulldoc/html/css/common.css +107 -0
- data/yard/templates/default/layout/html/layout.erb +89 -0
- data/yard/templates/default/module/html/configuration_summary.erb +31 -0
- data/yard/templates/default/module/setup.rb +17 -0
- metadata +544 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
Generators
|
2
|
+
==========
|
3
|
+
|
4
|
+
Unlike processors and encoders, generators create content out of nothing, rather than modifying already existing content, for example text image generation.
|
5
|
+
|
6
|
+
You can register as many generators as you like.
|
7
|
+
|
8
|
+
Given a Dragonfly app
|
9
|
+
|
10
|
+
app = Dragonfly[:images]
|
11
|
+
|
12
|
+
we can get generated content using
|
13
|
+
|
14
|
+
image = app.generate(:some_method, :some => :args)
|
15
|
+
|
16
|
+
where `:some_method` is added by the configured generators.
|
17
|
+
|
18
|
+
ImageMagick Generator
|
19
|
+
---------------------
|
20
|
+
See {file:Imagemagick}.
|
21
|
+
|
22
|
+
Custom Generators
|
23
|
+
-----------------
|
24
|
+
To register a single custom generator:
|
25
|
+
|
26
|
+
app.generator.add :blank_image do |colour|
|
27
|
+
SomeLibrary.create_blank_image(colour) # return a String, Pathname, File or Tempfile
|
28
|
+
end
|
29
|
+
|
30
|
+
app.generate(:blank_image, 'red') # => 'Job' object which we can get data, etc.
|
31
|
+
|
32
|
+
|
33
|
+
Or create a class like the ImageMagick one above, in which case all public methods will be counted as generator methods.
|
34
|
+
|
35
|
+
class RoundedCornerGenerator
|
36
|
+
|
37
|
+
def top_left_corner(opts={})
|
38
|
+
SomeLib.tlc(opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
def bottom_right_corner(opts={})
|
42
|
+
tempfile = Tempfile.new('brc')
|
43
|
+
`some_command -c #{opts[:colour]} -o #{tempfile.path}`
|
44
|
+
tempfile
|
45
|
+
end
|
46
|
+
|
47
|
+
# ...
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def my_helper_method
|
52
|
+
# do stuff
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
app.generator.register(RoundedCornerGenerator)
|
58
|
+
|
59
|
+
app.generate(:top_left_corner, :colour => 'green')
|
60
|
+
app.generate(:bottom_right_corner, :colour => 'mauve')
|
@@ -0,0 +1,50 @@
|
|
1
|
+
Heroku
|
2
|
+
======
|
3
|
+
|
4
|
+
The default configuration won't work out of the box for Heroku, because
|
5
|
+
|
6
|
+
- Heroku doesn't allow saving files to the filesystem (although it does use tempfiles)
|
7
|
+
- We won't need {http://tomayko.com/src/rack-cache/ Rack::Cache} on Heroku because it already uses the caching proxy {http://varnish.projects.linpro.no/ Varnish}, which we can make use of
|
8
|
+
|
9
|
+
Instead of the normal {Dragonfly::DataStorage::FileDataStore FileDataStore}, we can use the {Dragonfly::DataStorage::S3DataStore S3DataStore}.
|
10
|
+
|
11
|
+
Assuming you have an S3 account set up...
|
12
|
+
|
13
|
+
Gem dependencies:
|
14
|
+
|
15
|
+
- aws-s3 (require as aws/s3)
|
16
|
+
- dragonfly
|
17
|
+
|
18
|
+
Initializer (e.g. config/initializers/dragonfly.rb):
|
19
|
+
|
20
|
+
require 'dragonfly'
|
21
|
+
app = Dragonfly[:images]
|
22
|
+
|
23
|
+
app.configure_with(:imagemagick)
|
24
|
+
app.configure_with(:rails)
|
25
|
+
app.configure_with(:heroku, 'my_bucket_name') if Rails.env.production?
|
26
|
+
|
27
|
+
app.define_macro(ActiveRecord::Base, :image_accessor)
|
28
|
+
|
29
|
+
The datastore remains as the {Dragonfly::DataStorage::FileDataStore FileDataStore} for non-production environments.
|
30
|
+
|
31
|
+
environment.rb (application.rb in Rails 3):
|
32
|
+
|
33
|
+
# make sure the last arg is the same as the app's configured prefix
|
34
|
+
config.middleware.insert_before 'Rack::Lock', 'Dragonfly::Middleware', :images, '/media'
|
35
|
+
|
36
|
+
We don't store the S3 access key and secret in the repository, rather we use Heroku's
|
37
|
+
{http://docs.heroku.com/config-vars config variables} using the command line (we only have to do this once).
|
38
|
+
|
39
|
+
From your app's directory:
|
40
|
+
|
41
|
+
heroku config:add S3_KEY=XXXXXXXXX S3_SECRET=XXXXXXXXXX
|
42
|
+
|
43
|
+
Obviously replace 'XXXXXXXXX' with your access key and secret.
|
44
|
+
|
45
|
+
Now you can benefit from super-fast images served straight from Heroku's cache!
|
46
|
+
|
47
|
+
NOTE: HEROKU'S CACHE IS CLEARED EVERY TIME YOU DEPLOY!!!
|
48
|
+
|
49
|
+
If this is an issue, you may want to look into using something like a Memcached add-on, or maybe an after-deploy hook for hitting specific Dragonfly urls you want to cache, etc.
|
50
|
+
It won't be a problem for most sites though.
|
@@ -0,0 +1,125 @@
|
|
1
|
+
ImageMagick
|
2
|
+
===========
|
3
|
+
Either `require 'dragonfly/rails/images'` or `Dragonfly[:images].configure_with(:imagemagick)`
|
4
|
+
gives us an ImageMagick {Dragonfly::ImageMagick::Processor Processor}, {Dragonfly::ImageMagick::Encoder Encoder},
|
5
|
+
{Dragonfly::ImageMagick::Analyser Analyser} and {Dragonfly::ImageMagick::Generator Generator}.
|
6
|
+
|
7
|
+
Given a {Dragonfly::Job Job} object
|
8
|
+
|
9
|
+
image = app.fetch('some/uid')
|
10
|
+
|
11
|
+
...OR a Dragonfly model accessor...
|
12
|
+
|
13
|
+
image = @album.cover_image
|
14
|
+
|
15
|
+
we have the following:
|
16
|
+
|
17
|
+
Shortcuts
|
18
|
+
---------
|
19
|
+
image.thumb('40x30') # same as image.process(:thumb, '40x30')
|
20
|
+
image.jpg # same as image.encode(:jpg)
|
21
|
+
image.png # same as image.encode(:png)
|
22
|
+
image.gif # same as image.encode(:gif)
|
23
|
+
image.convert('-scale 30x30') # same as image.process(:convert, '-scale 30x30')
|
24
|
+
|
25
|
+
`thumb` and `convert` can optionally take a format (e.g. :gif) as the second argument.
|
26
|
+
Bang methods like `image.thumb!('40x30')`, `image.png!` etc. will operate on `self`.
|
27
|
+
|
28
|
+
Below are some examples of geometry strings for `thumb`:
|
29
|
+
|
30
|
+
'400x300' # resize, maintain aspect ratio
|
31
|
+
'400x300!' # force resize, don't maintain aspect ratio
|
32
|
+
'400x' # resize width, maintain aspect ratio
|
33
|
+
'x300' # resize height, maintain aspect ratio
|
34
|
+
'400x300>' # resize only if the image is larger than this
|
35
|
+
'400x300<' # resize only if the image is smaller than this
|
36
|
+
'50x50%' # resize width and height to 50%
|
37
|
+
'400x300^' # resize width, height to minimum 400,300, maintain aspect ratio
|
38
|
+
'2000@' # resize so max area in pixels is 2000
|
39
|
+
'400x300#' # resize, crop if necessary to maintain aspect ratio (centre gravity)
|
40
|
+
'400x300#ne' # as above, north-east gravity
|
41
|
+
'400x300se' # crop, with south-east gravity
|
42
|
+
'400x300+50+100' # crop from the point 50,100 with width, height 400,300
|
43
|
+
|
44
|
+
Processor
|
45
|
+
---------
|
46
|
+
|
47
|
+
image.process(:crop, :width => 40, :height => 50, :x => 20, :y => 30)
|
48
|
+
image.process(:crop, :width => 40, :height => 50, :gravity => 'ne')
|
49
|
+
|
50
|
+
image.process(:flip) # flips it vertically
|
51
|
+
image.process(:flop) # flips it horizontally
|
52
|
+
|
53
|
+
image.process(:greyscale, :depth => 128) # default depth 256
|
54
|
+
|
55
|
+
image.process(:resize, '40x40')
|
56
|
+
image.process(:resize_and_crop, :width => 40, :height=> 50, :gravity => 'ne')
|
57
|
+
|
58
|
+
image.process(:rotate, 45, :background_colour => 'transparent') # default bg black
|
59
|
+
|
60
|
+
The method `thumb` takes a geometry string and calls `resize`, `resize_and_crop` or `crop` accordingly.
|
61
|
+
|
62
|
+
image.process(:thumb, '400x300') # calls resize
|
63
|
+
|
64
|
+
Encoder
|
65
|
+
-------
|
66
|
+
The {Dragonfly::ImageMagick::Encoder ImageMagick Encoder} gives us:
|
67
|
+
|
68
|
+
image.encode(:jpg)
|
69
|
+
image.encode(:gif)
|
70
|
+
image.encode(:png)
|
71
|
+
image.encode(:tiff)
|
72
|
+
|
73
|
+
and various other formats (see {Dragonfly::ImageMagick::Encoder ImageMagick Encoder}).
|
74
|
+
|
75
|
+
You can also pass additional options to the imagemagick command line:
|
76
|
+
|
77
|
+
image.encode(:jpg, '-quality 10')
|
78
|
+
|
79
|
+
Analyser
|
80
|
+
--------
|
81
|
+
The {Dragonfly::ImageMagick::Analyser ImageMagick Analyser} gives us these methods:
|
82
|
+
|
83
|
+
image.width # => 280
|
84
|
+
image.height # => 355
|
85
|
+
image.aspect_ratio # => 0.788732394366197
|
86
|
+
image.portrait? # => true
|
87
|
+
image.landscape? # => false
|
88
|
+
image.depth # => 8
|
89
|
+
image.number_of_colours # => 34703
|
90
|
+
image.format # => :png
|
91
|
+
image.image? # => true - will return true or false for any content
|
92
|
+
|
93
|
+
Generator
|
94
|
+
---------
|
95
|
+
The {Dragonfly::ImageMagick::Generator ImageMagick Generator} gives us these methods:
|
96
|
+
|
97
|
+
image = app.generate(:plain, 600, 400, 'rgba(40,200,30,0.5)')
|
98
|
+
image = app.generate(:plain, 600, 400, '#ccc', :format => :gif)
|
99
|
+
# generate a 600x400 plain image
|
100
|
+
# any css-style colour should work
|
101
|
+
|
102
|
+
image = app.generate(:plasma, 600, 400, :gif) # generate a 600x400 plasma image
|
103
|
+
# last arg defaults to :png
|
104
|
+
|
105
|
+
image = app.generate(:text, "Hello there") # an image of the text "Hello there"
|
106
|
+
|
107
|
+
image = app.generate(:text, "Hello there",
|
108
|
+
:font_size => 30, # defaults to 12
|
109
|
+
:font_family => 'Monaco',
|
110
|
+
:stroke_color => '#ddd',
|
111
|
+
:color => 'red',
|
112
|
+
:font_style => 'italic',
|
113
|
+
:font_stretch => 'expanded',
|
114
|
+
:font_weight => 'bold',
|
115
|
+
:padding => '30 20 10',
|
116
|
+
:background_color => '#efefef', # defaults to transparent
|
117
|
+
:format => :gif # defaults to png
|
118
|
+
)
|
119
|
+
|
120
|
+
Note that the text generation options are meant to resemble css as much as possible. You can also use, for example, `'font-family'` instead of `:font_family`.
|
121
|
+
|
122
|
+
You can use `padding-top`, `padding-left`, etc., as well as the standard css shortcuts for `padding` (it assumes unit is px).
|
123
|
+
|
124
|
+
An alternative for `:font_family` is `:font` (see {http://www.imagemagick.org/script/command-line-options.php#font}), which could be a complete filename.
|
125
|
+
Available fonts are those available on your system.
|
data/extra_docs/Index.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
Dragonfly is a {http://rack.rubyforge.org Rack} framework for on-the-fly image handling in Ruby.
|
2
|
+
|
3
|
+
It is suitable for using with web frameworks such as Rails(2.3 and 3), Sinatra, etc.
|
4
|
+
|
5
|
+
I actually lied about image handling - it can be used for any type of content.
|
6
|
+
|
7
|
+
See the links on the right for more info.
|
8
|
+
|
9
|
+
Installation
|
10
|
+
------------
|
11
|
+
|
12
|
+
gem install dragonfly
|
13
|
+
|
14
|
+
Add-ons
|
15
|
+
-------
|
16
|
+
For third-party add-ons, see [the Add-ons wiki](http://github.com/markevans/dragonfly/wiki/Dragonfly-add-ons)
|
17
|
+
|
18
|
+
Issues
|
19
|
+
------
|
20
|
+
Please use the <a href="http://github.com/markevans/dragonfly/issues">github issue tracker</a>.
|
21
|
+
|
22
|
+
Suggestions/Questions
|
23
|
+
---------------------
|
24
|
+
{http://groups.google.com/group/dragonfly-users}
|
25
|
+
|
26
|
+
Credits
|
27
|
+
-------
|
28
|
+
- [Mark Evans](http://github.com/markevans) (author)
|
29
|
+
- Loads of helpful comments, issues, questions, suggestions and insults from others - you know who you are!
|
30
|
+
|
31
|
+
Copyright
|
32
|
+
---------
|
33
|
+
Copyright (c) 2009-2010 Mark Evans. See LICENSE for details.
|
@@ -0,0 +1,40 @@
|
|
1
|
+
Mime Types
|
2
|
+
==========
|
3
|
+
|
4
|
+
Responses from the Dragonfly app have the HTTP 'Content-Type' header set.
|
5
|
+
|
6
|
+
This is decided by the first found from:
|
7
|
+
|
8
|
+
1. The requested format (e.g. when encode is specifically called)
|
9
|
+
2. The original file extension (you can configure it to ignore this if you wish)
|
10
|
+
3. Analyse the content using the analyser's 'mime_type' method (if exists)
|
11
|
+
4. Analyse the content using the analyser's 'format' method (if exists)
|
12
|
+
5. Use the fallback mime-type (default 'application/octet-stream')
|
13
|
+
|
14
|
+
Note that 'format' means 'jpg', 'png', etc. whereas mime-type would be 'image/jpeg', image/png', etc.
|
15
|
+
Formats are mapped to mime-types using the app's registered list of mime-types.
|
16
|
+
|
17
|
+
Registered mime-types
|
18
|
+
---------------------
|
19
|
+
Registered mime-types default to the list given by Rack (see {http://rack.rubyforge.org/doc/Rack/Mime.html#MIME_TYPES Rack mime-types}).
|
20
|
+
|
21
|
+
To register a mime-type for the format 'egg':
|
22
|
+
|
23
|
+
Dragonfly[:my_app].register_mime_type(:egg, 'fried/egg')
|
24
|
+
|
25
|
+
You can also do this inside a configuration block.
|
26
|
+
|
27
|
+
Analysers
|
28
|
+
---------
|
29
|
+
The {Dragonfly::Analysis::FileCommandAnalyser FileCommandAnalyser} has a `mime_type` method and the
|
30
|
+
{Dragonfly::ImageMagick::Analyser ImageMagick Analyser} has a `format` method.
|
31
|
+
|
32
|
+
These are both registered by default when you use the preconfigured 'dragonfly/rails/images' file.
|
33
|
+
|
34
|
+
Fallback mime-type
|
35
|
+
------------------
|
36
|
+
By default this is 'application/octet-stream', but it can be changed using
|
37
|
+
|
38
|
+
Dragonfly[:my_app].fallback_mime_type = 'meaty/beef'
|
39
|
+
|
40
|
+
This can also be done inside a configuration block.
|
@@ -0,0 +1,272 @@
|
|
1
|
+
Using with Models
|
2
|
+
=================
|
3
|
+
|
4
|
+
You can extend ActiveModel-compatible models to make working with content such as images
|
5
|
+
as easy as working with strings or numbers!
|
6
|
+
|
7
|
+
The examples below assume an initialized Dragonfly app, e.g.
|
8
|
+
|
9
|
+
app = Dragonfly[:images]
|
10
|
+
|
11
|
+
ActiveRecord
|
12
|
+
------------
|
13
|
+
If you've required 'dragonfly/rails/images', then the following step will be already done for you.
|
14
|
+
Otherwise:
|
15
|
+
|
16
|
+
app.define_macro(ActiveRecord::Base, :image_accessor)
|
17
|
+
|
18
|
+
defines the macro `image_accessor` on any ActiveRecord models.
|
19
|
+
|
20
|
+
Mongoid
|
21
|
+
-------
|
22
|
+
|
23
|
+
app.define_macro_on_include(Mongoid::Document, :image_accessor)
|
24
|
+
|
25
|
+
defines the macro `image_accessor` on any models that include `Mongoid::Document`
|
26
|
+
|
27
|
+
CouchRest::Model
|
28
|
+
-------
|
29
|
+
app.define_macro(CouchRest::Model::Base, :image_accessor)
|
30
|
+
|
31
|
+
defines the macro `image_accessor` on any models that include `CouchRest::Model::Base`
|
32
|
+
|
33
|
+
Adding accessors
|
34
|
+
----------------
|
35
|
+
Now we have the method `image_accessor` available in our model classes, which we can use as many times as we like
|
36
|
+
|
37
|
+
class Album
|
38
|
+
image_accessor :cover_image
|
39
|
+
image_accessor :band_photo # Note: this is a different image altogether, not a thumbnail of cover_image
|
40
|
+
end
|
41
|
+
|
42
|
+
Each accessor (e.g. `cover_image`) depends on a string field to actually hold the datastore uid,
|
43
|
+
named by appending the suffix `_uid` (e.g. `cover_image_uid`).
|
44
|
+
|
45
|
+
For example, ActiveRecord models need a migration such as:
|
46
|
+
|
47
|
+
class MyMigration < ActiveRecord::Migration
|
48
|
+
|
49
|
+
def self.up
|
50
|
+
add_column :albums, :cover_image_uid, :string
|
51
|
+
add_column :albums, :band_photo_uid, :string
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.down
|
55
|
+
remove_column :albums, :cover_image_uid
|
56
|
+
remove_column :albums, :band_photo_uid
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
Using the accessors
|
62
|
+
-------------------
|
63
|
+
|
64
|
+
We can use the attribute much like other other model attributes:
|
65
|
+
|
66
|
+
@album = Album.new
|
67
|
+
|
68
|
+
@album.cover_image = "\377???JFIF\000\..." # can assign as a string...
|
69
|
+
@album.cover_image = File.new('path/to/my_image.png') # ... or as a file...
|
70
|
+
@album.cover_image = some_tempfile # ... or as a tempfile...
|
71
|
+
@album.cover_image = @album.band_photo # ... or as another Dragonfly attachment
|
72
|
+
|
73
|
+
@album.cover_image # => #<Dragonfly::ActiveModelExtensions::Attachment:0x103ef6128...
|
74
|
+
|
75
|
+
@album.cover_image = nil
|
76
|
+
@album.cover_image # => nil
|
77
|
+
|
78
|
+
We can inspect properties of the attribute
|
79
|
+
|
80
|
+
@album.cover_image.width # => 280
|
81
|
+
@album.cover_image.height # => 140
|
82
|
+
@album.cover_image.number_of_colours # => 34703
|
83
|
+
@album.cover_image.mime_type # => 'image/png'
|
84
|
+
|
85
|
+
The properties available (i.e. 'width', etc.) come from the app's registered analysers - see {file:Analysers.md Analysers}.
|
86
|
+
|
87
|
+
We can play around with the data
|
88
|
+
|
89
|
+
@album.cover_image.data # => "\377???JFIF\000\..."
|
90
|
+
@album.cover_image.to_file('out.png') # writes to file 'out.png' and returns a readable file object
|
91
|
+
@album.cover_image.tempfile # => #<File:/var/folders/st/strHv74sH044JPabSiODz... a closed Tempfile object
|
92
|
+
@album.cover_image.file # => #<File:/var/folders/st/strHv74sH044JPabSiODz... a readable (open) File object
|
93
|
+
@album.cover_image.file do |f| # Yields an open file object, returns the return value of
|
94
|
+
data = f.read(256) # the block, and closes the file object
|
95
|
+
end
|
96
|
+
@album.cover_image.path # => '/var/folders/st/strHv74sH044JPabSiODz...' i.e. the path of the tempfile
|
97
|
+
@album.cover_image.size # => 134507 (size in bytes)
|
98
|
+
|
99
|
+
We can process the data
|
100
|
+
|
101
|
+
image = @album.cover_image.process(:thumb, '20x20') # returns a 'Job' object, with similar properties
|
102
|
+
image.width # => 20
|
103
|
+
@album.cover_image.width # => 280 (no change)
|
104
|
+
|
105
|
+
The available processing methods available (i.e. 'thumb', etc.) come from the {Dragonfly} app's registered processors - see {file:Processing.md Processing}
|
106
|
+
|
107
|
+
We can encode the data
|
108
|
+
|
109
|
+
image = @album.cover_image.encode(:gif) # returns a 'Job' object, with similar properties
|
110
|
+
image.format # => :gif
|
111
|
+
@album.cover_image.format # => :png (no change)
|
112
|
+
|
113
|
+
The encoding is implemented by the {Dragonfly} app's registered encoders (which will usually just be one) - see {file:Encoding.md Encoding}
|
114
|
+
|
115
|
+
We can use configured shortcuts for processing/encoding, and chain them:
|
116
|
+
|
117
|
+
@album.cover_image.thumb('300x200#ne') # => returns a 'Job' object, with similar properties
|
118
|
+
|
119
|
+
We can chain all these things much like ActiveRecord scopes:
|
120
|
+
|
121
|
+
@album.cover_image.png.thumb('300x200#ne').process(:greyscale).encode(:tiff)
|
122
|
+
|
123
|
+
Because the processing/encoding methods are lazy, no actual processing or encoding is done until a method like `data`, `file`, `to_file`, `width`, etc. is called.
|
124
|
+
You can force the processing to be done if you must by then calling `apply`.
|
125
|
+
|
126
|
+
@album.cover_image.process(:greyscale).apply
|
127
|
+
|
128
|
+
Persisting
|
129
|
+
----------
|
130
|
+
When the model is saved, a before_save callback persists the data to the {Dragonfly::App App}'s configured datastore (see {file:DataStorage.md DataStorage})
|
131
|
+
The uid column is then filled in.
|
132
|
+
|
133
|
+
@album = Album.new
|
134
|
+
|
135
|
+
@album.cover_image_uid # => nil
|
136
|
+
|
137
|
+
@album.cover_image = File.new('path/to/my_image.png')
|
138
|
+
@album.cover_image_uid # => nil
|
139
|
+
|
140
|
+
@album.save
|
141
|
+
@album.cover_image_uid # => '2009/12/05/file.png' (some unique uid, used by the datastore)
|
142
|
+
|
143
|
+
URLs
|
144
|
+
----
|
145
|
+
Once the model is saved, we can get a url for the image (which is served by the Dragonfly {Dragonfly::App App} itself), and for its processed/encoded versions:
|
146
|
+
|
147
|
+
@album.cover_image.url # => '/media/BAhbBlsHOgZmIhgy...'
|
148
|
+
@album.cover_image.thumb('300x200#nw').url # => '/media/BAhbB1sYusgZhgyM...'
|
149
|
+
@album.cover_image.process(:greyscale).jpg.url # => '/media/BnA6CnRodW1iIg8z...'
|
150
|
+
|
151
|
+
Because the processing/encoding methods (including shortcuts like `thumb` and `jpg`) are lazy, no processing or encoding is actually done.
|
152
|
+
|
153
|
+
Validations
|
154
|
+
-----------
|
155
|
+
`validates_presence_of` and `validates_size_of` work out of the box, and Dragonfly also provides `validates_property`.
|
156
|
+
|
157
|
+
class Album
|
158
|
+
|
159
|
+
validates_presence_of :cover_image
|
160
|
+
validates_size_of :cover_image, :maximum => 500.kilobytes
|
161
|
+
|
162
|
+
validates_property :format, :of => :cover_image, :in => [:jpeg, :png, :gif]
|
163
|
+
# ..or..
|
164
|
+
validates_property :mime_type, :of => :cover_image, :in => %w(image/jpeg image/png image/gif)
|
165
|
+
|
166
|
+
validates_property :width, :of => :cover_image, :in => (0..400), :message => "é demais cara!"
|
167
|
+
|
168
|
+
# ...
|
169
|
+
end
|
170
|
+
|
171
|
+
The property argument of `validates_property` will generally be one of the registered analyser properties as described in {file:Analysers.md Analysers}.
|
172
|
+
However it would actually work for arbitrary properties, including those of non-dragonfly model attributes.
|
173
|
+
|
174
|
+
Name and extension
|
175
|
+
------------------
|
176
|
+
If the object assigned is a file, or responds to `original_filename` (as is the case with file uploads in Rails, etc.), then `name` will be set.
|
177
|
+
|
178
|
+
@album.cover_image = File.new('path/to/my_image.png')
|
179
|
+
|
180
|
+
@album.cover_image.name # => 'my_image.png'
|
181
|
+
@album.cover_image.ext # => 'png'
|
182
|
+
|
183
|
+
Meta data
|
184
|
+
---------
|
185
|
+
You can store metadata along with the content data of your attachment:
|
186
|
+
|
187
|
+
@album.cover_image = File.new('path/to/my_image.png')
|
188
|
+
@album.cover_image.meta = {:taken => Date.yesterday}
|
189
|
+
@album.save!
|
190
|
+
|
191
|
+
@album.cover_image.meta # => {:model_class=>"Album",
|
192
|
+
# :model_attachment=>:cover_image,
|
193
|
+
# :taken=>Sat, 11 Sep 2010}
|
194
|
+
|
195
|
+
As you can see, a couple of things are added by the model. You can also access this directly on the {Dragonfly::Job Job} object.
|
196
|
+
|
197
|
+
app.fetch(@album.cover_image_uid).meta # => {:model_class=>"Album", ...}
|
198
|
+
|
199
|
+
"Magic" Attributes
|
200
|
+
------------------
|
201
|
+
An accessor like `cover_image` only relies on the accessor `cover_image_uid` to work.
|
202
|
+
However, in some cases you may want to record some other properties, whether it be for using in queries, or
|
203
|
+
for caching an attribute for performance reasons, etc.
|
204
|
+
|
205
|
+
For the properties `name`, `ext`, `size` and any of the registered analysis methods (e.g. `width`, etc. in the examples above),
|
206
|
+
this is done automatically for you, if the corresponding accessor exists.
|
207
|
+
|
208
|
+
For example - with ActiveRecord, given the migration:
|
209
|
+
|
210
|
+
add_column :albums, :cover_image_width, :integer
|
211
|
+
|
212
|
+
This will automatically be set when assigned:
|
213
|
+
|
214
|
+
@album.cover_image = File.new('path/to/my_image.png')
|
215
|
+
|
216
|
+
@album.cover_image_width # => 280
|
217
|
+
|
218
|
+
They can be used to avoid retrieving data from the datastore for analysis
|
219
|
+
|
220
|
+
@album = Album.first
|
221
|
+
|
222
|
+
@album.cover_image.width # => 280 - no need to retrieve data - takes it from `cover_image_width`
|
223
|
+
@album.cover_image.size # => 134507 - but this needs to retrieve data from the data store, then analyse
|
224
|
+
|
225
|
+
|
226
|
+
Custom Model
|
227
|
+
------------
|
228
|
+
The accessors only require that your model class implements `before_save`, `before_destroy` and `validates_each`
|
229
|
+
(if using validations), as well as of course the `..._uid` field for storing the datastore uid.
|
230
|
+
|
231
|
+
Here is an example of a minimal ActiveModel `Album` model:
|
232
|
+
|
233
|
+
class CustomModel::Base
|
234
|
+
|
235
|
+
extend ActiveModel::Callbacks
|
236
|
+
define_model_callbacks :save, :destroy
|
237
|
+
|
238
|
+
include ActiveModel::Validations # if needed
|
239
|
+
|
240
|
+
def save
|
241
|
+
_run_save_callbacks {
|
242
|
+
# do some saving!
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
def destroy
|
247
|
+
_run_destroy_callbacks {
|
248
|
+
# do some destroying!
|
249
|
+
}
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
|
254
|
+
Define our `image_accessor` macro...
|
255
|
+
|
256
|
+
app.define_macro(CustomModel::Base, :image_accessor)
|
257
|
+
|
258
|
+
...which is used by `Album`:
|
259
|
+
|
260
|
+
class Album < CustomModel::Base
|
261
|
+
|
262
|
+
def cover_image_uid=
|
263
|
+
# ...
|
264
|
+
end
|
265
|
+
|
266
|
+
def cover_image_uid
|
267
|
+
# ...
|
268
|
+
end
|
269
|
+
|
270
|
+
image_accessor :cover_image
|
271
|
+
|
272
|
+
end
|