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.

Files changed (155) hide show
  1. data/{.specopts → .rspec} +0 -1
  2. data/.yardopts +6 -2
  3. data/Gemfile +14 -13
  4. data/History.md +47 -9
  5. data/README.md +25 -5
  6. data/Rakefile +37 -79
  7. data/VERSION +1 -1
  8. data/dragonfly.gemspec +140 -89
  9. data/extra_docs/Analysers.md +8 -48
  10. data/extra_docs/Configuration.md +40 -25
  11. data/extra_docs/Couch.md +49 -0
  12. data/extra_docs/DataStorage.md +94 -24
  13. data/extra_docs/Encoding.md +6 -35
  14. data/extra_docs/ExampleUseCases.md +113 -0
  15. data/extra_docs/GeneralUsage.md +7 -23
  16. data/extra_docs/Generators.md +15 -49
  17. data/extra_docs/Heroku.md +7 -8
  18. data/extra_docs/ImageMagick.md +126 -0
  19. data/extra_docs/MimeTypes.md +3 -3
  20. data/extra_docs/Models.md +163 -0
  21. data/extra_docs/Mongo.md +1 -4
  22. data/extra_docs/Processing.md +7 -60
  23. data/extra_docs/Rails2.md +3 -1
  24. data/extra_docs/Rails3.md +2 -10
  25. data/extra_docs/ServingRemotely.md +83 -0
  26. data/extra_docs/Sinatra.md +3 -3
  27. data/extra_docs/URLs.md +60 -33
  28. data/features/rails_3.0.5.feature +8 -0
  29. data/features/steps/rails_steps.rb +7 -18
  30. data/features/support/env.rb +10 -37
  31. data/features/support/setup.rb +32 -0
  32. data/fixtures/rails_3.0.5/files/app/models/album.rb +5 -0
  33. data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
  34. data/fixtures/{files → rails_3.0.5/files}/app/views/albums/show.html.erb +2 -0
  35. data/fixtures/{files → rails_3.0.5/files}/config/initializers/dragonfly.rb +0 -0
  36. data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
  37. data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
  38. data/fixtures/{files → rails_3.0.5/files}/features/step_definitions/image_steps.rb +11 -1
  39. data/fixtures/{files → rails_3.0.5/files}/features/support/paths.rb +2 -0
  40. data/fixtures/{files → rails_3.0.5/files}/features/text_images.feature +0 -0
  41. data/fixtures/{rails_3.0.3 → rails_3.0.5}/template.rb +2 -2
  42. data/irbrc.rb +2 -1
  43. data/lib/dragonfly.rb +7 -0
  44. data/lib/dragonfly/active_model_extensions/attachment.rb +134 -46
  45. data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +144 -0
  46. data/lib/dragonfly/active_model_extensions/class_methods.rb +62 -9
  47. data/lib/dragonfly/active_model_extensions/instance_methods.rb +2 -2
  48. data/lib/dragonfly/active_model_extensions/validations.rb +10 -6
  49. data/lib/dragonfly/analyser.rb +0 -1
  50. data/lib/dragonfly/analysis/file_command_analyser.rb +1 -1
  51. data/lib/dragonfly/analysis/image_magick_analyser.rb +2 -43
  52. data/lib/dragonfly/app.rb +64 -55
  53. data/lib/dragonfly/config/heroku.rb +1 -1
  54. data/lib/dragonfly/config/image_magick.rb +2 -37
  55. data/lib/dragonfly/config/rails.rb +5 -2
  56. data/lib/dragonfly/configurable.rb +115 -35
  57. data/lib/dragonfly/core_ext/object.rb +1 -1
  58. data/lib/dragonfly/core_ext/string.rb +1 -1
  59. data/lib/dragonfly/data_storage/couch_data_store.rb +84 -0
  60. data/lib/dragonfly/data_storage/file_data_store.rb +43 -18
  61. data/lib/dragonfly/data_storage/mongo_data_store.rb +8 -4
  62. data/lib/dragonfly/data_storage/s3data_store.rb +82 -38
  63. data/lib/dragonfly/encoding/image_magick_encoder.rb +2 -53
  64. data/lib/dragonfly/function_manager.rb +4 -2
  65. data/lib/dragonfly/generation/image_magick_generator.rb +2 -136
  66. data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
  67. data/lib/dragonfly/image_magick/analyser.rb +51 -0
  68. data/lib/dragonfly/image_magick/config.rb +44 -0
  69. data/lib/dragonfly/{encoding/r_magick_encoder.rb → image_magick/encoder.rb} +10 -14
  70. data/lib/dragonfly/image_magick/generator.rb +145 -0
  71. data/lib/dragonfly/image_magick/processor.rb +104 -0
  72. data/lib/dragonfly/image_magick/utils.rb +72 -0
  73. data/lib/dragonfly/image_magick_utils.rb +2 -79
  74. data/lib/dragonfly/job.rb +152 -90
  75. data/lib/dragonfly/middleware.rb +5 -19
  76. data/lib/dragonfly/processing/image_magick_processor.rb +2 -95
  77. data/lib/dragonfly/rails/images.rb +15 -10
  78. data/lib/dragonfly/response.rb +26 -12
  79. data/lib/dragonfly/serializer.rb +1 -4
  80. data/lib/dragonfly/server.rb +103 -0
  81. data/lib/dragonfly/temp_object.rb +56 -101
  82. data/lib/dragonfly/url_mapper.rb +78 -0
  83. data/spec/dragonfly/active_model_extensions/model_spec.rb +772 -65
  84. data/spec/dragonfly/active_model_extensions/spec_helper.rb +90 -10
  85. data/spec/dragonfly/analyser_spec.rb +1 -1
  86. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +5 -14
  87. data/spec/dragonfly/app_spec.rb +35 -180
  88. data/spec/dragonfly/configurable_spec.rb +259 -18
  89. data/spec/dragonfly/core_ext/string_spec.rb +2 -2
  90. data/spec/dragonfly/core_ext/symbol_spec.rb +1 -1
  91. data/spec/dragonfly/data_storage/couch_data_store_spec.rb +84 -0
  92. data/spec/dragonfly/data_storage/file_data_store_spec.rb +149 -22
  93. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +21 -2
  94. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +207 -43
  95. data/spec/dragonfly/data_storage/{data_store_spec.rb → shared_data_store_examples.rb} +16 -15
  96. data/spec/dragonfly/function_manager_spec.rb +2 -2
  97. data/spec/dragonfly/{generation/hash_with_css_style_keys_spec.rb → hash_with_css_style_keys_spec.rb} +2 -2
  98. data/spec/dragonfly/{analysis/shared_analyser_spec.rb → image_magick/analyser_spec.rb} +19 -6
  99. data/spec/dragonfly/{encoding/image_magick_encoder_spec.rb → image_magick/encoder_spec.rb} +2 -2
  100. data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
  101. data/spec/dragonfly/{processing/shared_processing_spec.rb → image_magick/processor_spec.rb} +55 -6
  102. data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
  103. data/spec/dragonfly/job_builder_spec.rb +1 -1
  104. data/spec/dragonfly/job_definitions_spec.rb +1 -1
  105. data/spec/dragonfly/job_endpoint_spec.rb +26 -3
  106. data/spec/dragonfly/job_spec.rb +426 -208
  107. data/spec/dragonfly/loggable_spec.rb +2 -2
  108. data/spec/dragonfly/middleware_spec.rb +5 -26
  109. data/spec/dragonfly/routed_endpoint_spec.rb +1 -1
  110. data/spec/dragonfly/serializer_spec.rb +1 -14
  111. data/spec/dragonfly/server_spec.rb +261 -0
  112. data/spec/dragonfly/simple_cache_spec.rb +1 -1
  113. data/spec/dragonfly/temp_object_spec.rb +84 -130
  114. data/spec/dragonfly/url_mapper_spec.rb +130 -0
  115. data/spec/functional/deprecations_spec.rb +51 -0
  116. data/spec/functional/image_magick_app_spec.rb +27 -0
  117. data/spec/functional/model_urls_spec.rb +85 -0
  118. data/spec/functional/remote_on_the_fly_spec.rb +51 -0
  119. data/spec/functional/to_response_spec.rb +31 -0
  120. data/spec/spec_helper.rb +12 -22
  121. data/spec/{argument_matchers.rb → support/argument_matchers.rb} +0 -0
  122. data/spec/{image_matchers.rb → support/image_matchers.rb} +4 -4
  123. data/spec/support/simple_matchers.rb +53 -0
  124. data/yard/handlers/configurable_attr_handler.rb +2 -2
  125. data/yard/templates/default/fulldoc/html/css/common.css +12 -10
  126. data/yard/templates/default/layout/html/layout.erb +6 -0
  127. metadata +267 -308
  128. data/Gemfile.rails.2.3.5 +0 -20
  129. data/features/3.0.3.feature +0 -8
  130. data/features/rails_2.3.5.feature +0 -7
  131. data/fixtures/files/app/models/album.rb +0 -3
  132. data/fixtures/files/app/views/albums/new.html.erb +0 -4
  133. data/fixtures/files/features/manage_album_images.feature +0 -12
  134. data/fixtures/rails_2.3.5/template.rb +0 -10
  135. data/lib/dragonfly/analysis/r_magick_analyser.rb +0 -63
  136. data/lib/dragonfly/config/r_magick.rb +0 -46
  137. data/lib/dragonfly/generation/hash_with_css_style_keys.rb +0 -23
  138. data/lib/dragonfly/generation/r_magick_generator.rb +0 -155
  139. data/lib/dragonfly/processing/r_magick_processor.rb +0 -126
  140. data/lib/dragonfly/r_magick_utils.rb +0 -48
  141. data/lib/dragonfly/simple_endpoint.rb +0 -76
  142. data/spec/dragonfly/active_model_extensions/active_model_setup.rb +0 -97
  143. data/spec/dragonfly/active_model_extensions/active_record_setup.rb +0 -85
  144. data/spec/dragonfly/analysis/image_magick_analyser_spec.rb +0 -15
  145. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +0 -31
  146. data/spec/dragonfly/config/r_magick_spec.rb +0 -29
  147. data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +0 -41
  148. data/spec/dragonfly/generation/image_magick_generator_spec.rb +0 -12
  149. data/spec/dragonfly/generation/r_magick_generator_spec.rb +0 -28
  150. data/spec/dragonfly/generation/shared_generator_spec.rb +0 -91
  151. data/spec/dragonfly/image_magick_utils_spec.rb +0 -16
  152. data/spec/dragonfly/processing/image_magick_processor_spec.rb +0 -29
  153. data/spec/dragonfly/processing/r_magick_processor_spec.rb +0 -30
  154. data/spec/dragonfly/simple_endpoint_spec.rb +0 -97
  155. 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).
