dragonfly 0.4.1 → 0.4.2
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 +1 -0
- data/VERSION +1 -1
- data/dragonfly.gemspec +2 -2
- data/extra_docs/ExampleUseCases.md +183 -4
- data/extra_docs/Index.md +1 -0
- data/extra_docs/UsingWithRails.md +3 -0
- data/lib/dragonfly/extended_temp_object.rb +21 -0
- data/lib/dragonfly/parameters.rb +2 -2
- data/lib/dragonfly/temp_object.rb +26 -0
- data/spec/dragonfly/parameters_spec.rb +4 -4
- metadata +2 -2
data/.yardopts
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.2
|
data/dragonfly.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{dragonfly}
|
8
|
-
s.version = "0.4.
|
8
|
+
s.version = "0.4.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Mark Evans"]
|
12
|
-
s.date = %q{2010-01-
|
12
|
+
s.date = %q{2010-01-10}
|
13
13
|
s.email = %q{mark@new-bamboo.co.uk}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE",
|
@@ -1,17 +1,196 @@
|
|
1
1
|
Example Use Cases
|
2
2
|
=================
|
3
3
|
|
4
|
+
This document is concerned with different Dragonfly configurations, for various use cases.
|
5
|
+
|
6
|
+
In a Rails app the configuration would generally be done in an initializer.
|
7
|
+
|
8
|
+
In other Rack-based apps it could be config.ru, or anywhere where the application is generally set up.
|
9
|
+
|
4
10
|
Image thumbnails in Rails
|
5
11
|
-------------------------
|
12
|
+
See {file:UsingWithRails}
|
13
|
+
|
14
|
+
|
15
|
+
Image thumbnails in Rails, hosted on Heroku with S3 storage
|
16
|
+
-----------------------------------------------------------
|
17
|
+
{http://heroku.com Heroku} is a commonly used platform for hosting Rack-based websites.
|
18
|
+
The following assumes your site is set up for deployment onto Heroku.
|
19
|
+
|
20
|
+
As explained in {file:UsingWithRails}, we can use a generator to create an initializer for setting up Dragonfly.
|
21
|
+
|
22
|
+
./script/generate dragonfly_app images
|
23
|
+
|
24
|
+
The default configuration won't work out of the box for Heroku, because
|
25
|
+
|
26
|
+
- Heroku doesn't allow saving files to the filesystem (although it does use tempfiles)
|
27
|
+
- 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
|
28
|
+
|
29
|
+
Instead of the normal {Dragonfly::DataStorage::FileDataStore FileDataStore}, we can use the {Dragonfly::DataStorage::S3DataStore S3DataStore}.
|
30
|
+
Amazon's {http://aws.amazon.com/s3 S3} is a commonly used platform for storing data.
|
31
|
+
|
32
|
+
The following assumes you have an S3 account set up, and know your provided 'access key' and 'secret'.
|
33
|
+
|
34
|
+
Assuming we don't bother with any caching for development/testing environments, our environment.rb then has:
|
35
|
+
|
36
|
+
config.gem 'rmagick', :lib => 'RMagick'
|
37
|
+
config.gem 'dragonfly', :source => "http://www.gemcutter.org"
|
38
|
+
|
39
|
+
(these are ignored by Heroku but you might want them locally)
|
40
|
+
|
41
|
+
The gems file for Heroku, `.gems`, has
|
42
|
+
|
43
|
+
dragonfly
|
44
|
+
|
45
|
+
(rmagick not needed because it is already installed)
|
46
|
+
|
47
|
+
Then in our configuration initializer, we replace
|
48
|
+
|
49
|
+
c.datastore.configure do |d|
|
50
|
+
d.root_path = "#{Rails.root}/public/system/dragonfly/#{Rails.env}"
|
51
|
+
end
|
52
|
+
|
53
|
+
with
|
54
|
+
|
55
|
+
# Use S3 for production
|
56
|
+
if Rails.env == 'production'
|
57
|
+
c.datastore = Dragonfly::DataStorage::S3DataStore.new
|
58
|
+
c.datastore.configure do |d|
|
59
|
+
d.bucket_name = 'my_s3_bucket_name'
|
60
|
+
d.access_key_id = ENV['S3_KEY'] || raise("ENV variable 'S3_KEY' needs to be set")
|
61
|
+
d.secret_access_key = ENV['S3_SECRET'] || raise("ENV variable 'S3_SECRET' needs to be set")
|
62
|
+
end
|
63
|
+
# and filesystem for other environments
|
64
|
+
else
|
65
|
+
c.datastore.configure do |d|
|
66
|
+
d.root_path = "#{Rails.root}/public/system/dragonfly/#{Rails.env}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
We've left the datastore as {Dragonfly::DataStorage::FileDataStore FileDataStore} for non-production environments.
|
71
|
+
|
72
|
+
As you can see we've used `ENV` to store the S3 access key and secret to avoid having them in the repository.
|
73
|
+
Heroku has a {http://docs.heroku.com/config-vars way of setting these} using the command line (we only have to do this once).
|
74
|
+
|
75
|
+
From your app's directory:
|
76
|
+
|
77
|
+
heroku config:add S3_KEY=XXXXXXXXX S3_SECRET=XXXXXXXXXX
|
78
|
+
|
79
|
+
Obviously you replace 'XXXXXXXXX' with your access key and secret.
|
80
|
+
|
81
|
+
Now you can benefit use Dragonfly in the normal way, benefitting from super-fast images served straight from Heroku's cache!
|
82
|
+
|
83
|
+
The only downside is that Heroku's cache is cleared every time you deploy, so if this is an issue you may want to look into using something like
|
84
|
+
a Memcached add-on, or maybe an after-deploy hook for hitting specific Dragonfly urls you want to cache, etc.
|
85
|
+
It won't be a problem for most sites though.
|
86
|
+
|
87
|
+
|
88
|
+
Attaching files to ActiveRecord models with no processing or encoding
|
89
|
+
---------------------------------------------------------------------
|
90
|
+
Although Dragonfly is normally concerned with processing and encoding, you may want to just use it with arbitrary uploaded files
|
91
|
+
(e.g. .doc, .xls, .pdf files, etc.) without processing or encoding them, so as to still benefit from the {file:ActiveRecord ActiveRecord Extensions} API.
|
92
|
+
|
93
|
+
The below shows how to do it in Rails, but the principles are the same in any context.
|
94
|
+
Let's generate a configuration for a Dragonfly App called 'attachments'
|
6
95
|
|
7
|
-
|
8
|
-
|
96
|
+
./script/generate dragonfly_app attachments
|
97
|
+
|
98
|
+
This generates an initializer for configuring the Dragonfly App 'attachments'.
|
99
|
+
|
100
|
+
We won't be using RMagick or Rack::Cache (as there is no processing), so our environment.rb only has:
|
101
|
+
|
102
|
+
config.gem 'dragonfly', :source => 'http://gemcutter.org'
|
103
|
+
|
104
|
+
and in the generated configuration, we DELETE the line
|
105
|
+
|
106
|
+
app.configure_with(Dragonfly::RMagickConfiguration)
|
107
|
+
|
108
|
+
Then in the configure block, we add the lines
|
109
|
+
|
110
|
+
c.url_handler.configure do |u|
|
111
|
+
# ...
|
112
|
+
u.protect_from_dos_attacks = false
|
113
|
+
end
|
114
|
+
c.register_analyser(Dragonfly::Analysis::FileCommandAnalyser)
|
115
|
+
c.register_encoder(Dragonfly::Encoding::TransparentEncoder)
|
116
|
+
|
117
|
+
We don't need to protect the urls from Denial-of-service attacks as we aren't doing any expensive processing.
|
118
|
+
The {Dragonfly::Analysis::FileCommandAnalyser FileCommandAnalyser} is needed to know the mime-type of the content,
|
119
|
+
and the {Dragonfly::Encoding::TransparentEncoder TransparentEncoder} is like a 'dummy' encoder which does nothing
|
120
|
+
(the way to switch off encoding may change in the future).
|
121
|
+
|
122
|
+
If a user uploads a file called 'report.pdf', then normally the original file extension will be lost.
|
123
|
+
Thankfully, to record it is as easy as adding an 'ext' column as well as the usual uid column to our migration
|
124
|
+
(see {file:ActiveRecord} for more info about 'magic attributes'):
|
125
|
+
|
126
|
+
add_column :my_models, :attachment_uid, :string
|
127
|
+
add_column :my_models, :attachment_ext, :string
|
128
|
+
|
129
|
+
Then we include a helper method in our model for setting the correct file extension when we link to the attachment:
|
130
|
+
|
131
|
+
class MyModel < ActiveRecord::Base
|
132
|
+
|
133
|
+
attachment_accessor :attachment
|
134
|
+
|
135
|
+
def url_for_attachment
|
136
|
+
attachment.url :format => attachment_ext
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
Now we can add links to the attached file in our views:
|
141
|
+
|
142
|
+
<%= link_to 'Attachment', @my_model.url_for_attachment %>
|
9
143
|
|
10
|
-
Serving attachments with no processing
|
11
|
-
--------------------------------------
|
12
144
|
|
13
145
|
Generating test data
|
14
146
|
--------------------
|
147
|
+
We may want to generate a load of test data in a test / populate script.
|
148
|
+
|
149
|
+
Each {Dragonfly::App Dragonfly App} has a 'generate' method, which returns an {Dragonfly::ExtendedTempObject ExtendedTempObject} with generated data.
|
150
|
+
The actual generation is delegated to the registered processors (along with any args passed in).
|
151
|
+
|
152
|
+
For example, if our app is registered with the {Dragonfly::Processing::RMagickProcessor RMagickProcessor} (which is already done if using with one of
|
153
|
+
the Rails helper files/generators)
|
154
|
+
|
155
|
+
Dragonfly::App[:my_app].register_processor(Dragonfly::Processing::RMagickProcessor)
|
156
|
+
|
157
|
+
then we can generate images of different sizes/formats):
|
158
|
+
|
159
|
+
image = Dragonfly::App[:my_app].generate(300, 200) # creates a png image of size 300x200 (as an ExtendedTempObject)
|
160
|
+
image.to_file('out.png') # writes to file 'out.png'
|
161
|
+
image = Dragonfly::App[:my_app].generate(50, 50, :gif) # creates a gif image of size 50x50
|
162
|
+
|
15
163
|
|
16
164
|
Text image replacement
|
17
165
|
----------------------
|
166
|
+
A common technique for making sure a specific font is displayed on a website is replacing text with images.
|
167
|
+
|
168
|
+
We can easily use Dragonfly to do this on-the-fly.
|
169
|
+
|
170
|
+
The {Dragonfly::Processing::RMagickProcessor RMagickProcessor} has a 'text' method, which takes content in the form of text,
|
171
|
+
and creates an image of the text, given the options passed in.
|
172
|
+
|
173
|
+
We can also make use of the simple {Dragonfly::DataStorage::TransparentDataStore TransparentDataStore}, where rather than fetch
|
174
|
+
data for a given uid, the uid IS the data.
|
175
|
+
|
176
|
+
The configuration will look something like:
|
177
|
+
|
178
|
+
Dragonfly::App[:text].configure do |c|
|
179
|
+
c.datastore = Dragonfly::DataStorage::TransparentDataStore.new
|
180
|
+
c.register_analyser(Dragonfly::Analysis::FileCommandAnalyser)
|
181
|
+
c.register_processor(Dragonfly::Processing::RMagickProcessor)
|
182
|
+
c.register_encoder(Dragonfly::Encoding::RMagickEncoder)
|
183
|
+
c.parameters.configure do |p|
|
184
|
+
p.default_format = :png
|
185
|
+
p.default_processing_method = :text
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
We need the {Dragonfly::Analysis::FileCommandAnalyser FileCommandAnalyser} for mime-type analysis.
|
190
|
+
|
191
|
+
Then when we visit a url like
|
192
|
+
|
193
|
+
url = Dragonfly::App[:text].url_for('some text', :processing_options => {:font_size => 30, :font_family => 'Monaco'})
|
194
|
+
|
195
|
+
we get a png image of the text. We could easily wrap this in some kind of helper if we use it often.
|
196
|
+
|
data/extra_docs/Index.md
CHANGED
@@ -19,6 +19,7 @@ Use the links at the top-right to navigate around the code, or jump straight to
|
|
19
19
|
- {file:Processing.md Processing}
|
20
20
|
- {file:Encoding.md Encoding}
|
21
21
|
- {file:Shortcuts.md Shortcuts for processing and encoding}
|
22
|
+
- {file:ExampleUseCases.md Example use cases}
|
22
23
|
|
23
24
|
Installation
|
24
25
|
------------
|
@@ -82,3 +82,6 @@ but make sure to change the 'secret' configuration option, so as to protect your
|
|
82
82
|
# :metastore => "file:#{Rails.root}/tmp/dragonfly/cache/meta",
|
83
83
|
# :entitystore => "file:#{Rails.root}/tmp/dragonfly/cache/body"
|
84
84
|
# }
|
85
|
+
|
86
|
+
|
87
|
+
To see what you can do with the active record accessors, see {file:ActiveRecord}.
|
@@ -1,4 +1,25 @@
|
|
1
1
|
module Dragonfly
|
2
|
+
|
3
|
+
# An ExtendedTempObject is just a TempObject that belongs to a Dragonfly App.
|
4
|
+
#
|
5
|
+
# Because of this, it can make use of the app's registered processors, encoders and analysers
|
6
|
+
#
|
7
|
+
# Analysis methods can be accessed on the object itself, e.g. for a registered analysis method 'width', we have
|
8
|
+
# temp_object.width # ===> 280
|
9
|
+
#
|
10
|
+
# Processing methods can be accessed using 'process', e.g. for a registered process method 'resize', we have
|
11
|
+
# temp_object.process(:resize, {:some => 'option'}) # ===> processed ExtendedTempObject
|
12
|
+
#
|
13
|
+
# Similarly, encoding with the method 'encode' delegates to the app's registered encoders
|
14
|
+
# temp_object.encode(:png, {:some => 'option'}) # ===> encoded ExtendedTempObject
|
15
|
+
#
|
16
|
+
# We can use bang methods to operate on the temp_object itself, rather than return a new one:
|
17
|
+
# temp_object.process!(:resize, {:some => 'option'})
|
18
|
+
# temp_object.encode!(:png, {:some => 'option'})
|
19
|
+
#
|
20
|
+
# You can make use of the app's registered parameter shortcuts (for processing and encoding in one go) using 'transform', e.g.
|
21
|
+
# temp_object.transform('300x200', :gif) # ===> return a new processed and encoded ExtendedTempObject
|
22
|
+
# temp_object.transform!('300x200', :gif) # ===> operate on self
|
2
23
|
class ExtendedTempObject < TempObject
|
3
24
|
|
4
25
|
# Exceptions
|
data/lib/dragonfly/parameters.rb
CHANGED
@@ -52,8 +52,8 @@ module Dragonfly
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def from_args(*args)
|
55
|
-
if args.empty? then
|
56
|
-
elsif args.length == 1 && args.first.is_a?(Hash) then
|
55
|
+
if args.empty? then new_with_defaults
|
56
|
+
elsif args.length == 1 && args.first.is_a?(Hash) then new_with_defaults(args.first)
|
57
57
|
elsif args.length == 1 && args.first.is_a?(Parameters) then args.first.dup
|
58
58
|
else from_shortcut(*args)
|
59
59
|
end
|
@@ -1,6 +1,32 @@
|
|
1
1
|
require 'tempfile'
|
2
2
|
|
3
3
|
module Dragonfly
|
4
|
+
|
5
|
+
# A TempObject is used for HOLDING DATA.
|
6
|
+
# It's the thing that is passed between the datastore, the processor and the encoder, and is useful
|
7
|
+
# for separating how the data was created and how it is later accessed.
|
8
|
+
#
|
9
|
+
# You can initialize it various ways:
|
10
|
+
#
|
11
|
+
# temp_object = Dragonfly::TempObject.new('this is the content') # with a String
|
12
|
+
# temp_object = Dragonfly::TempObject.new(File.new('path/to/content')) # with a File
|
13
|
+
# temp_object = Dragonfly::TempObject.new(some_tempfile) # with a Tempfile
|
14
|
+
# temp_object = Dragonfly::TempObject.new(some_other_temp_object) # with another TempObject
|
15
|
+
#
|
16
|
+
# However, no matter how it was initialized, you can always access the data a number of ways:
|
17
|
+
#
|
18
|
+
# temp_object.data # returns a data string
|
19
|
+
# temp_object.file # returns a file object holding the data
|
20
|
+
# temp_object.path # returns a path for the file
|
21
|
+
#
|
22
|
+
# The data/file are created lazily, something which you may wish to take advantage of.
|
23
|
+
#
|
24
|
+
# For example, if a TempObject is initialized with a file, and temp_object.data is never called, then
|
25
|
+
# the data string will never be loaded into memory.
|
26
|
+
#
|
27
|
+
# Conversely, if the TempObject is initialized with a data string, and neither temp_object.file nor temp_object.path
|
28
|
+
# are ever called, then the filesystem will never be hit.
|
29
|
+
#
|
4
30
|
class TempObject
|
5
31
|
|
6
32
|
# Class configuration
|
@@ -231,13 +231,13 @@ describe Dragonfly::Parameters do
|
|
231
231
|
@parameters_class.default_format = :tif
|
232
232
|
end
|
233
233
|
|
234
|
-
it "should be the same as '
|
235
|
-
@parameters_class.from_args.should == @parameters_class.
|
234
|
+
it "should be the same as 'new_with_defaults' if empty args" do
|
235
|
+
@parameters_class.from_args.should == @parameters_class.new_with_defaults
|
236
236
|
end
|
237
237
|
|
238
|
-
it "should treat the arguments as actual parameter values if args is a single hash" do
|
238
|
+
it "should treat the arguments as actual parameter values (including defaults) if args is a single hash" do
|
239
239
|
@parameters_class.from_args(:uid => 'some_uid', :processing_method => :resize).
|
240
|
-
should == @parameters_class.
|
240
|
+
should == @parameters_class.new_with_defaults(:uid => 'some_uid', :processing_method => :resize)
|
241
241
|
end
|
242
242
|
|
243
243
|
it "should simply return the same parameters if args is a single parameters object" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dragonfly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Evans
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-10 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|