dragonfly 0.1.6 → 0.2.1
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/.yardopts +4 -0
- data/{README.markdown → README.md} +12 -24
- data/Rakefile +6 -6
- data/VERSION +1 -1
- data/config.rb +1 -3
- data/docs.watchr +1 -1
- data/dragonfly-rails.gemspec +2 -5
- data/dragonfly.gemspec +32 -12
- data/extra_docs/ActiveRecord.md +195 -0
- data/extra_docs/Analysers.md +63 -0
- data/extra_docs/DataStorage.md +33 -0
- data/extra_docs/Encoding.md +58 -0
- data/extra_docs/GettingStarted.md +114 -0
- data/extra_docs/Processing.md +58 -0
- data/extra_docs/Shortcuts.md +118 -0
- data/extra_docs/UsingWithRails.md +104 -0
- data/features/{dragonfly.feature → images.feature} +14 -4
- data/features/no_processing.feature +20 -0
- data/features/steps/dragonfly_steps.rb +29 -8
- data/features/support/env.rb +20 -8
- data/generators/dragonfly_app/USAGE +0 -1
- data/generators/dragonfly_app/dragonfly_app_generator.rb +1 -13
- data/generators/dragonfly_app/templates/metal_file.erb +1 -1
- data/lib/dragonfly/active_record_extensions.rb +1 -0
- data/lib/dragonfly/active_record_extensions/attachment.rb +52 -6
- data/lib/dragonfly/active_record_extensions/validations.rb +26 -6
- data/lib/dragonfly/analysis/base.rb +6 -3
- data/lib/dragonfly/analysis/r_magick_analyser.rb +0 -6
- data/lib/dragonfly/app.rb +53 -35
- data/lib/dragonfly/configurable.rb +1 -1
- data/lib/dragonfly/data_storage/file_data_store.rb +8 -8
- data/lib/dragonfly/delegatable.rb +14 -0
- data/lib/dragonfly/delegator.rb +50 -0
- data/lib/dragonfly/encoding/base.rb +7 -7
- data/lib/dragonfly/encoding/r_magick_encoder.rb +3 -0
- data/lib/dragonfly/encoding/transparent_encoder.rb +1 -1
- data/lib/dragonfly/extended_temp_object.rb +13 -7
- data/lib/dragonfly/parameters.rb +17 -11
- data/lib/dragonfly/processing/base.rb +9 -0
- data/lib/dragonfly/processing/r_magick_processor.rb +15 -1
- data/lib/dragonfly/r_magick_configuration.rb +12 -8
- data/lib/dragonfly/rails/images.rb +1 -1
- data/lib/dragonfly/temp_object.rb +14 -2
- data/lib/dragonfly/url_handler.rb +5 -6
- data/samples/sample.docx +0 -0
- data/spec/dragonfly/active_record_extensions/model_spec.rb +175 -84
- data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +0 -8
- data/spec/dragonfly/app_spec.rb +3 -3
- data/spec/dragonfly/configurable_spec.rb +1 -1
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +55 -40
- data/spec/dragonfly/delegatable_spec.rb +32 -0
- data/spec/dragonfly/delegator_spec.rb +133 -0
- data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +28 -0
- data/spec/dragonfly/extended_temp_object_spec.rb +5 -5
- data/spec/dragonfly/parameters_spec.rb +22 -32
- data/spec/dragonfly/processing/rmagick_processor_spec.rb +1 -2
- data/spec/dragonfly/temp_object_spec.rb +51 -0
- data/spec/dragonfly/url_handler_spec.rb +10 -15
- data/yard/handlers/configurable_attr_handler.rb +38 -0
- data/yard/setup.rb +9 -0
- data/yard/templates/default/fulldoc/html/css/common.css +27 -0
- data/yard/templates/default/module/html/configuration_summary.erb +31 -0
- data/yard/templates/default/module/setup.rb +17 -0
- metadata +31 -12
- data/features/support/image_helpers.rb +0 -9
- data/generators/dragonfly_app/templates/custom_processing.erb +0 -13
- data/lib/dragonfly/analysis/analyser.rb +0 -45
- data/lib/dragonfly/processing/processor.rb +0 -14
- data/spec/dragonfly/analysis/analyser_spec.rb +0 -85
@@ -0,0 +1,63 @@
|
|
1
|
+
Analysers
|
2
|
+
=========
|
3
|
+
|
4
|
+
Analysing data for things like width, mime_type, etc. come under the banner of Analysis.
|
5
|
+
|
6
|
+
Let's say we have a dragonfly app called 'images'
|
7
|
+
|
8
|
+
app = Dragonfly::App[:images]
|
9
|
+
|
10
|
+
Data gets passed around between the datastore, processor, analyser, etc. in the form of an {Dragonfly::ExtendedTempObject ExtendedTempObject}.
|
11
|
+
|
12
|
+
temp_object = app.create_object(File.new('path/to/image.png'))
|
13
|
+
|
14
|
+
This object will have any methods which have been registered with the analyser. For example, registering
|
15
|
+
the {Dragonfly::Analysis::RMagickAnalyser rmagick analyser}
|
16
|
+
|
17
|
+
app.register_analyser(Dragonfly::Analysis::RMagickAnalyser)
|
18
|
+
|
19
|
+
give us the methods `width`, `height`, `depth` and `number_of_colours` (or `number_of_colors`).
|
20
|
+
|
21
|
+
temp_object.width # => 280
|
22
|
+
# ...etc.
|
23
|
+
|
24
|
+
Registering the {Dragonfly::Analysis::FileCommandAnalyser 'file' command analyser}
|
25
|
+
|
26
|
+
app.register_analyser(Dragonfly::Analysis::FileCommandAnalyser)
|
27
|
+
|
28
|
+
gives us the method `mime_type`, which is necessary for the app to serve the file properly.
|
29
|
+
|
30
|
+
temp_object.mime_type # => 'image/png'
|
31
|
+
|
32
|
+
As the file command analyser is {Dragonfly::Configurable configurable}, we can configure it as we register it if we need to
|
33
|
+
|
34
|
+
app.register_analyser(Dragonfly::Analysis::FileCommandAnalyser) do |a|
|
35
|
+
a.file_command = '/usr/bin/file'
|
36
|
+
end
|
37
|
+
|
38
|
+
The saved configuration {Dragonfly::RMagickConfiguration RMagickConfiguration} registers the above two analysers automatically.
|
39
|
+
|
40
|
+
Custom Analysers
|
41
|
+
----------------
|
42
|
+
|
43
|
+
To register a custom analyser, derive from {Dragonfly::Analysis::Base Analysis::Base} and register.
|
44
|
+
Each method takes the temp_object as its argument.
|
45
|
+
|
46
|
+
class MyAnalyser < Dragonfly::Analysis::Base
|
47
|
+
|
48
|
+
def coolness(temp_object)
|
49
|
+
# use temp_object.data, temp_object.path, etc...
|
50
|
+
temp_object.size / 30
|
51
|
+
end
|
52
|
+
|
53
|
+
# ... add as many methods as you wish
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
app.register_analyser(MyAnalyser)
|
58
|
+
|
59
|
+
temp_object = app.create_object(File.new('path/to/image.png'))
|
60
|
+
|
61
|
+
temp_object.coolness # => 2067
|
62
|
+
|
63
|
+
You can register multiple analysers.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
Data Storage
|
2
|
+
============
|
3
|
+
|
4
|
+
Each dragonfly app has a datastore.
|
5
|
+
|
6
|
+
Dragonfly::App[:my_app_name].datastore
|
7
|
+
|
8
|
+
By default it uses the file data store, but you can configure it to use
|
9
|
+
a custom data store (e.g. S3, SQL, CouchDB, etc.) by registering one with the correct interface, namely
|
10
|
+
having `store`, `retrieve` and `destroy`.
|
11
|
+
|
12
|
+
class MyDataStore < Dragonfly::DataStorage::Base
|
13
|
+
|
14
|
+
def store(temp_object)
|
15
|
+
# ... use temp_object.data, temp_object.file, or temp_object.path and store
|
16
|
+
'return_some_unique_uid'
|
17
|
+
end
|
18
|
+
|
19
|
+
def retrieve(uid)
|
20
|
+
# find the content and return either a data string, a file, a tempfile or a Dragonfly::TempObject
|
21
|
+
end
|
22
|
+
|
23
|
+
def destroy(uid)
|
24
|
+
# find the content and destroy
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
You can now configure the app to use this datastore like so
|
30
|
+
|
31
|
+
Dragonfly::App[:my_app_name].datastore = MyDataStore.new
|
32
|
+
|
33
|
+
If you want your datastore to be configurable, you can include the {Dragonfly::Configurable Configurable} module.
|
@@ -0,0 +1,58 @@
|
|
1
|
+
Encoding
|
2
|
+
========
|
3
|
+
|
4
|
+
'Encoding' encapsulates the idea of a format (e.g. 'png', 'jpeg', 'doc', 'txt', etc.), and any encoding options
|
5
|
+
(e.g. bit_rate, etc.).
|
6
|
+
|
7
|
+
It is up to the encoder to modify the data according to the requested format (note the format is not the same as the mime-type;
|
8
|
+
the mime-type is detected by the analyser).
|
9
|
+
|
10
|
+
The encoder needs to implement a single method, `encode`, which takes a {Dragonfly::ExtendedTempObject temp_object}, a format, and encoding options as arguments.
|
11
|
+
|
12
|
+
def encode(temp_object, format, encoding={})
|
13
|
+
#... encode and return a String, File, Tempfile or TempObject
|
14
|
+
end
|
15
|
+
|
16
|
+
Let's say we register the {Dragonfly::Encoding::RMagickEncoder rmagick encoder} to our dragonfly app called 'images'
|
17
|
+
|
18
|
+
app = Dragonfly::App[:images]
|
19
|
+
app.register_encoder(Dragonfly::Encoding::RMagickEncoder)
|
20
|
+
|
21
|
+
Then we can encode {Dragonfly::ExtendedTempObject temp_objects} to formats recognised by the RMagickEncoder
|
22
|
+
|
23
|
+
temp_object = app.create_object(File.new('path/to/image.png'))
|
24
|
+
|
25
|
+
temp_object.encode(:png) # => returns a new temp_object with data encoded as 'png'
|
26
|
+
temp_object.encode!(:gif) # => encodes its own data as a 'png'
|
27
|
+
|
28
|
+
temp_object.encode(:doc) # => throws :unable_to_handle
|
29
|
+
|
30
|
+
The saved configuration {Dragonfly::RMagickConfiguration RMagickConfiguration} registers the above encoder automatically.
|
31
|
+
|
32
|
+
Custom Encoders
|
33
|
+
---------------
|
34
|
+
|
35
|
+
To register a custom encoder, derive from {Dragonfly::Encoding::Base Encoding::Base} and register.
|
36
|
+
As described above, you need to implement the `encode` method.
|
37
|
+
If the encoder can't handle a format, it should throw `:unable_to_handle`, and control will pass to the previously
|
38
|
+
registered encoder, and so on.
|
39
|
+
|
40
|
+
class MyEncoder < Dragonfly::Encoding::Base
|
41
|
+
|
42
|
+
def encode(temp_object, format, encoding={})
|
43
|
+
if format.to_s == 'yo'
|
44
|
+
#... encode and return a String, File, Tempfile or TempObject
|
45
|
+
else
|
46
|
+
throw :unable_to_handle
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
app.register_encoder(MyEncoder)
|
53
|
+
|
54
|
+
If the encoder is {Dragonfly::Configurable configurable}, we can configure it as we register it if we need to
|
55
|
+
|
56
|
+
app.register_encoder(MyEncoder) do |e|
|
57
|
+
e.some_attribute = 'hello'
|
58
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
Getting Started
|
2
|
+
===============
|
3
|
+
|
4
|
+
Below is a general guide for setting up and using Dragonfly.
|
5
|
+
|
6
|
+
For setting up with Ruby on Rails, see {file:UsingWithRails UsingWithRails}.
|
7
|
+
|
8
|
+
For more info about using Rack applications, see the docs at {http://rack.rubyforge.org/}
|
9
|
+
|
10
|
+
Running as a Standalone Rack Application
|
11
|
+
----------------------------------------
|
12
|
+
|
13
|
+
Basic usage of a dragonfly app involves storing data (e.g. images),
|
14
|
+
then serving that data, either in its original form, processed, encoded or both.
|
15
|
+
|
16
|
+
A basic rackup file `config.ru`:
|
17
|
+
|
18
|
+
require 'rubygems'
|
19
|
+
require 'dragonfly'
|
20
|
+
|
21
|
+
Dragonfly::App[:my_app_name].configure do |c|
|
22
|
+
# ...
|
23
|
+
c.some_attribute = 'blah'
|
24
|
+
# ...
|
25
|
+
end
|
26
|
+
|
27
|
+
run Dragonfly:App[:my_app_name]
|
28
|
+
|
29
|
+
As you can see, this involves instantiating an app, configuring it (how data is stored,
|
30
|
+
processing, encoding, etc.), then running it.
|
31
|
+
|
32
|
+
You can have multiple dragonfly apps, each with their own configuration.
|
33
|
+
Each app has a name, and is referred to by that name.
|
34
|
+
|
35
|
+
Dragonfly::App[:images] # ===> Creates an app called 'images'
|
36
|
+
Dragonfly::App[:images] # ===> Refers to the already created app 'images'
|
37
|
+
|
38
|
+
Example: Using to serve resized images
|
39
|
+
--------------------------------------
|
40
|
+
|
41
|
+
`config.ru`:
|
42
|
+
|
43
|
+
require 'rubygems'
|
44
|
+
require 'dragonfly'
|
45
|
+
require 'rack/cache'
|
46
|
+
|
47
|
+
app = Dragonfly::App[:images]
|
48
|
+
app.configure_with(Dragonfly::RMagickConfiguration)
|
49
|
+
|
50
|
+
use Rack::Cache,
|
51
|
+
:verbose => true,
|
52
|
+
:metastore => 'file:/var/cache/rack/meta',
|
53
|
+
:entitystore => 'file:/var/cache/rack/body'
|
54
|
+
|
55
|
+
run app
|
56
|
+
|
57
|
+
This configures the app to use the RMagick {Dragonfly::Processing::RMagickProcessor processor},
|
58
|
+
{Dragonfly::Encoding::RMagickEncoder encoder} and {Dragonfly::Analysis::RMagickAnalyser analyser}.
|
59
|
+
By default the {Dragonfly::DataStorage::FileDataStore file data store} is used.
|
60
|
+
|
61
|
+
Elsewhere in our code:
|
62
|
+
|
63
|
+
app = Dragonfly::App[:images]
|
64
|
+
|
65
|
+
# Store
|
66
|
+
uid = app.store(File.new('path/to/image.png')) # ===> returns a unique uid for that image, "2009/11/29/145804_file"
|
67
|
+
|
68
|
+
# Get the url for a thumbnail
|
69
|
+
url = app.url_for(uid, '30x30', :gif) # ===> "/2009/11/29/145804_file.gif?m=resize&o[geometry]=30x30"
|
70
|
+
|
71
|
+
Now when we visit the url `/2009/11/29/145804_file.gif?m=resize&o[geometry]=30x30` in the browser, we get the resized
|
72
|
+
image!
|
73
|
+
|
74
|
+
Caching
|
75
|
+
-------
|
76
|
+
Processing and encoding can be an expensive operation. The first time we visit the url,
|
77
|
+
the image is processed, and there might be a short delay and getting the response.
|
78
|
+
|
79
|
+
However, dragonfly apps send `Cache-Control` and `ETag` headers in the response, so we can easily put a caching
|
80
|
+
proxy like {http://varnish.projects.linpro.no Varnish}, {http://www.squid-cache.org Squid},
|
81
|
+
{http://tomayko.com/src/rack-cache/ Rack::Cache}, etc. in front of the app.
|
82
|
+
|
83
|
+
In the example above, we've put the middleware {http://tomayko.com/src/rack-cache/ Rack::Cache} in front of the app.
|
84
|
+
So although the first time we access the url the content is processed, every time after that it is received from the
|
85
|
+
cache, and is served super quick!
|
86
|
+
|
87
|
+
Avoiding Denial-of-service attacks
|
88
|
+
----------------------------------
|
89
|
+
The url given above, `/2009/11/29/145804_file.gif?m=resize&o[geometry]=30x30`, could easily be modified to
|
90
|
+
generate all different sizes of thumbnails, just by changing the size, e.g.
|
91
|
+
|
92
|
+
`/2009/11/29/145804_file.gif?m=resize&o[geometry]=30x31`,
|
93
|
+
|
94
|
+
`/2009/11/29/145804_file.gif?m=resize&o[geometry]=30x32`,
|
95
|
+
|
96
|
+
etc.
|
97
|
+
|
98
|
+
Therefore the app can protect the url by generating a unique sha from a secret specified by you
|
99
|
+
|
100
|
+
Dragonfly::App[:images].url_handler.configure do |c|
|
101
|
+
c.protect_from_dos_attacks = true # Actually this is true by default
|
102
|
+
c.secret = 'You should supply some random secret here'
|
103
|
+
end
|
104
|
+
|
105
|
+
Then the required urls become something more like
|
106
|
+
|
107
|
+
`/2009/12/10/215214_file.gif?m=resize&o[geometry]=30x30&s=aa78e877ad3f6bc9`,
|
108
|
+
|
109
|
+
with a sha parameter on the end.
|
110
|
+
If we try to hack this url to get a different thumbnail,
|
111
|
+
|
112
|
+
`/2009/12/10/215214_file.gif?m=resize&o[geometry]=30x31&s=aa78e877ad3f6bc9`,
|
113
|
+
|
114
|
+
then we get a 400 (bad parameters) error.
|
@@ -0,0 +1,58 @@
|
|
1
|
+
Processing
|
2
|
+
==========
|
3
|
+
|
4
|
+
Processing is changing content in some way, and does not involve encoding.
|
5
|
+
For example, resizing an image is classed as processing, whereas converting it from 'png' to 'jpeg' is classed as encoding.
|
6
|
+
|
7
|
+
All processing jobs are defined by a processing method and a processing options hash (which is passed to the method).
|
8
|
+
|
9
|
+
Let's say we have a dragonfly app called 'images'
|
10
|
+
|
11
|
+
app = Dragonfly::App[:images]
|
12
|
+
|
13
|
+
Data gets passed around between the datastore, processor, analyser, etc. in the form of an {Dragonfly::ExtendedTempObject ExtendedTempObject}.
|
14
|
+
|
15
|
+
temp_object = app.create_object(File.new('path/to/image.png'))
|
16
|
+
|
17
|
+
We can process this object with any of the methods which have been registered with the app's processor.
|
18
|
+
For example, registering the {Dragonfly::Processing::RMagickProcessor rmagick processor}
|
19
|
+
|
20
|
+
app.register_processor(Dragonfly::Processing::RMagickProcessor)
|
21
|
+
|
22
|
+
give us the processing methods `resize`, `crop`, `resize_and_crop`, `rotate`, etc.
|
23
|
+
|
24
|
+
temp_object.process(:resize, :geometry => '30x30!') # => returns a new temp_object with width x height = 30x30
|
25
|
+
temp_object.process!(:resize, :geometry => '30x30!') # => resizes its own data
|
26
|
+
|
27
|
+
The saved configuration {Dragonfly::RMagickConfiguration RMagickConfiguration} registers the above processor automatically.
|
28
|
+
|
29
|
+
Custom Processing
|
30
|
+
-----------------
|
31
|
+
|
32
|
+
To register a custom processor, derive from {Dragonfly::Processing::Base Processing::Base} and register.
|
33
|
+
Each method takes the temp_object, and the (optional) processing options hash as its argument.
|
34
|
+
|
35
|
+
class MyProcessor < Dragonfly::Processing::Base
|
36
|
+
|
37
|
+
def black_and_white(temp_object, opts={})
|
38
|
+
# use temp_object.data, temp_object.path, etc...
|
39
|
+
# ...process and return a String, File, Tempfile or TempObject
|
40
|
+
end
|
41
|
+
|
42
|
+
# ... add as many methods as you wish
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
app.register_processor(MyProcessor)
|
47
|
+
|
48
|
+
temp_object = app.create_object(File.new('path/to/image.png'))
|
49
|
+
|
50
|
+
temp_object.process(:black_and_white, :some => 'option')
|
51
|
+
|
52
|
+
You can register multiple processors.
|
53
|
+
|
54
|
+
As with analysers and encoders, if the processor is {Dragonfly::Configurable configurable}, we can configure it as we register it if we need to
|
55
|
+
|
56
|
+
app.register_processor(MyProcessor) do |p|
|
57
|
+
p.some_attribute = 'hello'
|
58
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
Shortcuts
|
2
|
+
=========
|
3
|
+
|
4
|
+
When you call {Dragonfly::App#fetch fetch}, {Dragonfly::ExtendedTempObject#transform transform},
|
5
|
+
{Dragonfly::UrlHandler#url_for url_for}, (or {Dragonfly::ActiveRecordExtensions::Attachment#url url} on an ActiveRecord attachment), you can specify all the job parameters
|
6
|
+
necessary to fetch the appropriate content.
|
7
|
+
|
8
|
+
The parameters are the following:
|
9
|
+
|
10
|
+
- `:uid` - the uid used by the datastore to retrieve the data (was returned when stored)
|
11
|
+
- `:processing_method` - the method used to do the processing
|
12
|
+
- `:processing_options` - options hash passed to the processing method
|
13
|
+
- `:format` - the format to encode the data with, e.g. 'png'
|
14
|
+
- `:encoding` - an options hash passed to the encoder for other options, e.g. bitrate, etc.
|
15
|
+
|
16
|
+
Say we have an app configured with the {Dragonfly::RMagickConfiguration RMagickConfiguration}
|
17
|
+
|
18
|
+
app = Dragonfly::App[:my_app]
|
19
|
+
app.configure_with(Dragonfly::RMagickConfiguration)
|
20
|
+
|
21
|
+
we can call things like
|
22
|
+
|
23
|
+
app.fetch 'some_uid',
|
24
|
+
:processing_method => :resize_and_crop,
|
25
|
+
:processing_options => {:width => 100, :height => 50, :gravity => 'ne'},
|
26
|
+
:format => :jpg,
|
27
|
+
:encoding => {:some => 'option'} # => gets a processed and encoded temp_object
|
28
|
+
|
29
|
+
app.url_for 'some_uid',
|
30
|
+
:processing_method => :resize_and_crop,
|
31
|
+
:processing_options => {:width => 100, :height => 50, :gravity => 'ne'},
|
32
|
+
:format => :jpg,
|
33
|
+
:encoding => {:some => 'option'} # => "/images/some_uid.tif?m=resize_and_crop&o[width]=...."
|
34
|
+
|
35
|
+
YIKES!!!!!
|
36
|
+
|
37
|
+
That's an awful lot of code every time, especially if we reuse the same parameters over and over.
|
38
|
+
That's why for the arguments after the uid, you can register {Dragonfly::Parameters parameter shortcuts}.
|
39
|
+
|
40
|
+
Simple shortcuts
|
41
|
+
----------------
|
42
|
+
If we were to use the parameters in the example above a number of times, we could register a simple named shortcut, say 'thumb', using a symbol.
|
43
|
+
|
44
|
+
app.parameters.add_shortcut(:thumb,
|
45
|
+
:processing_method => :resize_and_crop,
|
46
|
+
:processing_options => {:width => 100, :height => 50, :gravity => 'ne'},
|
47
|
+
:format => :jpg,
|
48
|
+
:encoding => {:some => 'option'}
|
49
|
+
)
|
50
|
+
|
51
|
+
We can now use this named shortcut, `:thumb`, in place of the parameters elsewhere
|
52
|
+
|
53
|
+
app.fetch 'some_uid', :thumb # => gets a processed and encoded temp_object as before
|
54
|
+
app.url_for 'some_uid', :thumb # => "/images/some_uid.tif?m=resize_and_crop&o[width]=....", as before
|
55
|
+
|
56
|
+
Complex shortcuts
|
57
|
+
-----------------
|
58
|
+
Rather than create a new named shortcut for every different set of parameters, we can make life easier by defining shortcuts
|
59
|
+
which match a set of arguments, then form parameters from them.
|
60
|
+
|
61
|
+
For example, take the shortcut
|
62
|
+
|
63
|
+
app.parameters.add_shortcut(/\d+x\d+/, Symbol) do |geometry, format|
|
64
|
+
{
|
65
|
+
:processing_method => :my_resize_method,
|
66
|
+
:processing_options => {:geometry => geometry},
|
67
|
+
:format => format
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
It will match where there are two arguments, a string matching `/\d+x\d+/` and a symbol, and convert to a hash of job parameters.
|
72
|
+
Note that the matching arguments are yielded to the block.
|
73
|
+
So, for example,
|
74
|
+
|
75
|
+
app.url_for('some_uid', '100x50', :jpg)
|
76
|
+
|
77
|
+
generates the same url as if we'd used
|
78
|
+
|
79
|
+
app.url_for 'some_uid',
|
80
|
+
:processing_method => :my_resize_method,
|
81
|
+
:processing_options => {:geometry => 100},
|
82
|
+
:format => :jpg
|
83
|
+
|
84
|
+
The following examples would not match
|
85
|
+
|
86
|
+
app.url_for('some_uid', '100xg50', :jpg) # '100xg50' doesn't match /\d+x\d+/
|
87
|
+
app.url_for('some_uid', '100x50', 'jpg') # 'jpg' is not a Symbol
|
88
|
+
app.url_for('some_uid', '100x50') # not enough arguments
|
89
|
+
app.url_for('some_uid', '100x50', :jpg, 4) # too many arguments
|
90
|
+
|
91
|
+
The arguments are matched using the `===` operator (as used in `case` statements), which is why, for example, `:jpg` matches `Symbol`.
|
92
|
+
|
93
|
+
Regexp shortcuts
|
94
|
+
----------------
|
95
|
+
If we register a complex shortcut as a single regexp, then the match data is also yielded, for convenience.
|
96
|
+
|
97
|
+
app.parameters.add_shortcut(/(\d+)x(\d+)/) do |geometry, match_data|
|
98
|
+
{
|
99
|
+
:processing_method => :my_resize_method,
|
100
|
+
:processing_options => {:width => match_data[1], :height => match_data[2]}
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
Default parameters
|
106
|
+
------------------
|
107
|
+
If we've configured a default parameter, e.g.
|
108
|
+
|
109
|
+
app.parameters.default_format = :jpg
|
110
|
+
|
111
|
+
then this will be used in these methods whenever that parameter is not given, such as in the example above.
|
112
|
+
|
113
|
+
Avoiding processing/encoding
|
114
|
+
----------------------------
|
115
|
+
If `:processing_method` is set to nil, then no processing takes place when methods like {Dragonfly::App#fetch fetch}, {Dragonfly::ExtendedTempObject#transform transform},
|
116
|
+
{Dragonfly::UrlHandler#url_for url_for}, and {Dragonfly::ActiveRecordExtensions::Attachment#url url} are called.
|
117
|
+
|
118
|
+
Similarly, if `:format` is set to nil, then no encoding takes place.
|