dragonfly 0.6.2 → 0.7.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.

Files changed (157) hide show
  1. data/.gitignore +2 -0
  2. data/.specopts +2 -0
  3. data/.yardopts +11 -5
  4. data/Gemfile +22 -0
  5. data/Gemfile.rails.2.3.5 +13 -0
  6. data/History.md +49 -0
  7. data/README.md +18 -28
  8. data/Rakefile +24 -36
  9. data/VERSION +1 -1
  10. data/config.ru +4 -1
  11. data/dragonfly.gemspec +85 -99
  12. data/extra_docs/Analysers.md +66 -30
  13. data/extra_docs/Caching.md +22 -0
  14. data/extra_docs/Configuration.md +116 -0
  15. data/extra_docs/DataStorage.md +114 -14
  16. data/extra_docs/Encoding.md +62 -37
  17. data/extra_docs/GeneralUsage.md +118 -0
  18. data/extra_docs/Generators.md +92 -0
  19. data/extra_docs/Heroku.md +51 -0
  20. data/extra_docs/Index.md +8 -9
  21. data/extra_docs/MimeTypes.md +18 -17
  22. data/extra_docs/Models.md +251 -0
  23. data/extra_docs/Processing.md +94 -70
  24. data/extra_docs/Rack.md +53 -0
  25. data/extra_docs/Rails2.md +44 -0
  26. data/extra_docs/Rails3.md +51 -0
  27. data/extra_docs/Sinatra.md +21 -0
  28. data/extra_docs/URLs.md +114 -0
  29. data/features/images.feature +6 -7
  30. data/features/no_processing.feature +0 -6
  31. data/features/rails_2.3.5.feature +1 -1
  32. data/features/rails_3.0.0.rc.feature +8 -0
  33. data/features/steps/dragonfly_steps.rb +14 -12
  34. data/features/steps/rails_steps.rb +20 -9
  35. data/features/support/env.rb +10 -11
  36. data/fixtures/files/app/views/albums/new.html.erb +4 -4
  37. data/fixtures/files/app/views/albums/show.html.erb +1 -1
  38. data/fixtures/files/features/manage_album_images.feature +1 -1
  39. data/fixtures/files/features/step_definitions/{album_steps.rb → image_steps.rb} +4 -3
  40. data/fixtures/files/features/support/paths.rb +2 -0
  41. data/fixtures/files/features/text_images.feature +7 -0
  42. data/fixtures/rails_3.0.0.rc/template.rb +21 -0
  43. data/irbrc.rb +2 -1
  44. data/lib/dragonfly.rb +4 -16
  45. data/lib/dragonfly/{active_record_extensions.rb → active_model_extensions.rb} +1 -1
  46. data/lib/dragonfly/active_model_extensions/attachment.rb +146 -0
  47. data/lib/dragonfly/{active_record_extensions → active_model_extensions}/class_methods.rb +5 -6
  48. data/lib/dragonfly/{active_record_extensions → active_model_extensions}/instance_methods.rb +1 -1
  49. data/lib/dragonfly/{active_record_extensions → active_model_extensions}/validations.rb +5 -9
  50. data/lib/dragonfly/analyser.rb +59 -0
  51. data/lib/dragonfly/analysis/file_command_analyser.rb +1 -1
  52. data/lib/dragonfly/analysis/r_magick_analyser.rb +46 -31
  53. data/lib/dragonfly/app.rb +138 -173
  54. data/lib/dragonfly/config/heroku.rb +19 -0
  55. data/lib/dragonfly/config/r_magick.rb +37 -0
  56. data/lib/dragonfly/config/{rails_defaults.rb → rails.rb} +6 -7
  57. data/lib/dragonfly/configurable.rb +30 -27
  58. data/lib/dragonfly/core_ext/object.rb +1 -1
  59. data/lib/dragonfly/data_storage/file_data_store.rb +59 -26
  60. data/lib/dragonfly/data_storage/mongo_data_store.rb +65 -0
  61. data/lib/dragonfly/data_storage/s3data_store.rb +31 -12
  62. data/lib/dragonfly/encoder.rb +13 -0
  63. data/lib/dragonfly/encoding/r_magick_encoder.rb +10 -19
  64. data/lib/dragonfly/endpoint.rb +43 -0
  65. data/lib/dragonfly/function_manager.rb +65 -0
  66. data/lib/dragonfly/{processing/r_magick_text_processor.rb → generation/r_magick_generator.rb} +25 -11
  67. data/lib/dragonfly/generator.rb +9 -0
  68. data/lib/dragonfly/job.rb +290 -0
  69. data/lib/dragonfly/job_builder.rb +39 -0
  70. data/lib/dragonfly/job_definitions.rb +26 -0
  71. data/lib/dragonfly/job_endpoint.rb +17 -0
  72. data/lib/dragonfly/loggable.rb +28 -0
  73. data/lib/dragonfly/middleware.rb +21 -14
  74. data/lib/dragonfly/processing/r_magick_processor.rb +71 -48
  75. data/lib/dragonfly/processor.rb +9 -0
  76. data/lib/dragonfly/r_magick_utils.rb +24 -0
  77. data/lib/dragonfly/rails/images.rb +10 -7
  78. data/lib/dragonfly/routed_endpoint.rb +42 -0
  79. data/lib/dragonfly/serializer.rb +32 -0
  80. data/lib/dragonfly/simple_cache.rb +23 -0
  81. data/lib/dragonfly/simple_endpoint.rb +64 -0
  82. data/lib/dragonfly/temp_object.rb +77 -45
  83. data/spec/argument_matchers.rb +7 -17
  84. data/spec/dragonfly/active_model_extensions/active_model_setup.rb +97 -0
  85. data/spec/dragonfly/active_model_extensions/active_record_setup.rb +85 -0
  86. data/spec/dragonfly/{active_record_extensions → active_model_extensions}/model_spec.rb +282 -244
  87. data/spec/dragonfly/active_model_extensions/spec_helper.rb +11 -0
  88. data/spec/dragonfly/analyser_spec.rb +123 -0
  89. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +2 -2
  90. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +10 -1
  91. data/spec/dragonfly/app_spec.rb +175 -69
  92. data/spec/dragonfly/configurable_spec.rb +14 -0
  93. data/spec/dragonfly/data_storage/data_store_spec.rb +36 -9
  94. data/spec/dragonfly/data_storage/file_data_store_spec.rb +61 -38
  95. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +18 -0
  96. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +34 -39
  97. data/spec/dragonfly/deprecation_spec.rb +20 -0
  98. data/spec/dragonfly/function_manager_spec.rb +154 -0
  99. data/spec/dragonfly/generation/r_magick_generator_spec.rb +119 -0
  100. data/spec/dragonfly/job_builder_spec.rb +37 -0
  101. data/spec/dragonfly/job_definitions_spec.rb +35 -0
  102. data/spec/dragonfly/job_endpoint_spec.rb +66 -0
  103. data/spec/dragonfly/job_spec.rb +605 -0
  104. data/spec/dragonfly/loggable_spec.rb +80 -0
  105. data/spec/dragonfly/middleware_spec.rb +37 -17
  106. data/spec/dragonfly/processing/r_magick_processor_spec.rb +182 -166
  107. data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
  108. data/spec/dragonfly/serializer_spec.rb +61 -0
  109. data/spec/dragonfly/simple_cache_spec.rb +27 -0
  110. data/spec/dragonfly/simple_endpoint_spec.rb +78 -0
  111. data/spec/dragonfly/temp_object_spec.rb +154 -119
  112. data/spec/simple_matchers.rb +22 -0
  113. data/spec/spec_helper.rb +28 -4
  114. data/yard/templates/default/layout/html/layout.erb +18 -11
  115. metadata +89 -190
  116. data/config.rb +0 -5
  117. data/extra_docs/ActiveRecord.md +0 -196
  118. data/extra_docs/ExampleUseCases.md +0 -189
  119. data/extra_docs/GettingStarted.md +0 -114
  120. data/extra_docs/Shortcuts.md +0 -118
  121. data/extra_docs/UsingWithRails.md +0 -81
  122. data/features/rails_3.0.0.beta3.feature +0 -7
  123. data/fixtures/rails_3.0.0.beta3/template.rb +0 -16
  124. data/lib/dragonfly/active_record_extensions/attachment.rb +0 -170
  125. data/lib/dragonfly/analyser_list.rb +0 -9
  126. data/lib/dragonfly/analysis/base.rb +0 -10
  127. data/lib/dragonfly/belongs_to_app.rb +0 -24
  128. data/lib/dragonfly/config/heroku_rails_images.rb +0 -23
  129. data/lib/dragonfly/config/r_magick_images.rb +0 -69
  130. data/lib/dragonfly/config/r_magick_text.rb +0 -25
  131. data/lib/dragonfly/config/rails_images.rb +0 -13
  132. data/lib/dragonfly/data_storage/base.rb +0 -21
  133. data/lib/dragonfly/data_storage/base64_data_store.rb +0 -23
  134. data/lib/dragonfly/data_storage/transparent_data_store.rb +0 -21
  135. data/lib/dragonfly/delegatable.rb +0 -14
  136. data/lib/dragonfly/delegator.rb +0 -62
  137. data/lib/dragonfly/encoder_list.rb +0 -9
  138. data/lib/dragonfly/encoding/base.rb +0 -14
  139. data/lib/dragonfly/encoding/transparent_encoder.rb +0 -14
  140. data/lib/dragonfly/extended_temp_object.rb +0 -120
  141. data/lib/dragonfly/parameters.rb +0 -163
  142. data/lib/dragonfly/processing/base.rb +0 -10
  143. data/lib/dragonfly/processor_list.rb +0 -9
  144. data/lib/dragonfly/url_handler.rb +0 -147
  145. data/spec/dragonfly/active_record_extensions/attachment_spec.rb +0 -8
  146. data/spec/dragonfly/active_record_extensions/migration.rb +0 -42
  147. data/spec/dragonfly/active_record_extensions/models.rb +0 -6
  148. data/spec/dragonfly/active_record_extensions/spec_helper.rb +0 -24
  149. data/spec/dragonfly/belongs_to_app_spec.rb +0 -55
  150. data/spec/dragonfly/delegatable_spec.rb +0 -32
  151. data/spec/dragonfly/delegator_spec.rb +0 -145
  152. data/spec/dragonfly/extended_temp_object_spec.rb +0 -71
  153. data/spec/dragonfly/parameters_spec.rb +0 -298
  154. data/spec/dragonfly/processing/r_magick_text_processor_spec.rb +0 -84
  155. data/spec/dragonfly/url_handler_spec.rb +0 -247
  156. data/spec/dragonfly_spec.rb +0 -16
  157. data/spec/ginger_scenarios.rb +0 -13
