fog-dragonfly 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fog-dragonfly might be problematic. Click here for more details.
- data/.specopts +2 -0
- data/.yardopts +23 -0
- data/Gemfile +23 -0
- data/Gemfile.rails.2.3.5 +14 -0
- data/History.md +266 -0
- data/LICENSE +20 -0
- data/README.md +88 -0
- data/Rakefile +92 -0
- data/VERSION +1 -0
- data/config.ru +13 -0
- data/docs.watchr +1 -0
- data/dragonfly.gemspec +293 -0
- data/extra_docs/Analysers.md +108 -0
- data/extra_docs/Caching.md +23 -0
- data/extra_docs/Configuration.md +138 -0
- data/extra_docs/DataStorage.md +136 -0
- data/extra_docs/Encoding.md +96 -0
- data/extra_docs/GeneralUsage.md +121 -0
- data/extra_docs/Generators.md +102 -0
- data/extra_docs/Heroku.md +50 -0
- data/extra_docs/Index.md +36 -0
- data/extra_docs/MimeTypes.md +40 -0
- data/extra_docs/Models.md +266 -0
- data/extra_docs/Mongo.md +45 -0
- data/extra_docs/Processing.md +130 -0
- data/extra_docs/Rack.md +52 -0
- data/extra_docs/Rails2.md +55 -0
- data/extra_docs/Rails3.md +62 -0
- data/extra_docs/Sinatra.md +25 -0
- data/extra_docs/URLs.md +169 -0
- data/features/3.0.3.feature +8 -0
- data/features/images.feature +47 -0
- data/features/no_processing.feature +14 -0
- data/features/rails_2.3.5.feature +7 -0
- data/features/steps/common_steps.rb +8 -0
- data/features/steps/dragonfly_steps.rb +66 -0
- data/features/steps/rails_steps.rb +39 -0
- data/features/support/env.rb +40 -0
- data/fixtures/files/app/models/album.rb +3 -0
- data/fixtures/files/app/views/albums/new.html.erb +4 -0
- data/fixtures/files/app/views/albums/show.html.erb +4 -0
- data/fixtures/files/config/initializers/dragonfly.rb +4 -0
- data/fixtures/files/features/manage_album_images.feature +12 -0
- data/fixtures/files/features/step_definitions/image_steps.rb +15 -0
- data/fixtures/files/features/support/paths.rb +15 -0
- data/fixtures/files/features/text_images.feature +7 -0
- data/fixtures/rails_2.3.5/template.rb +10 -0
- data/fixtures/rails_3.0.3/template.rb +20 -0
- data/irbrc.rb +17 -0
- data/lib/dragonfly.rb +45 -0
- data/lib/dragonfly/active_model_extensions.rb +13 -0
- data/lib/dragonfly/active_model_extensions/attachment.rb +169 -0
- data/lib/dragonfly/active_model_extensions/class_methods.rb +45 -0
- data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
- data/lib/dragonfly/active_model_extensions/validations.rb +37 -0
- data/lib/dragonfly/analyser.rb +59 -0
- data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
- data/lib/dragonfly/analysis/image_magick_analyser.rb +47 -0
- data/lib/dragonfly/analysis/r_magick_analyser.rb +63 -0
- data/lib/dragonfly/app.rb +182 -0
- data/lib/dragonfly/config/heroku.rb +19 -0
- data/lib/dragonfly/config/image_magick.rb +41 -0
- data/lib/dragonfly/config/r_magick.rb +46 -0
- data/lib/dragonfly/config/rails.rb +17 -0
- data/lib/dragonfly/configurable.rb +119 -0
- data/lib/dragonfly/core_ext/object.rb +8 -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/file_data_store.rb +114 -0
- data/lib/dragonfly/data_storage/mongo_data_store.rb +82 -0
- data/lib/dragonfly/data_storage/s3data_store.rb +115 -0
- data/lib/dragonfly/encoder.rb +13 -0
- data/lib/dragonfly/encoding/image_magick_encoder.rb +57 -0
- data/lib/dragonfly/encoding/r_magick_encoder.rb +61 -0
- data/lib/dragonfly/function_manager.rb +69 -0
- data/lib/dragonfly/generation/hash_with_css_style_keys.rb +23 -0
- data/lib/dragonfly/generation/image_magick_generator.rb +140 -0
- data/lib/dragonfly/generation/r_magick_generator.rb +155 -0
- data/lib/dragonfly/generator.rb +9 -0
- data/lib/dragonfly/image_magick_utils.rb +81 -0
- data/lib/dragonfly/job.rb +371 -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 +34 -0
- data/lib/dragonfly/processing/image_magick_processor.rb +99 -0
- data/lib/dragonfly/processing/r_magick_processor.rb +126 -0
- data/lib/dragonfly/processor.rb +9 -0
- data/lib/dragonfly/r_magick_utils.rb +48 -0
- data/lib/dragonfly/rails/images.rb +22 -0
- data/lib/dragonfly/response.rb +82 -0
- data/lib/dragonfly/routed_endpoint.rb +40 -0
- data/lib/dragonfly/serializer.rb +32 -0
- data/lib/dragonfly/simple_cache.rb +23 -0
- data/lib/dragonfly/simple_endpoint.rb +63 -0
- data/lib/dragonfly/temp_object.rb +220 -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/argument_matchers.rb +19 -0
- data/spec/dragonfly/active_model_extensions/active_model_setup.rb +97 -0
- data/spec/dragonfly/active_model_extensions/active_record_setup.rb +85 -0
- data/spec/dragonfly/active_model_extensions/model_spec.rb +723 -0
- data/spec/dragonfly/active_model_extensions/spec_helper.rb +11 -0
- data/spec/dragonfly/analyser_spec.rb +123 -0
- data/spec/dragonfly/analysis/file_command_analyser_spec.rb +57 -0
- data/spec/dragonfly/analysis/image_magick_analyser_spec.rb +15 -0
- data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +27 -0
- data/spec/dragonfly/analysis/shared_analyser_spec.rb +51 -0
- data/spec/dragonfly/app_spec.rb +280 -0
- data/spec/dragonfly/config/r_magick_spec.rb +25 -0
- data/spec/dragonfly/configurable_spec.rb +220 -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/data_store_spec.rb +76 -0
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +169 -0
- data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +38 -0
- data/spec/dragonfly/data_storage/s3_data_store_spec.rb +94 -0
- data/spec/dragonfly/deprecation_spec.rb +20 -0
- data/spec/dragonfly/encoding/image_magick_encoder_spec.rb +41 -0
- data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +37 -0
- data/spec/dragonfly/function_manager_spec.rb +154 -0
- data/spec/dragonfly/generation/hash_with_css_style_keys_spec.rb +24 -0
- data/spec/dragonfly/generation/image_magick_generator_spec.rb +12 -0
- data/spec/dragonfly/generation/r_magick_generator_spec.rb +24 -0
- data/spec/dragonfly/generation/shared_generator_spec.rb +91 -0
- data/spec/dragonfly/image_magick_utils_spec.rb +16 -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 +120 -0
- data/spec/dragonfly/job_spec.rb +773 -0
- data/spec/dragonfly/loggable_spec.rb +80 -0
- data/spec/dragonfly/middleware_spec.rb +68 -0
- data/spec/dragonfly/processing/image_magick_processor_spec.rb +29 -0
- data/spec/dragonfly/processing/r_magick_processor_spec.rb +26 -0
- data/spec/dragonfly/processing/shared_processing_spec.rb +215 -0
- data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
- data/spec/dragonfly/serializer_spec.rb +61 -0
- data/spec/dragonfly/simple_cache_spec.rb +27 -0
- data/spec/dragonfly/simple_endpoint_spec.rb +89 -0
- data/spec/dragonfly/temp_object_spec.rb +352 -0
- data/spec/image_matchers.rb +47 -0
- data/spec/simple_matchers.rb +44 -0
- data/spec/spec_helper.rb +58 -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 +87 -0
- data/yard/templates/default/module/html/configuration_summary.erb +31 -0
- data/yard/templates/default/module/setup.rb +17 -0
- metadata +550 -0
@@ -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.
|
data/extra_docs/Index.md
ADDED
@@ -0,0 +1,36 @@
|
|
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
|
+
**NOTE: the API has changed since v0.6.2!
|
10
|
+
[Docs for v0.6.2 are here](v0.6.2/index.html) for a very limited time.**
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
|
15
|
+
gem install dragonfly
|
16
|
+
|
17
|
+
Add-ons
|
18
|
+
-------
|
19
|
+
For third-party add-ons, see [the Add-ons wiki](http://github.com/markevans/dragonfly/wiki/Dragonfly-add-ons)
|
20
|
+
|
21
|
+
Issues
|
22
|
+
------
|
23
|
+
Please use the <a href="http://github.com/markevans/dragonfly/issues">github issue tracker</a>.
|
24
|
+
|
25
|
+
Suggestions/Questions
|
26
|
+
---------------------
|
27
|
+
{http://groups.google.com/group/dragonfly-users}
|
28
|
+
|
29
|
+
Credits
|
30
|
+
-------
|
31
|
+
- [Mark Evans](http://github.com/markevans) (author)
|
32
|
+
- Loads of helpful comments, issues, questions, suggestions and insults from others - you know who you are!
|
33
|
+
|
34
|
+
Copyright
|
35
|
+
---------
|
36
|
+
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::Analysis::ImageMagickAnalyser ImageMagickAnalyser} 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,266 @@
|
|
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
|
+
Adding accessors
|
28
|
+
----------------
|
29
|
+
Now we have the method `image_accessor` available in our model classes, which we can use as many times as we like
|
30
|
+
|
31
|
+
class Album
|
32
|
+
image_accessor :cover_image
|
33
|
+
image_accessor :band_photo # Note: this is a different image altogether, not a thumbnail of cover_image
|
34
|
+
end
|
35
|
+
|
36
|
+
Each accessor (e.g. `cover_image`) depends on a string field to actually hold the datastore uid,
|
37
|
+
named by appending the suffix `_uid` (e.g. `cover_image_uid`).
|
38
|
+
|
39
|
+
For example, ActiveRecord models need a migration such as:
|
40
|
+
|
41
|
+
class MyMigration < ActiveRecord::Migration
|
42
|
+
|
43
|
+
def self.up
|
44
|
+
add_column :albums, :cover_image_uid, :string
|
45
|
+
add_column :albums, :band_photo_uid, :string
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.down
|
49
|
+
remove_column :albums, :cover_image_uid
|
50
|
+
remove_column :albums, :band_photo_uid
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
Using the accessors
|
56
|
+
-------------------
|
57
|
+
|
58
|
+
We can use the attribute much like other other model attributes:
|
59
|
+
|
60
|
+
@album = Album.new
|
61
|
+
|
62
|
+
@album.cover_image = "\377???JFIF\000\..." # can assign as a string...
|
63
|
+
@album.cover_image = File.new('path/to/my_image.png') # ... or as a file...
|
64
|
+
@album.cover_image = some_tempfile # ... or as a tempfile...
|
65
|
+
@album.cover_image = @album.band_photo # ... or as another Dragonfly attachment
|
66
|
+
|
67
|
+
@album.cover_image # => #<Dragonfly::ActiveModelExtensions::Attachment:0x103ef6128...
|
68
|
+
|
69
|
+
@album.cover_image = nil
|
70
|
+
@album.cover_image # => nil
|
71
|
+
|
72
|
+
We can inspect properties of the attribute
|
73
|
+
|
74
|
+
@album.cover_image.width # => 280
|
75
|
+
@album.cover_image.height # => 140
|
76
|
+
@album.cover_image.number_of_colours # => 34703
|
77
|
+
@album.cover_image.mime_type # => 'image/png'
|
78
|
+
|
79
|
+
The properties available (i.e. 'width', etc.) come from the app's registered analysers - see {file:Analysers.md Analysers}.
|
80
|
+
|
81
|
+
We can play around with the data
|
82
|
+
|
83
|
+
@album.cover_image.data # => "\377???JFIF\000\..."
|
84
|
+
@album.cover_image.to_file('out.png') # writes to file 'out.png' and returns a readable file object
|
85
|
+
@album.cover_image.tempfile # => #<File:/var/folders/st/strHv74sH044JPabSiODz... a closed Tempfile object
|
86
|
+
@album.cover_image.file # => #<File:/var/folders/st/strHv74sH044JPabSiODz... a readable (open) File object
|
87
|
+
@album.cover_image.file do |f| # Yields an open file object, returns the return value of
|
88
|
+
data = f.read(256) # the block, and closes the file object
|
89
|
+
end
|
90
|
+
@album.cover_image.path # => '/var/folders/st/strHv74sH044JPabSiODz...' i.e. the path of the tempfile
|
91
|
+
@album.cover_image.size # => 134507 (size in bytes)
|
92
|
+
|
93
|
+
We can process the data
|
94
|
+
|
95
|
+
image = @album.cover_image.process(:thumb, '20x20') # returns a 'Job' object, with similar properties
|
96
|
+
image.width # => 20
|
97
|
+
@album.cover_image.width # => 280 (no change)
|
98
|
+
|
99
|
+
The available processing methods available (i.e. 'thumb', etc.) come from the {Dragonfly} app's registered processors - see {file:Processing.md Processing}
|
100
|
+
|
101
|
+
We can encode the data
|
102
|
+
|
103
|
+
image = @album.cover_image.encode(:gif) # returns a 'Job' object, with similar properties
|
104
|
+
image.format # => :gif
|
105
|
+
@album.cover_image.format # => :png (no change)
|
106
|
+
|
107
|
+
The encoding is implemented by the {Dragonfly} app's registered encoders (which will usually just be one) - see {file:Encoding.md Encoding}
|
108
|
+
|
109
|
+
We can use configured shortcuts for processing/encoding, and chain them:
|
110
|
+
|
111
|
+
@album.cover_image.thumb('300x200#ne') # => returns a 'Job' object, with similar properties
|
112
|
+
|
113
|
+
We can chain all these things much like ActiveRecord scopes:
|
114
|
+
|
115
|
+
@album.cover_image.png.thumb('300x200#ne').process(:greyscale).encode(:tiff)
|
116
|
+
|
117
|
+
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.
|
118
|
+
You can force the processing to be done if you must by then calling `apply`.
|
119
|
+
|
120
|
+
@album.cover_image.process(:greyscale).apply
|
121
|
+
|
122
|
+
Persisting
|
123
|
+
----------
|
124
|
+
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})
|
125
|
+
The uid column is then filled in.
|
126
|
+
|
127
|
+
@album = Album.new
|
128
|
+
|
129
|
+
@album.cover_image_uid # => nil
|
130
|
+
|
131
|
+
@album.cover_image = File.new('path/to/my_image.png')
|
132
|
+
@album.cover_image_uid # => nil
|
133
|
+
|
134
|
+
@album.save
|
135
|
+
@album.cover_image_uid # => '2009/12/05/file.png' (some unique uid, used by the datastore)
|
136
|
+
|
137
|
+
URLs
|
138
|
+
----
|
139
|
+
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:
|
140
|
+
|
141
|
+
@album.cover_image.url # => '/media/BAhbBlsHOgZmIhgy...'
|
142
|
+
@album.cover_image.thumb('300x200#nw').url # => '/media/BAhbB1sYusgZhgyM...'
|
143
|
+
@album.cover_image.process(:greyscale).jpg.url # => '/media/BnA6CnRodW1iIg8z...'
|
144
|
+
|
145
|
+
Because the processing/encoding methods (including shortcuts like `thumb` and `jpg`) are lazy, no processing or encoding is actually done.
|
146
|
+
|
147
|
+
Validations
|
148
|
+
-----------
|
149
|
+
`validates_presence_of` and `validates_size_of` work out of the box, and Dragonfly also provides `validates_property`.
|
150
|
+
|
151
|
+
class Album
|
152
|
+
|
153
|
+
validates_presence_of :cover_image
|
154
|
+
validates_size_of :cover_image, :maximum => 500.kilobytes
|
155
|
+
|
156
|
+
validates_property :format, :of => :cover_image, :in => [:jpeg, :png, :gif]
|
157
|
+
# ..or..
|
158
|
+
validates_property :mime_type, :of => :cover_image, :in => %w(image/jpeg image/png image/gif)
|
159
|
+
|
160
|
+
validates_property :width, :of => :cover_image, :in => (0..400), :message => "é demais cara!"
|
161
|
+
|
162
|
+
# ...
|
163
|
+
end
|
164
|
+
|
165
|
+
The property argument of `validates_property` will generally be one of the registered analyser properties as described in {file:Analysers.md Analysers}.
|
166
|
+
However it would actually work for arbitrary properties, including those of non-dragonfly model attributes.
|
167
|
+
|
168
|
+
Name and extension
|
169
|
+
------------------
|
170
|
+
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.
|
171
|
+
|
172
|
+
@album.cover_image = File.new('path/to/my_image.png')
|
173
|
+
|
174
|
+
@album.cover_image.name # => 'my_image.png'
|
175
|
+
@album.cover_image.ext # => 'png'
|
176
|
+
|
177
|
+
Meta data
|
178
|
+
---------
|
179
|
+
You can store metadata along with the content data of your attachment:
|
180
|
+
|
181
|
+
@album.cover_image = File.new('path/to/my_image.png')
|
182
|
+
@album.cover_image.meta = {:taken => Date.yesterday}
|
183
|
+
@album.save!
|
184
|
+
|
185
|
+
@album.cover_image.meta # => {:model_class=>"Album",
|
186
|
+
# :model_attachment=>:cover_image,
|
187
|
+
# :taken=>Sat, 11 Sep 2010}
|
188
|
+
|
189
|
+
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.
|
190
|
+
|
191
|
+
app.fetch(@album.cover_image_uid).meta # => {:model_class=>"Album", ...}
|
192
|
+
|
193
|
+
"Magic" Attributes
|
194
|
+
------------------
|
195
|
+
An accessor like `cover_image` only relies on the accessor `cover_image_uid` to work.
|
196
|
+
However, in some cases you may want to record some other properties, whether it be for using in queries, or
|
197
|
+
for caching an attribute for performance reasons, etc.
|
198
|
+
|
199
|
+
For the properties `name`, `ext`, `size` and any of the registered analysis methods (e.g. `width`, etc. in the examples above),
|
200
|
+
this is done automatically for you, if the corresponding accessor exists.
|
201
|
+
|
202
|
+
For example - with ActiveRecord, given the migration:
|
203
|
+
|
204
|
+
add_column :albums, :cover_image_width, :integer
|
205
|
+
|
206
|
+
This will automatically be set when assigned:
|
207
|
+
|
208
|
+
@album.cover_image = File.new('path/to/my_image.png')
|
209
|
+
|
210
|
+
@album.cover_image_width # => 280
|
211
|
+
|
212
|
+
They can be used to avoid retrieving data from the datastore for analysis
|
213
|
+
|
214
|
+
@album = Album.first
|
215
|
+
|
216
|
+
@album.cover_image.width # => 280 - no need to retrieve data - takes it from `cover_image_width`
|
217
|
+
@album.cover_image.size # => 134507 - but this needs to retrieve data from the data store, then analyse
|
218
|
+
|
219
|
+
|
220
|
+
Custom Model
|
221
|
+
------------
|
222
|
+
The accessors only require that your model class implements `before_save`, `before_destroy` and `validates_each`
|
223
|
+
(if using validations), as well as of course the `..._uid` field for storing the datastore uid.
|
224
|
+
|
225
|
+
Here is an example of a minimal ActiveModel `Album` model:
|
226
|
+
|
227
|
+
class CustomModel::Base
|
228
|
+
|
229
|
+
extend ActiveModel::Callbacks
|
230
|
+
define_model_callbacks :save, :destroy
|
231
|
+
|
232
|
+
include ActiveModel::Validations # if needed
|
233
|
+
|
234
|
+
def save
|
235
|
+
_run_save_callbacks {
|
236
|
+
# do some saving!
|
237
|
+
}
|
238
|
+
end
|
239
|
+
|
240
|
+
def destroy
|
241
|
+
_run_destroy_callbacks {
|
242
|
+
# do some destroying!
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
Define our `image_accessor` macro...
|
249
|
+
|
250
|
+
app.define_macro(CustomModel::Base, :image_accessor)
|
251
|
+
|
252
|
+
...which is used by `Album`:
|
253
|
+
|
254
|
+
class Album < CustomModel::Base
|
255
|
+
|
256
|
+
def cover_image_uid=
|
257
|
+
# ...
|
258
|
+
end
|
259
|
+
|
260
|
+
def cover_image_uid
|
261
|
+
# ...
|
262
|
+
end
|
263
|
+
|
264
|
+
image_accessor :cover_image
|
265
|
+
|
266
|
+
end
|
data/extra_docs/Mongo.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
Mongo
|
2
|
+
=====
|
3
|
+
Dragonfly can be used with any ActiveModel-compatible model, therefore libraries like [Mongoid](http://mongoid.org) work out of the box.
|
4
|
+
|
5
|
+
Furthermore, Mongo DB has support for storing blob-like objects directly in the database (using MongoDB 'GridFS'),
|
6
|
+
so you can make use of this with the supplied {Dragonfly::DataStorage::MongoDataStore MongoDataStore}.
|
7
|
+
|
8
|
+
For more info about ActiveModel, see {file:Models}.
|
9
|
+
|
10
|
+
For more info about using the Mongo data store, see {file:DataStorage}.
|
11
|
+
|
12
|
+
Example setup in Rails, using Mongoid
|
13
|
+
-------------------------------------
|
14
|
+
In config/initializers/dragonfly.rb:
|
15
|
+
|
16
|
+
require 'dragonfly'
|
17
|
+
|
18
|
+
app = Dragonfly[:images]
|
19
|
+
|
20
|
+
# Get database name from config/mongoid.yml
|
21
|
+
db = YAML.load_file(Rails.root.join('config/mongoid.yml'))[Rails.env]['database']
|
22
|
+
|
23
|
+
# Configure to use ImageMagick, Rails defaults, and the Mongo data store
|
24
|
+
app.configure_with(:imagemagick)
|
25
|
+
app.configure_with(:rails) do |c|
|
26
|
+
c.datastore = Dragonfly::DataStorage::MongoDataStore.new :database => db
|
27
|
+
end
|
28
|
+
|
29
|
+
# Allow all mongoid models to use the macro 'image_accessor'
|
30
|
+
app.define_macro_on_include(Mongoid::Document, :image_accessor)
|
31
|
+
|
32
|
+
# ... any other setup, see Rails docs
|
33
|
+
|
34
|
+
Then in models:
|
35
|
+
|
36
|
+
class Album
|
37
|
+
include Mongoid::Document
|
38
|
+
|
39
|
+
field :cover_image_uid
|
40
|
+
image_accessor :cover_image
|
41
|
+
|
42
|
+
# ...
|
43
|
+
end
|
44
|
+
|
45
|
+
See {file:Models} for more info.
|
@@ -0,0 +1,130 @@
|
|
1
|
+
Processing
|
2
|
+
==========
|
3
|
+
|
4
|
+
Changing data in some way, e.g. resizing an image, comes under the banner of Processing.
|
5
|
+
|
6
|
+
You can register as many processors as you like.
|
7
|
+
|
8
|
+
Let's say we have a Dragonfly app
|
9
|
+
|
10
|
+
app = Dragonfly[:images]
|
11
|
+
|
12
|
+
and an image object (actually a {Dragonfly::Job Job} object)...
|
13
|
+
|
14
|
+
image = app.fetch('some/uid')
|
15
|
+
|
16
|
+
...OR a Dragonfly model accessor...
|
17
|
+
|
18
|
+
image = @album.cover_image
|
19
|
+
|
20
|
+
We can process it using any processing methods that have been registered with the processor.
|
21
|
+
|
22
|
+
ImageMagickProcessor
|
23
|
+
--------------------
|
24
|
+
The {Dragonfly::Processing::ImageMagickProcessor ImageMagickProcessor} is registered by default by
|
25
|
+
the {Dragonfly::Config::ImageMagick ImageMagick configuration} used by 'dragonfly/rails/images'.
|
26
|
+
|
27
|
+
If not already registered:
|
28
|
+
|
29
|
+
app.processor.register(Dragonfly::Processing::ImageMagickProcessor)
|
30
|
+
|
31
|
+
gives us these methods:
|
32
|
+
|
33
|
+
image.process(:thumb, '400x300#') # see below
|
34
|
+
|
35
|
+
image.process(:crop, :width => 40, :height => 50, :x => 20, :y => 30)
|
36
|
+
image.process(:crop, :width => 40, :height => 50, :gravity => 'ne')
|
37
|
+
|
38
|
+
image.process(:flip) # flips it vertically
|
39
|
+
image.process(:flop) # flips it horizontally
|
40
|
+
|
41
|
+
image.process(:greyscale, :depth => 128) # default depth 256
|
42
|
+
|
43
|
+
image.process(:resize, '40x40')
|
44
|
+
image.process(:resize_and_crop, :width => 40, :height=> 50, :gravity => 'ne')
|
45
|
+
|
46
|
+
image.process(:rotate, 45, :background_colour => 'transparent') # default bg black
|
47
|
+
|
48
|
+
The method `thumb` takes a geometry string and calls `resize`, `resize_and_crop` or `crop` accordingly.
|
49
|
+
|
50
|
+
image.process(:thumb, '400x300') # calls resize
|
51
|
+
|
52
|
+
Below are some examples of geometry strings:
|
53
|
+
|
54
|
+
'400x300' # resize, maintain aspect ratio
|
55
|
+
'400x300!' # force resize, don't maintain aspect ratio
|
56
|
+
'400x' # resize width, maintain aspect ratio
|
57
|
+
'x300' # resize height, maintain aspect ratio
|
58
|
+
'400x300>' # resize only if the image is larger than this
|
59
|
+
'400x300<' # resize only if the image is smaller than this
|
60
|
+
'50x50%' # resize width and height to 50%
|
61
|
+
'400x300^' # resize width, height to minimum 400,300, maintain aspect ratio
|
62
|
+
'2000@' # resize so max area in pixels is 2000
|
63
|
+
'400x300#' # resize, crop if necessary to maintain aspect ratio (centre gravity)
|
64
|
+
'400x300#ne' # as above, north-east gravity
|
65
|
+
'400x300se' # crop, with south-east gravity
|
66
|
+
'400x300+50+100' # crop from the point 50,100 with width, height 400,300
|
67
|
+
|
68
|
+
RMagickProcessor
|
69
|
+
----------------
|
70
|
+
The {Dragonfly::Processing::RMagickProcessor RMagickProcessor} uses the {http://rmagick.rubyforge.org RMagick} library and provides the methods
|
71
|
+
`thumb`, `crop`, `flip`, `flop`, `greyscale`, `resize`, `resize_and_crop` and `rotate` like the ImageMagickProcessor above.
|
72
|
+
|
73
|
+
You can tell it not to use the file system when registering it
|
74
|
+
|
75
|
+
app.processor.register(Dragonfly::Processing::RMagickProcessor){|p| p.use_filesystem = false }
|
76
|
+
|
77
|
+
|
78
|
+
Lazy evaluation
|
79
|
+
---------------
|
80
|
+
|
81
|
+
new_image = image.process(:some_method)
|
82
|
+
|
83
|
+
doesn't actually do anything until you call something on the returned {Dragonfly::Job Job} object, like `url`, `data`, etc.
|
84
|
+
|
85
|
+
Bang method
|
86
|
+
-----------
|
87
|
+
|
88
|
+
image.process!(:some_method)
|
89
|
+
|
90
|
+
modifies the image object itself, rather than returning a new object.
|
91
|
+
|
92
|
+
Custom Processors
|
93
|
+
-----------------
|
94
|
+
|
95
|
+
To register a single custom processor:
|
96
|
+
|
97
|
+
app.processor.add :watermark do |temp_object, *args|
|
98
|
+
# use temp_object.data, temp_object.path, temp_object.file, etc.
|
99
|
+
SomeLibrary.add_watermark(temp_object.data, 'some/watermark/file.png')
|
100
|
+
# return a String, File or Tempfile
|
101
|
+
end
|
102
|
+
|
103
|
+
new_image = image.process(:watermark)
|
104
|
+
|
105
|
+
You can create a class like the RMagick one above, in which case all public methods will be counted as processing methods.
|
106
|
+
Each method takes the temp_object as its argument, plus any other args.
|
107
|
+
|
108
|
+
class MyProcessor
|
109
|
+
|
110
|
+
def coolify(temp_object, opts={})
|
111
|
+
SomeLib.coolify(temp_object.data, opts)
|
112
|
+
end
|
113
|
+
|
114
|
+
def uglify(temp_object, ugliness)
|
115
|
+
`uglify -i #{temp_object.path} -u #{ugliness}`
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def my_helper_method
|
121
|
+
# do stuff
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
app.processor.register(MyProcessor)
|
127
|
+
|
128
|
+
new_image = image.coolify(:some => :args)
|
129
|
+
|
130
|
+
new_image = image.uglify(:loads)
|