@@ -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
- Three methods can be used to get content:
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.
@@ -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
- ImageMagickGenerator
19
- --------------------
20
- The {Dragonfly::Generation::ImageMagickGenerator ImageMagickGenerator} is registered by default by the
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 :blank_image do |colour|
69
- SomeLibrary.create_blank_image(colour) # return a String, File or Tempfile
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(:blank_image, 'red') # => 'Job' object which we can get data, etc.
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
@@ -12,10 +12,10 @@ Assuming you have an S3 account set up...
12
12
 
13
13
  Gem dependencies:
14
14
 
15
- - aws-s3 (require as aws/s3)
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
- environment.rb (application.rb in Rails 3):
31
+ application.rb if using with Rails:
32
32
 
33
- # make sure the last arg is the same as the app's configured prefix
34
- config.middleware.insert_before 'Rack::Lock', 'Dragonfly::Middleware', :images, '/media'
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
- Obviously replace 'XXXXXXXXX' with your access key and secret.
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: HEROKU'S CACHE IS CLEARED EVERY TIME YOU DEPLOY!!!
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 using something like a Memcached add-on, or maybe an after-deploy hook for hitting specific Dragonfly urls you want to cache, etc.
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.
@@ -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 'mime_type' method (if exists)
11
- 4. Analyse the content using the analyser's 'format' method (if exists)
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::Analysis::ImageMagickAnalyser ImageMagickAnalyser} has a `format` method.
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
 
@@ -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
  ------------