@@ -0,0 +1,118 @@
1
+ General Usage
2
+ =============
3
+
4
+ You can have multiple dragonfly apps, each with their own configuration.
5
+ Each app has a name, and is referred to by that name.
6
+
7
+ Dragonfly[:images] # ===> Creates an app called 'images'
8
+ Dragonfly[:images] # ===> Refers to the already created app 'images'
9
+
10
+ app = Dragonfly[:images]
11
+
12
+ Getting/generating content
13
+ --------------------------
14
+ Three methods can be used to get content:
15
+
16
+ app.fetch('some_uid') # Fetch from datastore (default filesystem)
17
+
18
+ app.fetch_file('~/path/to/file.png') # Fetch from a local file
19
+
20
+ app.generate(:plasma, 400, 300) # Generates using a method from the configured
21
+ # generator (in this case a plasma image)
22
+
23
+ These all return {Dragonfly::Job Job} objects. These objects are lazy - they don't do any fetching/generating until
24
+ some other method is called on them.
25
+
26
+ Using the content
27
+ -----------------
28
+ Once we have a {Dragonfly::Job Job} object:
29
+
30
+ image = app.fetch('some_uid')
31
+
32
+ We can get the data a number of ways...
33
+
34
+ image.data # => "\377???JFIF\000\..."
35
+ image.to_file('out.png') # writes to file 'out.png' and returns a readable file object
36
+ image.tempfile # => #<File:/var/folders/st/strHv74sH044JPabSiODz... a closed Tempfile object
37
+ image.file # => #<File:/var/folders/st/strHv74sH044JPabSiODz... a readable (open) File object
38
+ image.file do |f| # Yields an open file object, returns the return value of
39
+ data = f.read(256) # the block, and closes the file object
40
+ end
41
+ image.path # => '/var/folders/st/strHv74sH044JPabSiODz...' i.e. the path of the tempfile
42
+ image.size # => 134507 (size in bytes)
43
+
44
+ We can get its url...
45
+
46
+ image.url # => "/media/BAhbBlsHOgZmIg9hc..."
47
+
48
+ We can analyse it (see {file:Analysers} for more info) ...
49
+
50
+ image.width # => 280
51
+
52
+ We can process it (see {file:Processing} for more info) ...
53
+
54
+ new_image = image.process(:thumb, '40x30') # returns another 'Job' object
55
+
56
+ We can encode it (see {file:Encoding} for more info) ...
57
+
58
+ new_image = image.encode(:gif) # returns another 'Job' object
59
+
60
+ Chaining
61
+ --------
62
+ Because the methods
63
+
64
+ - `fetch`
65
+
66
+ - `fetch_file`
67
+
68
+ - `generate`
69
+
70
+ - `process`
71
+
72
+ - `encode`
73
+
74
+ all return {Dragonfly::Job Job} objects, we can chain them as much as we want...
75
+
76
+ image = app.fetch('some_uid').process(:greyscale).process(:thumb, '40x20#').encode(:gif)
77
+
78
+ ... and because they're lazy, we don't actually do any processing/encoding until either `apply` is called
79
+
80
+ image.apply # actually 'does' the processing and returns self
81
+
82
+ ... or a method is called like `data`, `to_file`, etc.
83
+
84
+ This means we can cheaply generate urls for processed data without doing any fetching or processing:
85
+
86
+ url = app.fetch('some_uid').process(:thumb, '40x20#').encode(:gif).url
87
+
88
+ and then visit that url in a browser to get the actual processed image.
89
+
90
+ Shortcuts
91
+ ---------
92
+ Commonly used processing/encoding steps can be shortened, so instead of
93
+
94
+ app.fetch('some_uid').process(:greyscale).process(:thumb, '40x20#').encode(:jpg)
95
+
96
+ we could use something like
97
+
98
+ app.fetch('some_uid').grey('40x20#')
99
+
100
+ This does exactly the same, returning a {Dragonfly::Job Job} object.
101
+
102
+ To define this shortcut:
103
+
104
+ app.configure do |c|
105
+ c.job :grey do |size|
106
+ process :greyscale
107
+ process :thumb, size
108
+ encode :jpg
109
+ end
110
+ # ...
111
+ end
112
+
113
+ The {Dragonfly::Config::RMagick RMagick} 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)
@@ -0,0 +1,92 @@
1
+ Generators
2
+ ==========
3
+
4
+ Unlike processors and encoders, generators create content out of nothing, rather than modifying already existing content, for example text image generation.
5
+
6
+ You can register as many generators as you like.
7
+
8
+ Given a Dragonfly app
9
+
10
+ app = Dragonfly[:images]
11
+
12
+ we can get generated content using
13
+
14
+ image = app.generate(:some_method, :some => :args)
15
+
16
+ where `:some_method` is added by the configured generators.
17
+
18
+ RMagickGenerator
19
+ ----------------
20
+ The {Dragonfly::Generation::RMagickGenerator RMagickGenerator} is registered by default by the
21
+ {Dragonfly::Config::RMagick RMagick configuration} used by 'dragonfly/rails/images'.
22
+
23
+ If not already registered:
24
+
25
+ app.generator.register(Dragonfly::Generation::RMagickGenerator)
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/RMagick/doc/draw.html#font}), which could be a complete filename.
52
+ Available fonts are those available on your system.
53
+
54
+ Custom Generators
55
+ -----------------
56
+ To register a single custom generator:
57
+
58
+ app.generator.add :blank_image do |colour|
59
+ SomeLibrary.create_blank_image(colour) # return a String, File or Tempfile
60
+ end
61
+
62
+ app.generate(:blank_image, 'red') # => 'Job' object which we can get data, etc.
63
+
64
+
65
+ Or create a class like the RMagick one above, in which case all public methods will be counted as generator methods.
66
+
67
+ class RoundedCornerGenerator
68
+
69
+ def top_left_corner(opts={})
70
+ SomeLib.tlc(opts)
71
+ end
72
+
73
+ def bottom_right_corner(opts={})
74
+ tempfile = Tempfile.new('brc')
75
+ `some_command -c #{opts[:colour]} -o #{tempfile.path}`
76
+ tempfile
77
+ end
78
+
79
+ # ...
80
+
81
+ private
82
+
83
+ def my_helper_method
84
+ # do stuff
85
+ end
86
+
87
+ end
88
+
89
+ app.generator.register(RoundedCornerGenerator)
90
+
91
+ app.generate(:top_left_corner, :colour => 'green')
92
+ app.generate(:bottom_right_corner, :colour => 'mauve')
@@ -0,0 +1,51 @@
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
+ - rmagick (require as 'RMagick')
16
+ - aws-s3 (require as aws/s3)
17
+ - dragonfly
18
+
19
+ Initializer (e.g. config/initializers/dragonfly.rb):
20
+
21
+ require 'dragonfly'
22
+ app = Dragonfly[:images]
23
+
24
+ app.configure_with(:rmagick)
25
+ app.configure_with(:rails)
26
+ app.configure_with(:heroku, 'my_bucket_name') if Rails.env.production?
27
+
28
+ app.define_macro(ActiveRecord::Base, :image_accessor)
29
+
30
+ The datastore remains as the {Dragonfly::DataStorage::FileDataStore FileDataStore} for non-production environments.
31
+
32
+ environment.rb (application.rb in Rails 3):
33
+
34
+ config.middleware.insert 0, 'Dragonfly::Middleware', :images, '/media' # make sure this is the same
35
+ # as the app's configured prefix
36
+
37
+ We don't store the S3 access key and secret in the repository, rather we use Heroku's
38
+ {http://docs.heroku.com/config-vars config variables} using the command line (we only have to do this once).
39
+
40
+ From your app's directory:
41
+
42
+ heroku config:add S3_KEY=XXXXXXXXX S3_SECRET=XXXXXXXXXX
43
+
44
+ Obviously replace 'XXXXXXXXX' with your access key and secret.
45
+
46
+ Now you can benefit from super-fast images served straight from Heroku's cache!
47
+
48
+ NOTE: HEROKU'S CACHE IS CLEARED EVERY TIME YOU DEPLOY!!!
49
+
50
+ 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.
51
+ It won't be a problem for most sites though.
data/extra_docs/Index.md CHANGED
@@ -1,19 +1,17 @@
1
1
  Dragonfly Documentation
2
2
  =======================
3
+ Dragonfly is a {http://rack.rubyforge.org Rack} framework for on-the-fly image handling in Ruby.
3
4
 
4
- Welcome to the documentation for Dragonfly!
5
+ It is suitable for using with web frameworks such as Rails(2.3 and 3), Sinatra, etc.
5
6
 
6
- Dragonfly is a {http://rack.rubyforge.org Rack} framework for on-the-fly processing and encoding.
7
+ I actually lied about image handling - it can be used for any type of content.
7
8
 
8
- It includes an extension for Ruby on Rails to enable easy image handling.
9
- It is intended to be highly customizable, and is not limited to images, but any data type that could suit on-the-fly processing/encoding.
10
-
11
- Use the dropdowns at the top-right to navigate around the code, or jump straight to one of the guides on the right.
9
+ See the links on the right for more info.
12
10
 
13
11
  Installation
14
12
  ------------
15
13
 
16
- gem install dragonfly --source=http://gemcutter.org
14
+ gem install dragonfly
17
15
 
18
16
  Issues
19
17
  ------
@@ -25,8 +23,9 @@ Suggestions/Questions
25
23
 
26
24
  Credits
27
25
  -------
28
- - <a href="http://github.com/markevans">Mark Evans</a> (author)
26
+ - [Mark Evans](http://github.com/markevans) (author)
27
+ - Loads of helpful comments, issues, questions, suggestions and insults from others - you know who you are!
29
28
 
30
29
  Copyright
31
30
  ---------
32
- Copyright (c) 2009 Mark Evans. See LICENSE for details.
31
+ Copyright (c) 2009-2010 Mark Evans. See LICENSE for details.
@@ -3,37 +3,38 @@ Mime Types
3
3
 
4
4
  Responses from the Dragonfly app have the HTTP 'Content-Type' header set.
5
5
 
6
- Suppose we request the url '/media/some_uid.jpg'.
7
- The mime-type is looked for in the following order (and the first found is used):
6
+ This is decided by the first found from:
8
7
 
9
- 1. The app's registered mime-types
10
- 2. Analyse the content using the analyser's 'mime_type' method (if exists)
11
- 3. Use the fallback mime-type (default 'application/octet-stream')
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.
12
16
 
13
17
  Registered mime-types
14
18
  ---------------------
15
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}).
16
20
 
17
- To register a mime-type for the format 'egg', you can do the following:
21
+ To register a mime-type for the format 'egg':
18
22
 
19
- Dragonfly::App[:my_app].register_mime_type(:egg, 'fried/egg')
23
+ Dragonfly[:my_app].register_mime_type(:egg, 'fried/egg')
20
24
 
21
25
  You can also do this inside a configuration block.
22
26
 
23
- Mime-type analysis
24
- ------------------
25
- The {Dragonfly::Analysis::FileCommandAnalyser FileCommandAnalyser} has a `mime_type` method which will return the
26
- mime-type of any given content.
27
-
28
- If this, or any other analyser that has the `mime_type` method, is registered, then this is used when no mime-type
29
- is found in the registered list.
27
+ Analysers
28
+ ---------
29
+ The {Dragonfly::Analysis::FileCommandAnalyser FileCommandAnalyser} has a `mime_type` method and the
30
+ {Dragonfly::Analysis::RMagickAnalyser RMagickAnalyser} has a `format` method.
30
31
 
31
- The FileCommandAnalyser is registered by default when you use the preconfigured 'dragonfly/rails/images' file.
32
+ These are both registered by default when you use the preconfigured 'dragonfly/rails/images' file.
32
33
 
33
34
  Fallback mime-type
34
35
  ------------------
35
- By default this is 'application/octet-stream', but it can be changed using the configuration method on the app
36
+ By default this is 'application/octet-stream', but it can be changed using
36
37
 
37
- Dragonfly::App[:my_app].fallback_mime_type = 'meaty/beef'
38
+ Dragonfly[:my_app].fallback_mime_type = 'meaty/beef'
38
39
 
39
40
  This can also be done inside a configuration block.
@@ -0,0 +1,251 @@
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
+
178
+ 'Magic' Attributes
179
+ ------------------
180
+ An accessor like `cover_image` only relies on the accessor `cover_image_uid` to work.
181
+ However, in some cases you may want to record some other properties, whether it be for using in queries, or
182
+ for caching an attribute for performance reasons, etc.
183
+
184
+ For the properties `name`, `ext`, `size` and any of the registered analysis methods (e.g. `width`, etc. in the examples above),
185
+ this is done automatically for you, if the corresponding accessor exists.
186
+
187
+ For example - with ActiveRecord, given the migration:
188
+
189
+ add_column :albums, :cover_image_width, :integer
190
+
191
+ This will automatically be set when assigned:
192
+
193
+ @album.cover_image = File.new('path/to/my_image.png')
194
+
195
+ @album.cover_image_width # => 280
196
+
197
+ They can be used to avoid retrieving data from the datastore for analysis
198
+
199
+ @album = Album.first
200
+
201
+ @album.cover_image.width # => 280 - no need to retrieve data - takes it from `cover_image_width`
202
+ @album.cover_image.size # => 134507 - but this needs to retrieve data from the data store, then analyse
203
+
204
+
205
+ Custom Model
206
+ ------------
207
+ The accessors only require that your model class implements `before_save`, `before_destroy` and `validates_each`
208
+ (if using validations), as well as of course the `..._uid` field for storing the datastore uid.
209
+
210
+ Here is an example of a minimal ActiveModel `Album` model:
211
+
212
+ class CustomModel::Base
213
+
214
+ extend ActiveModel::Callbacks
215
+ define_model_callbacks :save, :destroy
216
+
217
+ include ActiveModel::Validations # if needed
218
+
219
+ def save
220
+ _run_save_callbacks {
221
+ # do some saving!
222
+ }
223
+ end
224
+
225
+ def destroy
226
+ _run_destroy_callbacks {
227
+ # do some destroying!
228
+ }
229
+ end
230
+
231
+ end
232
+
233
+ Define our `image_accessor` macro...
234
+
235
+ app.define_macro(CustomModel::Base, :image_accessor)
236
+
237
+ ...which is used by `Album`:
238
+
239
+ class Album < CustomModel::Base
240
+
241
+ def cover_image_uid=
242
+ # ...
243
+ end
244
+
245
+ def cover_image_uid
246
+ # ...
247
+ end
248
+
249
+ image_accessor :cover_image
250
+
251
+ end