dragonfly 0.8.6 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dragonfly might be problematic. Click here for more details.
- data/{.specopts → .rspec} +0 -1
- data/.yardopts +6 -2
- data/Gemfile +14 -13
- data/History.md +47 -9
- data/README.md +25 -5
- data/Rakefile +37 -79
- data/VERSION +1 -1
- data/dragonfly.gemspec +140 -89
- data/extra_docs/Analysers.md +8 -48
- data/extra_docs/Configuration.md +40 -25
- data/extra_docs/Couch.md +49 -0
- data/extra_docs/DataStorage.md +94 -24
- data/extra_docs/Encoding.md +6 -35
- data/extra_docs/ExampleUseCases.md +113 -0
- data/extra_docs/GeneralUsage.md +7 -23
- data/extra_docs/Generators.md +15 -49
- data/extra_docs/Heroku.md +7 -8
- data/extra_docs/ImageMagick.md +126 -0
- data/extra_docs/MimeTypes.md +3 -3
- data/extra_docs/Models.md +163 -0
- data/extra_docs/Mongo.md +1 -4
- data/extra_docs/Processing.md +7 -60
- data/extra_docs/Rails2.md +3 -1
- data/extra_docs/Rails3.md +2 -10
- data/extra_docs/ServingRemotely.md +83 -0
- data/extra_docs/Sinatra.md +3 -3
- data/extra_docs/URLs.md +60 -33
- data/features/rails_3.0.5.feature +8 -0
- data/features/steps/rails_steps.rb +7 -18
- data/features/support/env.rb +10 -37
- data/features/support/setup.rb +32 -0
- data/fixtures/rails_3.0.5/files/app/models/album.rb +5 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
- data/fixtures/{files → rails_3.0.5/files}/app/views/albums/show.html.erb +2 -0
- data/fixtures/{files → rails_3.0.5/files}/config/initializers/dragonfly.rb +0 -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/{files → rails_3.0.5/files}/features/step_definitions/image_steps.rb +11 -1
- data/fixtures/{files → rails_3.0.5/files}/features/support/paths.rb +2 -0
- data/fixtures/{files → rails_3.0.5/files}/features/text_images.feature +0 -0
- data/fixtures/{rails_3.0.3 → rails_3.0.5}/template.rb +2 -2
- data/irbrc.rb +2 -1
- data/lib/dragonfly.rb +7 -0
- data/lib/dragonfly/active_model_extensions/attachment.rb +134 -46
- data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +144 -0
- data/lib/dragonfly/active_model_extensions/class_methods.rb +62 -9
- data/lib/dragonfly/active_model_extensions/instance_methods.rb +2 -2
- data/lib/dragonfly/active_model_extensions/validations.rb +10 -6
- data/lib/dragonfly/analyser.rb +0 -1
- data/lib/dragonfly/analysis/file_command_analyser.rb +1 -1
- data/lib/dragonfly/analysis/image_magick_analyser.rb +2 -43
- data/lib/dragonfly/app.rb +64 -55
- data/lib/dragonfly/config/heroku.rb +1 -1
- data/lib/dragonfly/config/image_magick.rb +2 -37
- data/lib/dragonfly/config/rails.rb +5 -2
- data/lib/dragonfly/configurable.rb +115 -35
- data/lib/dragonfly/core_ext/object.rb +1 -1
- data/lib/dragonfly/core_ext/string.rb +1 -1
- data/lib/dragonfly/data_storage/couch_data_store.rb +84 -0
- data/lib/dragonfly/data_storage/file_data_store.rb +43 -18
- data/lib/dragonfly/data_storage/mongo_data_store.rb +8 -4
- data/lib/dragonfly/data_storage/s3data_store.rb +82 -38
- data/lib/dragonfly/encoding/image_magick_encoder.rb +2 -53
- data/lib/dragonfly/function_manager.rb +4 -2
- data/lib/dragonfly/generation/image_magick_generator.rb +2 -136
- 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 +44 -0
- data/lib/dragonfly/{encoding/r_magick_encoder.rb → image_magick/encoder.rb} +10 -14
- data/lib/dragonfly/image_magick/generator.rb +145 -0
- data/lib/dragonfly/image_magick/processor.rb +104 -0
- data/lib/dragonfly/image_magick/utils.rb +72 -0
- data/lib/dragonfly/image_magick_utils.rb +2 -79
- data/lib/dragonfly/job.rb +152 -90
- data/lib/dragonfly/middleware.rb +5 -19
- data/lib/dragonfly/processing/image_magick_processor.rb +2 -95
- data/lib/dragonfly/rails/images.rb +15 -10
- data/lib/dragonfly/response.rb +26 -12
- data/lib/dragonfly/serializer.rb +1 -4
- data/lib/dragonfly/server.rb +103 -0
- data/lib/dragonfly/temp_object.rb +56 -101
- data/lib/dragonfly/url_mapper.rb +78 -0
- data/spec/dragonfly/active_model_extensions/model_spec.rb +772 -65
- data/spec/dragonfly/active_model_extensions/spec_helper.rb +90 -10
- data/spec/dragonfly/analyser_spec.rb +1 -1
- data/spec/dragonfly/analysis/file_command_analyser_spec.rb +5 -14
- data/spec/dragonfly/app_spec.rb +35 -180
- data/spec/dragonfly/configurable_spec.rb +259 -18
- data/spec/dragonfly/core_ext/string_spec.rb +2 -2
- data/spec/dragonfly/core_ext/symbol_spec.rb +1 -1
- data/spec/dragonfly/data_storage/couch_data_store_spec.rb +84 -0
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +149 -22
- data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +21 -2
- data/spec/dragonfly/data_storage/s3_data_store_spec.rb +207 -43
- data/spec/dragonfly/data_storage/{data_store_spec.rb → shared_data_store_examples.rb} +16 -15
- data/spec/dragonfly/function_manager_spec.rb +2 -2
- data/spec/dragonfly/{generation/hash_with_css_style_keys_spec.rb → hash_with_css_style_keys_spec.rb} +2 -2
- data/spec/dragonfly/{analysis/shared_analyser_spec.rb → image_magick/analyser_spec.rb} +19 -6
- data/spec/dragonfly/{encoding/image_magick_encoder_spec.rb → image_magick/encoder_spec.rb} +2 -2
- data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
- data/spec/dragonfly/{processing/shared_processing_spec.rb → image_magick/processor_spec.rb} +55 -6
- data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
- data/spec/dragonfly/job_builder_spec.rb +1 -1
- data/spec/dragonfly/job_definitions_spec.rb +1 -1
- data/spec/dragonfly/job_endpoint_spec.rb +26 -3
- data/spec/dragonfly/job_spec.rb +426 -208
- data/spec/dragonfly/loggable_spec.rb +2 -2
- data/spec/dragonfly/middleware_spec.rb +5 -26
- data/spec/dragonfly/routed_endpoint_spec.rb +1 -1
- data/spec/dragonfly/serializer_spec.rb +1 -14
- data/spec/dragonfly/server_spec.rb +261 -0
- data/spec/dragonfly/simple_cache_spec.rb +1 -1
- data/spec/dragonfly/temp_object_spec.rb +84 -130
- data/spec/dragonfly/url_mapper_spec.rb +130 -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 +12 -22
- data/spec/{argument_matchers.rb → support/argument_matchers.rb} +0 -0
- data/spec/{image_matchers.rb → support/image_matchers.rb} +4 -4
- data/spec/support/simple_matchers.rb +53 -0
- data/yard/handlers/configurable_attr_handler.rb +2 -2
- data/yard/templates/default/fulldoc/html/css/common.css +12 -10
- data/yard/templates/default/layout/html/layout.erb +6 -0
- metadata +267 -308
- data/Gemfile.rails.2.3.5 +0 -20
- data/features/3.0.3.feature +0 -8
- data/features/rails_2.3.5.feature +0 -7
- data/fixtures/files/app/models/album.rb +0 -3
- data/fixtures/files/app/views/albums/new.html.erb +0 -4
- data/fixtures/files/features/manage_album_images.feature +0 -12
- data/fixtures/rails_2.3.5/template.rb +0 -10
- data/lib/dragonfly/analysis/r_magick_analyser.rb +0 -63
- data/lib/dragonfly/config/r_magick.rb +0 -46
- data/lib/dragonfly/generation/hash_with_css_style_keys.rb +0 -23
- data/lib/dragonfly/generation/r_magick_generator.rb +0 -155
- data/lib/dragonfly/processing/r_magick_processor.rb +0 -126
- data/lib/dragonfly/r_magick_utils.rb +0 -48
- data/lib/dragonfly/simple_endpoint.rb +0 -76
- data/spec/dragonfly/active_model_extensions/active_model_setup.rb +0 -97
- data/spec/dragonfly/active_model_extensions/active_record_setup.rb +0 -85
- data/spec/dragonfly/analysis/image_magick_analyser_spec.rb +0 -15
- data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +0 -31
- data/spec/dragonfly/config/r_magick_spec.rb +0 -29
- data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +0 -41
- data/spec/dragonfly/generation/image_magick_generator_spec.rb +0 -12
- data/spec/dragonfly/generation/r_magick_generator_spec.rb +0 -28
- data/spec/dragonfly/generation/shared_generator_spec.rb +0 -91
- data/spec/dragonfly/image_magick_utils_spec.rb +0 -16
- data/spec/dragonfly/processing/image_magick_processor_spec.rb +0 -29
- data/spec/dragonfly/processing/r_magick_processor_spec.rb +0 -30
- data/spec/dragonfly/simple_endpoint_spec.rb +0 -97
- data/spec/simple_matchers.rb +0 -44
@@ -0,0 +1,113 @@
|
|
1
|
+
Example Use Cases
|
2
|
+
=================
|
3
|
+
Below are a number of examples of uses for Dragonfly which differ slightly from the standard image resizing with model attachments.
|
4
|
+
|
5
|
+
Non-image attachments in Rails
|
6
|
+
------------------------------
|
7
|
+
When using `'dragonfly/rails'images'` or similar configuration, if you're not calling `thumb`, `width`
|
8
|
+
or other image-related methods on your content, then non-image attachments should _just work_.
|
9
|
+
|
10
|
+
class User
|
11
|
+
image_accessor :mugshot
|
12
|
+
end
|
13
|
+
|
14
|
+
user.mugshot = Rails.root.join('some/text_file.txt')
|
15
|
+
user.save!
|
16
|
+
user.mugshot.url # /media/BAsfsdfajkl....
|
17
|
+
|
18
|
+
Furthermore, the imagemagick configuration gives you an `image?` analyser method which always returns a boolean
|
19
|
+
so you can still make a thumbnail if it is an image
|
20
|
+
|
21
|
+
user.mugshot.thumb!('400x300#') if user.mugshot.image?
|
22
|
+
|
23
|
+
`'dragonfly/rails/images'` also gives you a `file_accessor` macro which is actually just the same as `image_accessor`,
|
24
|
+
but a bit more meaningful when not dealing with images
|
25
|
+
|
26
|
+
class User
|
27
|
+
file_accessor :mugshot
|
28
|
+
end
|
29
|
+
|
30
|
+
You can always define your own macro for dealing with non-image attachments:
|
31
|
+
|
32
|
+
Dragonfly[:my_app].define_macro(ActiveRecord::Base, :attachment_accessor)
|
33
|
+
|
34
|
+
class User
|
35
|
+
attachment_accessor :mugshot
|
36
|
+
end
|
37
|
+
|
38
|
+
see {file:Models} for how to define macros with non-ActiveRecord libraries.
|
39
|
+
|
40
|
+
Quick custom image processing in Rails
|
41
|
+
--------------------------------------
|
42
|
+
With the imagemagick configuration, we can easily use `convert` to do some custom processing, e.g. in the view:
|
43
|
+
|
44
|
+
<%= image_tag @user.mugshot.thumb('300x300#').convert('-blur 4x2').url %>
|
45
|
+
|
46
|
+
If we use this a lot, we can define a shortcut for it - in config/initializers/dragonfly.rb:
|
47
|
+
|
48
|
+
Dragonfly[:images].configure do |c|
|
49
|
+
c.job :blurred_square do |size|
|
50
|
+
process :resize_and_crop, :width => size, :height => size
|
51
|
+
process :convert, '-blur 4x2'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
then in the view:
|
56
|
+
|
57
|
+
<%= image_tag @user.mugshot.blurred_square(300).url %>
|
58
|
+
|
59
|
+
See {file:ImageMagick} for more info.
|
60
|
+
|
61
|
+
Using Javascript to generate on-the-fly thumbnails in Rails
|
62
|
+
-----------------------------------------------------------
|
63
|
+
Supposing we have a `Pancake` model with an image attachment
|
64
|
+
|
65
|
+
pancake = Pancake.create! :image => Pathname.new('path/to/pancake.png')
|
66
|
+
|
67
|
+
Setting the attachment sets the uid field (this example uses the {file:DataStorage#File\_datastore FileDataStore})
|
68
|
+
|
69
|
+
pancake.image_uid # '2011/04/27/17_04_32_705_pancake.png'
|
70
|
+
|
71
|
+
We can set up a Dragonfly endpoint in routes.rb for generating thumbnails:
|
72
|
+
|
73
|
+
match '/thumbs/:geometry' => app.endpoint { |params, app|
|
74
|
+
app.fetch(params[:uid]).thumb(params[:geometry])
|
75
|
+
}
|
76
|
+
|
77
|
+
If we have access to the image uid in javascript, we can create the url like so:
|
78
|
+
|
79
|
+
var url = '/thumbs/400x300?uid=' + uid
|
80
|
+
|
81
|
+
Then we can get the content with ajax, create an img tag, etc.
|
82
|
+
|
83
|
+
NB: in the above example we've put the uid in the query string and not the path because the dot in it confuses Rails' pattern recognition.
|
84
|
+
You could always put it in the path and escape/unescape it either side of the request.
|
85
|
+
|
86
|
+
Also javascript's built-in `encodeURIComponent` function may be useful when Rails has difficulty matching routes due to special characters like '#' and '/'.
|
87
|
+
|
88
|
+
Text generation with Sinatra
|
89
|
+
----------------------------
|
90
|
+
We can easily generate on-the-fly text with Sinatra and the {Dragonfly::ImageMagick::Generator ImageMagick Generator}:
|
91
|
+
|
92
|
+
require 'rubygems'
|
93
|
+
require 'sinatra'
|
94
|
+
require 'dragonfly'
|
95
|
+
|
96
|
+
app = Dragonfly[:images].configure_with(:imagemagick)
|
97
|
+
|
98
|
+
get '/:text' do |text|
|
99
|
+
app.generate(:text, text, :font_size => 30).to_response(env)
|
100
|
+
end
|
101
|
+
|
102
|
+
When we visit '/hello!' we get a generated image of the text "hello!".
|
103
|
+
|
104
|
+
See {file:ImageMagick#Generator} for more details.
|
105
|
+
|
106
|
+
Creating a Dragonfly plugin
|
107
|
+
---------------------------
|
108
|
+
You can create custom {file:DataStorage#Custom\_datastore data stores}, {file:Processing#Custom\_Processors processors},
|
109
|
+
{file:Encoding#Custom\_Encoders encoders}, {file:Analysers#Custom\_Analysers analysers} and {file:Generators#Custom\_Generators generators}, and
|
110
|
+
then tie them all together with a {file:Configuration#Custom\_Saved\_Configuration saved configuration}.
|
111
|
+
|
112
|
+
See [Dragonfly-RMagick](http://github.com/markevans/dragonfly-rmagick) for an example.
|
113
|
+
NOTE: you will probably want to create classes and modules in your own namespace, rather than the `Dragonfly` namespace (even though Dragonfly-RMagick uses it).
|
data/extra_docs/GeneralUsage.md
CHANGED
@@ -11,15 +11,19 @@ Each app has a name, and is referred to by that name.
|
|
11
11
|
|
12
12
|
Getting/generating content
|
13
13
|
--------------------------
|
14
|
-
|
14
|
+
A number of methods can be used to get content:
|
15
15
|
|
16
16
|
app.fetch('some_uid') # Fetch from datastore (default filesystem)
|
17
17
|
|
18
18
|
app.fetch_file('~/path/to/file.png') # Fetch from a local file
|
19
19
|
|
20
|
+
app.fetch_url('somewhere.com/img.png') # Fetch from a url (will work with http, https)
|
21
|
+
|
20
22
|
app.generate(:plasma, 400, 300) # Generates using a method from the configured
|
21
23
|
# generator (in this case a plasma image)
|
22
24
|
|
25
|
+
app.create("CONTENT") # Can pass in a String, Pathname, File or Tempfile
|
26
|
+
|
23
27
|
These all return {Dragonfly::Job Job} objects. These objects are lazy - they don't do any fetching/generating until
|
24
28
|
some other method is called on them.
|
25
29
|
|
@@ -44,6 +48,7 @@ We can get the data a number of ways...
|
|
44
48
|
We can get its url...
|
45
49
|
|
46
50
|
image.url # => "/media/BAhbBlsHOgZmIg9hc..."
|
51
|
+
# this won't work if we've used create to get the content
|
47
52
|
|
48
53
|
We can analyse it (see {file:Analysers} for more info) ...
|
49
54
|
|
@@ -59,18 +64,7 @@ We can encode it (see {file:Encoding} for more info) ...
|
|
59
64
|
|
60
65
|
Chaining
|
61
66
|
--------
|
62
|
-
Because the methods
|
63
|
-
|
64
|
-
- `fetch`
|
65
|
-
|
66
|
-
- `fetch_file`
|
67
|
-
|
68
|
-
- `generate`
|
69
|
-
|
70
|
-
- `process`
|
71
|
-
|
72
|
-
- `encode`
|
73
|
-
|
67
|
+
Because the methods `fetch`, `fetch_file`, `fetch_url`, `generate`, `create`, `process` and `encode`
|
74
68
|
all return {Dragonfly::Job Job} objects, we can chain them as much as we want...
|
75
69
|
|
76
70
|
image = app.fetch('some_uid').process(:greyscale).process(:thumb, '40x20#').encode(:gif)
|
@@ -109,13 +103,3 @@ To define this shortcut:
|
|
109
103
|
end
|
110
104
|
# ...
|
111
105
|
end
|
112
|
-
|
113
|
-
The {Dragonfly::Config::ImageMagick ImageMagick} configuration comes with the pre-defined shortcuts:
|
114
|
-
|
115
|
-
image.thumb('40x30') # same as image.process(:thumb, '40x30')
|
116
|
-
image.jpg # same as image.encode(:jpg)
|
117
|
-
image.png # same as image.encode(:png)
|
118
|
-
image.gif # same as image.encode(:gif)
|
119
|
-
image.convert('-scale 30x30') # same as image.process(:convert, '-scale 30x30')
|
120
|
-
|
121
|
-
`thumb` and `convert` can optionally take a format (e.g. :gif) as the second argument.
|
data/extra_docs/Generators.md
CHANGED
@@ -15,62 +15,19 @@ we can get generated content using
|
|
15
15
|
|
16
16
|
where `:some_method` is added by the configured generators.
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
{Dragonfly::Config::ImageMagick ImageMagick configuration} used by 'dragonfly/rails/images'.
|
22
|
-
|
23
|
-
If not already registered:
|
24
|
-
|
25
|
-
app.generator.register(Dragonfly::Generation::ImageMagickGenerator)
|
26
|
-
|
27
|
-
gives us these methods:
|
28
|
-
|
29
|
-
image = app.generate(:plasma, 600, 400, :gif) # generate a 600x400 plasma image
|
30
|
-
# last arg defaults to :png
|
31
|
-
|
32
|
-
image = app.generate(:text, "Hello there") # an image of the text "Hello there"
|
33
|
-
|
34
|
-
image = app.generate(:text, "Hello there",
|
35
|
-
:font_size => 30, # defaults to 12
|
36
|
-
:font_family => 'Monaco',
|
37
|
-
:stroke_color => '#ddd',
|
38
|
-
:color => 'red',
|
39
|
-
:font_style => 'italic',
|
40
|
-
:font_stretch => 'expanded',
|
41
|
-
:font_weight => 'bold',
|
42
|
-
:padding => '30 20 10',
|
43
|
-
:background_color => '#efefef', # defaults to transparent
|
44
|
-
:format => :gif # defaults to png
|
45
|
-
)
|
46
|
-
|
47
|
-
Note that the options are meant to resemble css as much as possible. You can also use, for example, `'font-family'` instead of `:font_family`.
|
48
|
-
|
49
|
-
You can use `padding-top`, `padding-left`, etc., as well as the standard css shortcuts for `padding` (it assumes unit is px).
|
50
|
-
|
51
|
-
An alternative for `:font_family` is `:font` (see {http://www.imagemagick.org/script/command-line-options.php#font}), which could be a complete filename.
|
52
|
-
Available fonts are those available on your system.
|
53
|
-
|
54
|
-
RMagickGenerator
|
55
|
-
----------------
|
56
|
-
The {Dragonfly::Generation::RMagickGenerator RMagickGenerator} gives you `plasma` and `text` like the imagemagick generator above, using the
|
57
|
-
{http://rmagick.rubyforge.org RMagick} library.
|
58
|
-
|
59
|
-
You can tell it not to use the file system when registering it
|
60
|
-
|
61
|
-
app.generator.register(Dragonfly::Generation::RMagickGenerator){|g| g.use_filesystem = false }
|
62
|
-
|
18
|
+
ImageMagick Generator
|
19
|
+
---------------------
|
20
|
+
See {file:Imagemagick}.
|
63
21
|
|
64
22
|
Custom Generators
|
65
23
|
-----------------
|
66
24
|
To register a single custom generator:
|
67
25
|
|
68
|
-
app.generator.add :
|
69
|
-
SomeLibrary.
|
26
|
+
app.generator.add :triangle do |height|
|
27
|
+
SomeLibrary.create_triangle(height) # return a String, Pathname, File or Tempfile
|
70
28
|
end
|
71
29
|
|
72
|
-
app.generate(:
|
73
|
-
|
30
|
+
app.generate(:triangle, 10) # => 'Job' object which we can get data, etc.
|
74
31
|
|
75
32
|
Or create a class like the ImageMagick one above, in which case all public methods will be counted as generator methods.
|
76
33
|
|
@@ -100,3 +57,12 @@ Or create a class like the ImageMagick one above, in which case all public metho
|
|
100
57
|
|
101
58
|
app.generate(:top_left_corner, :colour => 'green')
|
102
59
|
app.generate(:bottom_right_corner, :colour => 'mauve')
|
60
|
+
|
61
|
+
You can also return meta data like name and format if you return an array from the generator
|
62
|
+
|
63
|
+
app.generator.add :triangle do |height|
|
64
|
+
[
|
65
|
+
SomeLibrary.create_triangle(height),
|
66
|
+
{:name => 'triangle.png', :format => :png}
|
67
|
+
]
|
68
|
+
end
|
data/extra_docs/Heroku.md
CHANGED
@@ -12,10 +12,10 @@ Assuming you have an S3 account set up...
|
|
12
12
|
|
13
13
|
Gem dependencies:
|
14
14
|
|
15
|
-
-
|
15
|
+
- fog
|
16
16
|
- dragonfly
|
17
17
|
|
18
|
-
Initializer (e.g. config/initializers/dragonfly.rb):
|
18
|
+
Initializer (e.g. config/initializers/dragonfly.rb in Rails):
|
19
19
|
|
20
20
|
require 'dragonfly'
|
21
21
|
app = Dragonfly[:images]
|
@@ -28,10 +28,9 @@ Initializer (e.g. config/initializers/dragonfly.rb):
|
|
28
28
|
|
29
29
|
The datastore remains as the {Dragonfly::DataStorage::FileDataStore FileDataStore} for non-production environments.
|
30
30
|
|
31
|
-
|
31
|
+
application.rb if using with Rails:
|
32
32
|
|
33
|
-
|
34
|
-
config.middleware.insert_before 'Rack::Lock', 'Dragonfly::Middleware', :images, '/media'
|
33
|
+
config.middleware.insert 0, 'Dragonfly::Middleware', :images
|
35
34
|
|
36
35
|
We don't store the S3 access key and secret in the repository, rather we use Heroku's
|
37
36
|
{http://docs.heroku.com/config-vars config variables} using the command line (we only have to do this once).
|
@@ -40,11 +39,11 @@ From your app's directory:
|
|
40
39
|
|
41
40
|
heroku config:add S3_KEY=XXXXXXXXX S3_SECRET=XXXXXXXXXX
|
42
41
|
|
43
|
-
|
42
|
+
Replace 'XXXXXXXXX' with your access key and secret.
|
44
43
|
|
45
44
|
Now you can benefit from super-fast images served straight from Heroku's cache!
|
46
45
|
|
47
|
-
NOTE
|
46
|
+
**NOTE**: HEROKU'S CACHE IS CLEARED EVERY TIME YOU DEPLOY!!!
|
48
47
|
|
49
|
-
If this is an issue, you may want to look into
|
48
|
+
If this is an issue, you may want to look into storing thumbnails on S3 (see {file:ExampleUseCases}), or maybe an after-deploy hook for hitting specific Dragonfly urls you want to cache, etc.
|
50
49
|
It won't be a problem for most sites though.
|
@@ -0,0 +1,126 @@
|
|
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.strip # same as image.process(:strip)
|
24
|
+
image.convert('-scale 30x30') # same as image.process(:convert, '-scale 30x30')
|
25
|
+
|
26
|
+
`thumb` and `convert` can optionally take a format (e.g. :gif) as the second argument.
|
27
|
+
Bang methods like `image.thumb!('40x30')`, `image.png!` etc. will operate on `self`.
|
28
|
+
|
29
|
+
Below are some examples of geometry strings for `thumb`:
|
30
|
+
|
31
|
+
'400x300' # resize, maintain aspect ratio
|
32
|
+
'400x300!' # force resize, don't maintain aspect ratio
|
33
|
+
'400x' # resize width, maintain aspect ratio
|
34
|
+
'x300' # resize height, maintain aspect ratio
|
35
|
+
'400x300>' # resize only if the image is larger than this
|
36
|
+
'400x300<' # resize only if the image is smaller than this
|
37
|
+
'50x50%' # resize width and height to 50%
|
38
|
+
'400x300^' # resize width, height to minimum 400,300, maintain aspect ratio
|
39
|
+
'2000@' # resize so max area in pixels is 2000
|
40
|
+
'400x300#' # resize, crop if necessary to maintain aspect ratio (centre gravity)
|
41
|
+
'400x300#ne' # as above, north-east gravity
|
42
|
+
'400x300se' # crop, with south-east gravity
|
43
|
+
'400x300+50+100' # crop from the point 50,100 with width, height 400,300
|
44
|
+
|
45
|
+
Processor
|
46
|
+
---------
|
47
|
+
|
48
|
+
image.process(:crop, :width => 40, :height => 50, :x => 20, :y => 30)
|
49
|
+
image.process(:crop, :width => 40, :height => 50, :gravity => 'ne')
|
50
|
+
|
51
|
+
image.process(:flip) # flips it vertically
|
52
|
+
image.process(:flop) # flips it horizontally
|
53
|
+
|
54
|
+
image.process(:greyscale, :depth => 128) # default depth 256
|
55
|
+
|
56
|
+
image.process(:resize, '40x40')
|
57
|
+
image.process(:resize_and_crop, :width => 40, :height=> 50, :gravity => 'ne')
|
58
|
+
|
59
|
+
image.process(:rotate, 45, :background_colour => 'transparent') # default bg black
|
60
|
+
|
61
|
+
The method `thumb` takes a geometry string and calls `resize`, `resize_and_crop` or `crop` accordingly.
|
62
|
+
|
63
|
+
image.process(:thumb, '400x300') # calls resize
|
64
|
+
|
65
|
+
Encoder
|
66
|
+
-------
|
67
|
+
The {Dragonfly::ImageMagick::Encoder ImageMagick Encoder} gives us:
|
68
|
+
|
69
|
+
image.encode(:jpg)
|
70
|
+
image.encode(:gif)
|
71
|
+
image.encode(:png)
|
72
|
+
image.encode(:tiff)
|
73
|
+
|
74
|
+
and various other formats (see {Dragonfly::ImageMagick::Encoder ImageMagick Encoder}).
|
75
|
+
|
76
|
+
You can also pass additional options to the imagemagick command line:
|
77
|
+
|
78
|
+
image.encode(:jpg, '-quality 10')
|
79
|
+
|
80
|
+
Analyser
|
81
|
+
--------
|
82
|
+
The {Dragonfly::ImageMagick::Analyser ImageMagick Analyser} gives us these methods:
|
83
|
+
|
84
|
+
image.width # => 280
|
85
|
+
image.height # => 355
|
86
|
+
image.aspect_ratio # => 0.788732394366197
|
87
|
+
image.portrait? # => true
|
88
|
+
image.landscape? # => false
|
89
|
+
image.depth # => 8
|
90
|
+
image.number_of_colours # => 34703
|
91
|
+
image.format # => :png
|
92
|
+
image.image? # => true - will return true or false for any content
|
93
|
+
|
94
|
+
Generator
|
95
|
+
---------
|
96
|
+
The {Dragonfly::ImageMagick::Generator ImageMagick Generator} gives us these methods:
|
97
|
+
|
98
|
+
image = app.generate(:plain, 600, 400, 'rgba(40,200,30,0.5)')
|
99
|
+
image = app.generate(:plain, 600, 400, '#ccc', :format => :gif)
|
100
|
+
# generate a 600x400 plain image
|
101
|
+
# any css-style colour should work
|
102
|
+
|
103
|
+
image = app.generate(:plasma, 600, 400, :gif) # generate a 600x400 plasma image
|
104
|
+
# last arg defaults to :png
|
105
|
+
|
106
|
+
image = app.generate(:text, "Hello there") # an image of the text "Hello there"
|
107
|
+
|
108
|
+
image = app.generate(:text, "Hello there",
|
109
|
+
:font_size => 30, # defaults to 12
|
110
|
+
:font_family => 'Monaco',
|
111
|
+
:stroke_color => '#ddd',
|
112
|
+
:color => 'red',
|
113
|
+
:font_style => 'italic',
|
114
|
+
:font_stretch => 'expanded',
|
115
|
+
:font_weight => 'bold',
|
116
|
+
:padding => '30 20 10',
|
117
|
+
:background_color => '#efefef', # defaults to transparent
|
118
|
+
:format => :gif # defaults to png
|
119
|
+
)
|
120
|
+
|
121
|
+
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`.
|
122
|
+
|
123
|
+
You can use `padding-top`, `padding-left`, etc., as well as the standard css shortcuts for `padding` (it assumes unit is px).
|
124
|
+
|
125
|
+
An alternative for `:font_family` is `:font` (see {http://www.imagemagick.org/script/command-line-options.php#font}), which could be a complete filename.
|
126
|
+
Available fonts are those available on your system.
|
data/extra_docs/MimeTypes.md
CHANGED
@@ -7,8 +7,8 @@ This is decided by the first found from:
|
|
7
7
|
|
8
8
|
1. The requested format (e.g. when encode is specifically called)
|
9
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 '
|
11
|
-
4. Analyse the content using the analyser's '
|
10
|
+
3. Analyse the content using the analyser's 'format' method (if exists)
|
11
|
+
4. Analyse the content using the analyser's 'mime_type' method (if exists)
|
12
12
|
5. Use the fallback mime-type (default 'application/octet-stream')
|
13
13
|
|
14
14
|
Note that 'format' means 'jpg', 'png', etc. whereas mime-type would be 'image/jpeg', image/png', etc.
|
@@ -27,7 +27,7 @@ You can also do this inside a configuration block.
|
|
27
27
|
Analysers
|
28
28
|
---------
|
29
29
|
The {Dragonfly::Analysis::FileCommandAnalyser FileCommandAnalyser} has a `mime_type` method and the
|
30
|
-
{Dragonfly::
|
30
|
+
{Dragonfly::ImageMagick::Analyser ImageMagick Analyser} has a `format` method.
|
31
31
|
|
32
32
|
These are both registered by default when you use the preconfigured 'dragonfly/rails/images' file.
|
33
33
|
|
data/extra_docs/Models.md
CHANGED
@@ -24,6 +24,12 @@ Mongoid
|
|
24
24
|
|
25
25
|
defines the macro `image_accessor` on any models that include `Mongoid::Document`
|
26
26
|
|
27
|
+
CouchRest::Model
|
28
|
+
-------
|
29
|
+
app.define_macro(CouchRest::Model::Base, :image_accessor)
|
30
|
+
|
31
|
+
defines the macro `image_accessor` on any models inherited from `CouchRest::Model::Base`.
|
32
|
+
|
27
33
|
Adding accessors
|
28
34
|
----------------
|
29
35
|
Now we have the method `image_accessor` available in our model classes, which we can use as many times as we like
|
@@ -62,6 +68,7 @@ We can use the attribute much like other other model attributes:
|
|
62
68
|
@album.cover_image = "\377???JFIF\000\..." # can assign as a string...
|
63
69
|
@album.cover_image = File.new('path/to/my_image.png') # ... or as a file...
|
64
70
|
@album.cover_image = some_tempfile # ... or as a tempfile...
|
71
|
+
@album.cover_image = Pathname.new('some/path.gif') # ... or as a pathname...
|
65
72
|
@album.cover_image = @album.band_photo # ... or as another Dragonfly attachment
|
66
73
|
|
67
74
|
@album.cover_image # => #<Dragonfly::ActiveModelExtensions::Attachment:0x103ef6128...
|
@@ -119,6 +126,42 @@ You can force the processing to be done if you must by then calling `apply`.
|
|
119
126
|
|
120
127
|
@album.cover_image.process(:greyscale).apply
|
121
128
|
|
129
|
+
Assigning from a url
|
130
|
+
--------------------
|
131
|
+
Dragonfly provides an accessor for assigning directly from a url:
|
132
|
+
|
133
|
+
@album.cover_image_url = 'http://some.url/file.jpg'
|
134
|
+
|
135
|
+
You can put this in a form view, e.g. in rails erb:
|
136
|
+
|
137
|
+
<% form_for @album, :html => {:multipart => true} do |f| %>
|
138
|
+
...
|
139
|
+
<%= f.text_field :cover_image_url %>
|
140
|
+
...
|
141
|
+
<% end %>
|
142
|
+
|
143
|
+
Removing an attachment via a form
|
144
|
+
---------------------------------
|
145
|
+
Normally unassignment of an attachment is done like any other attribute, by setting to nil
|
146
|
+
|
147
|
+
@album.cover_image = nil
|
148
|
+
|
149
|
+
but this can't be done via a form - instead `remove_<attachment_name>` is provided, which can be used with a checkbox:
|
150
|
+
|
151
|
+
<%= f.check_box :remove_cover_image %>
|
152
|
+
|
153
|
+
Retaining across form redisplays
|
154
|
+
--------------------------------
|
155
|
+
When a model fails validation, you don't normally want to have to upload your attachment again, so you can avoid having to do this by
|
156
|
+
including a hidden field in your form `retained_<attribute_name>`, e.g.
|
157
|
+
|
158
|
+
<% form_for @album, :html => {:multipart => true} do |f| %>
|
159
|
+
...
|
160
|
+
<%= f.file_field :cover_image %>
|
161
|
+
<%= f.hidden_field :retained_cover_image %>
|
162
|
+
...
|
163
|
+
<% end %>
|
164
|
+
|
122
165
|
Persisting
|
123
166
|
----------
|
124
167
|
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})
|
@@ -190,6 +233,124 @@ As you can see, a couple of things are added by the model. You can also access t
|
|
190
233
|
|
191
234
|
app.fetch(@album.cover_image_uid).meta # => {:model_class=>"Album", ...}
|
192
235
|
|
236
|
+
Meta data can be useful because at the time that Dragonfly serves content, it doesn't have access to your model, but it does
|
237
|
+
have access to the meta data that was stored alongside the content, so you could use it to provide custom response headers, etc.
|
238
|
+
(see {file:Configuration}).
|
239
|
+
|
240
|
+
Callbacks
|
241
|
+
---------
|
242
|
+
**after_assign**
|
243
|
+
|
244
|
+
`after_assign` can be used to do something every time content is assigned:
|
245
|
+
|
246
|
+
class Person
|
247
|
+
image_accessor :mugshot do
|
248
|
+
after_assign{|a| a.process!(:rotate, 90) } # 'a' is the attachment itself
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
person.mugshot = Pathname.new('some/path.png') # after_assign callback is called
|
253
|
+
person.mugshot = nil # after_assign callback is NOT called
|
254
|
+
|
255
|
+
Inside the block, you can call methods on the model instance directly (`self` is the model):
|
256
|
+
|
257
|
+
class Person
|
258
|
+
image_accessor :mugshot do
|
259
|
+
after_assign{|a| a.process!(:rotate, angle) }
|
260
|
+
end
|
261
|
+
|
262
|
+
def angle
|
263
|
+
90
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
Alternatively you can pass in a symbol, corresponding to a model instance method:
|
268
|
+
|
269
|
+
class Person
|
270
|
+
image_accessor :mugshot do
|
271
|
+
after_assign :rotate_it
|
272
|
+
end
|
273
|
+
|
274
|
+
def rotate_it
|
275
|
+
mugshot.process!(:rotate, 90)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
You can register more than one `after_assign` callback.
|
280
|
+
|
281
|
+
**after_unassign**
|
282
|
+
|
283
|
+
`after_unassign` is similar to `after_assign`, but is only called when the attachment is unassigned
|
284
|
+
|
285
|
+
person.mugshot = Pathname.new('some/path.png') # after_unassign callback is NOT called
|
286
|
+
person.mugshot = nil # after_unassign callback is called
|
287
|
+
|
288
|
+
Up-front thumbnailing
|
289
|
+
---------------------
|
290
|
+
The best way to create different versions of content such as thumbnails is generally on-the-fly, however if you _must_
|
291
|
+
create another version _on-upload_, then you could create another accessor and automatically copy to it using `copy_to`.
|
292
|
+
|
293
|
+
class Person
|
294
|
+
image_accessor :mugshot do
|
295
|
+
copy_to(:smaller_mugshot){|a| a.thumb('200x200#') }
|
296
|
+
end
|
297
|
+
image_accessor :smaller_mugshot
|
298
|
+
end
|
299
|
+
|
300
|
+
person.mugshot = Pathname.new('some/400x300/image.png')
|
301
|
+
|
302
|
+
person.mugshot # ---> 400x300 image
|
303
|
+
person.smaller_mugshot # ---> 200x200 image
|
304
|
+
|
305
|
+
In the above example you would need both a `mugshot_uid` field and a `smaller_mugshot_uid` field on your model.
|
306
|
+
|
307
|
+
Storage options
|
308
|
+
---------------
|
309
|
+
Some datastores take options when calling `store` - you can pass these through using `storage_xxx` methods, e.g.
|
310
|
+
|
311
|
+
**storage_path**
|
312
|
+
|
313
|
+
The {Dragonfly::DataStorage::FileDataStore FileDataStore} and {Dragonfly::DataStorage::S3DataStore S3DataStore} both
|
314
|
+
can take a `:path` option to specify where to store the content (which will also become the uid for that content)
|
315
|
+
|
316
|
+
class Person
|
317
|
+
image_accessor :mugshot do
|
318
|
+
storage_path{ "some/path/#{id}/#{rand(100)}" } # You can call model instance methods (like 'id') directly
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
or
|
323
|
+
|
324
|
+
class Person
|
325
|
+
image_accessor :mugshot do
|
326
|
+
storage_path :path_for_mugshot
|
327
|
+
end
|
328
|
+
|
329
|
+
def path_for_mugshot
|
330
|
+
"some/path/#{id}/#{rand(100)}"
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
or you can also yield the attachment itself
|
335
|
+
|
336
|
+
storage_path{|a| "some/path/#{a.width}x#{a.height}.#{a.format}" }
|
337
|
+
|
338
|
+
**BEWARE!!!!** you must make sure the path (which will become the uid for the content) is unique and changes each time the content
|
339
|
+
is changed, otherwise you could have caching problems, as the generated urls will be the same for the same uid.
|
340
|
+
|
341
|
+
You can pass any options through to the datastore using `storage_xxx` methods, or all at once using `storage_opts`:
|
342
|
+
|
343
|
+
class Person
|
344
|
+
image_accessor :mugshot do
|
345
|
+
storage_opts do |a|
|
346
|
+
{
|
347
|
+
:path => "some/path/#{id}/#{rand(100)}",
|
348
|
+
:other => 'option'
|
349
|
+
}
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
193
354
|
"Magic" Attributes
|
194
355
|
------------------
|
195
356
|
An accessor like `cover_image` only relies on the accessor `cover_image_uid` to work.
|
@@ -216,6 +377,8 @@ They can be used to avoid retrieving data from the datastore for analysis
|
|
216
377
|
@album.cover_image.width # => 280 - no need to retrieve data - takes it from `cover_image_width`
|
217
378
|
@album.cover_image.size # => 134507 - but this needs to retrieve data from the data store, then analyse
|
218
379
|
|
380
|
+
Furthermore, any magic attributes you add a field for will be added to the meta data for that attachment (and so can be used when Dragonfly serves the content
|
381
|
+
for e.g. setting custom response headers based on that meta - see {file:Configuration}).
|
219
382
|
|
220
383
|
Custom Model
|
221
384
|
------------
|