oahu-dragonfly 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. data/.rspec +1 -0
  2. data/.yardopts +24 -0
  3. data/Gemfile +30 -0
  4. data/History.md +323 -0
  5. data/LICENSE +20 -0
  6. data/README.md +88 -0
  7. data/Rakefile +50 -0
  8. data/VERSION +1 -0
  9. data/config.ru +14 -0
  10. data/docs.watchr +1 -0
  11. data/dragonfly.gemspec +297 -0
  12. data/extra_docs/Analysers.md +66 -0
  13. data/extra_docs/Caching.md +23 -0
  14. data/extra_docs/Configuration.md +124 -0
  15. data/extra_docs/Couch.md +49 -0
  16. data/extra_docs/DataStorage.md +153 -0
  17. data/extra_docs/Encoding.md +67 -0
  18. data/extra_docs/GeneralUsage.md +121 -0
  19. data/extra_docs/Generators.md +60 -0
  20. data/extra_docs/Heroku.md +50 -0
  21. data/extra_docs/ImageMagick.md +125 -0
  22. data/extra_docs/Index.md +33 -0
  23. data/extra_docs/MimeTypes.md +40 -0
  24. data/extra_docs/Models.md +272 -0
  25. data/extra_docs/Mongo.md +45 -0
  26. data/extra_docs/Processing.md +77 -0
  27. data/extra_docs/Rack.md +52 -0
  28. data/extra_docs/Rails2.md +57 -0
  29. data/extra_docs/Rails3.md +62 -0
  30. data/extra_docs/Sinatra.md +25 -0
  31. data/extra_docs/URLs.md +169 -0
  32. data/features/images.feature +47 -0
  33. data/features/no_processing.feature +14 -0
  34. data/features/rails_3.0.5.feature +8 -0
  35. data/features/steps/common_steps.rb +8 -0
  36. data/features/steps/dragonfly_steps.rb +66 -0
  37. data/features/steps/rails_steps.rb +28 -0
  38. data/features/support/env.rb +13 -0
  39. data/features/support/setup.rb +32 -0
  40. data/fixtures/rails_3.0.5/files/app/models/album.rb +7 -0
  41. data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
  42. data/fixtures/rails_3.0.5/files/app/views/albums/show.html.erb +6 -0
  43. data/fixtures/rails_3.0.5/files/config/initializers/dragonfly.rb +4 -0
  44. data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
  45. data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
  46. data/fixtures/rails_3.0.5/files/features/step_definitions/image_steps.rb +25 -0
  47. data/fixtures/rails_3.0.5/files/features/support/paths.rb +17 -0
  48. data/fixtures/rails_3.0.5/files/features/text_images.feature +7 -0
  49. data/fixtures/rails_3.0.5/template.rb +20 -0
  50. data/irbrc.rb +18 -0
  51. data/lib/dragonfly.rb +55 -0
  52. data/lib/dragonfly/active_model_extensions.rb +13 -0
  53. data/lib/dragonfly/active_model_extensions/attachment.rb +250 -0
  54. data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +148 -0
  55. data/lib/dragonfly/active_model_extensions/class_methods.rb +95 -0
  56. data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
  57. data/lib/dragonfly/active_model_extensions/validations.rb +41 -0
  58. data/lib/dragonfly/analyser.rb +58 -0
  59. data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
  60. data/lib/dragonfly/analysis/image_magick_analyser.rb +6 -0
  61. data/lib/dragonfly/app.rb +172 -0
  62. data/lib/dragonfly/config/heroku.rb +19 -0
  63. data/lib/dragonfly/config/image_magick.rb +6 -0
  64. data/lib/dragonfly/config/rails.rb +20 -0
  65. data/lib/dragonfly/configurable.rb +207 -0
  66. data/lib/dragonfly/core_ext/array.rb +7 -0
  67. data/lib/dragonfly/core_ext/hash.rb +7 -0
  68. data/lib/dragonfly/core_ext/object.rb +12 -0
  69. data/lib/dragonfly/core_ext/string.rb +9 -0
  70. data/lib/dragonfly/core_ext/symbol.rb +9 -0
  71. data/lib/dragonfly/data_storage.rb +9 -0
  72. data/lib/dragonfly/data_storage/couch_data_store.rb +64 -0
  73. data/lib/dragonfly/data_storage/file_data_store.rb +141 -0
  74. data/lib/dragonfly/data_storage/mongo_data_store.rb +86 -0
  75. data/lib/dragonfly/data_storage/s3data_store.rb +145 -0
  76. data/lib/dragonfly/encoder.rb +13 -0
  77. data/lib/dragonfly/encoding/image_magick_encoder.rb +6 -0
  78. data/lib/dragonfly/function_manager.rb +71 -0
  79. data/lib/dragonfly/generation/image_magick_generator.rb +6 -0
  80. data/lib/dragonfly/generator.rb +9 -0
  81. data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
  82. data/lib/dragonfly/image_magick/analyser.rb +51 -0
  83. data/lib/dragonfly/image_magick/config.rb +41 -0
  84. data/lib/dragonfly/image_magick/encoder.rb +57 -0
  85. data/lib/dragonfly/image_magick/generator.rb +145 -0
  86. data/lib/dragonfly/image_magick/processor.rb +99 -0
  87. data/lib/dragonfly/image_magick/utils.rb +72 -0
  88. data/lib/dragonfly/image_magick_utils.rb +4 -0
  89. data/lib/dragonfly/job.rb +451 -0
  90. data/lib/dragonfly/job_builder.rb +39 -0
  91. data/lib/dragonfly/job_definitions.rb +26 -0
  92. data/lib/dragonfly/job_endpoint.rb +15 -0
  93. data/lib/dragonfly/loggable.rb +28 -0
  94. data/lib/dragonfly/middleware.rb +20 -0
  95. data/lib/dragonfly/processing/image_magick_processor.rb +6 -0
  96. data/lib/dragonfly/processor.rb +9 -0
  97. data/lib/dragonfly/rails/images.rb +27 -0
  98. data/lib/dragonfly/response.rb +97 -0
  99. data/lib/dragonfly/routed_endpoint.rb +40 -0
  100. data/lib/dragonfly/serializer.rb +32 -0
  101. data/lib/dragonfly/server.rb +113 -0
  102. data/lib/dragonfly/simple_cache.rb +23 -0
  103. data/lib/dragonfly/temp_object.rb +175 -0
  104. data/lib/dragonfly/url_mapper.rb +78 -0
  105. data/samples/beach.png +0 -0
  106. data/samples/egg.png +0 -0
  107. data/samples/round.gif +0 -0
  108. data/samples/sample.docx +0 -0
  109. data/samples/taj.jpg +0 -0
  110. data/spec/dragonfly/active_model_extensions/model_spec.rb +1426 -0
  111. data/spec/dragonfly/active_model_extensions/spec_helper.rb +91 -0
  112. data/spec/dragonfly/analyser_spec.rb +123 -0
  113. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +48 -0
  114. data/spec/dragonfly/app_spec.rb +135 -0
  115. data/spec/dragonfly/configurable_spec.rb +461 -0
  116. data/spec/dragonfly/core_ext/array_spec.rb +19 -0
  117. data/spec/dragonfly/core_ext/hash_spec.rb +19 -0
  118. data/spec/dragonfly/core_ext/string_spec.rb +17 -0
  119. data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
  120. data/spec/dragonfly/data_storage/couch_data_store_spec.rb +76 -0
  121. data/spec/dragonfly/data_storage/file_data_store_spec.rb +296 -0
  122. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +57 -0
  123. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +258 -0
  124. data/spec/dragonfly/data_storage/shared_data_store_examples.rb +77 -0
  125. data/spec/dragonfly/function_manager_spec.rb +154 -0
  126. data/spec/dragonfly/hash_with_css_style_keys_spec.rb +24 -0
  127. data/spec/dragonfly/image_magick/analyser_spec.rb +64 -0
  128. data/spec/dragonfly/image_magick/encoder_spec.rb +41 -0
  129. data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
  130. data/spec/dragonfly/image_magick/processor_spec.rb +233 -0
  131. data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
  132. data/spec/dragonfly/job_builder_spec.rb +37 -0
  133. data/spec/dragonfly/job_definitions_spec.rb +35 -0
  134. data/spec/dragonfly/job_endpoint_spec.rb +173 -0
  135. data/spec/dragonfly/job_spec.rb +1046 -0
  136. data/spec/dragonfly/loggable_spec.rb +80 -0
  137. data/spec/dragonfly/middleware_spec.rb +47 -0
  138. data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
  139. data/spec/dragonfly/serializer_spec.rb +61 -0
  140. data/spec/dragonfly/server_spec.rb +278 -0
  141. data/spec/dragonfly/simple_cache_spec.rb +27 -0
  142. data/spec/dragonfly/temp_object_spec.rb +306 -0
  143. data/spec/dragonfly/url_mapper_spec.rb +126 -0
  144. data/spec/functional/deprecations_spec.rb +51 -0
  145. data/spec/functional/image_magick_app_spec.rb +27 -0
  146. data/spec/functional/model_urls_spec.rb +85 -0
  147. data/spec/functional/remote_on_the_fly_spec.rb +51 -0
  148. data/spec/functional/to_response_spec.rb +31 -0
  149. data/spec/spec_helper.rb +51 -0
  150. data/spec/support/argument_matchers.rb +19 -0
  151. data/spec/support/image_matchers.rb +47 -0
  152. data/spec/support/simple_matchers.rb +53 -0
  153. data/yard/handlers/configurable_attr_handler.rb +38 -0
  154. data/yard/setup.rb +15 -0
  155. data/yard/templates/default/fulldoc/html/css/common.css +107 -0
  156. data/yard/templates/default/layout/html/layout.erb +89 -0
  157. data/yard/templates/default/module/html/configuration_summary.erb +31 -0
  158. data/yard/templates/default/module/setup.rb +17 -0
  159. metadata +544 -0
@@ -0,0 +1,60 @@
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
+ ImageMagick Generator
19
+ ---------------------
20
+ See {file:Imagemagick}.
21
+
22
+ Custom Generators
23
+ -----------------
24
+ To register a single custom generator:
25
+
26
+ app.generator.add :blank_image do |colour|
27
+ SomeLibrary.create_blank_image(colour) # return a String, Pathname, File or Tempfile
28
+ end
29
+
30
+ app.generate(:blank_image, 'red') # => 'Job' object which we can get data, etc.
31
+
32
+
33
+ Or create a class like the ImageMagick one above, in which case all public methods will be counted as generator methods.
34
+
35
+ class RoundedCornerGenerator
36
+
37
+ def top_left_corner(opts={})
38
+ SomeLib.tlc(opts)
39
+ end
40
+
41
+ def bottom_right_corner(opts={})
42
+ tempfile = Tempfile.new('brc')
43
+ `some_command -c #{opts[:colour]} -o #{tempfile.path}`
44
+ tempfile
45
+ end
46
+
47
+ # ...
48
+
49
+ private
50
+
51
+ def my_helper_method
52
+ # do stuff
53
+ end
54
+
55
+ end
56
+
57
+ app.generator.register(RoundedCornerGenerator)
58
+
59
+ app.generate(:top_left_corner, :colour => 'green')
60
+ app.generate(:bottom_right_corner, :colour => 'mauve')
@@ -0,0 +1,50 @@
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
+ - aws-s3 (require as aws/s3)
16
+ - dragonfly
17
+
18
+ Initializer (e.g. config/initializers/dragonfly.rb):
19
+
20
+ require 'dragonfly'
21
+ app = Dragonfly[:images]
22
+
23
+ app.configure_with(:imagemagick)
24
+ app.configure_with(:rails)
25
+ app.configure_with(:heroku, 'my_bucket_name') if Rails.env.production?
26
+
27
+ app.define_macro(ActiveRecord::Base, :image_accessor)
28
+
29
+ The datastore remains as the {Dragonfly::DataStorage::FileDataStore FileDataStore} for non-production environments.
30
+
31
+ environment.rb (application.rb in Rails 3):
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'
35
+
36
+ We don't store the S3 access key and secret in the repository, rather we use Heroku's
37
+ {http://docs.heroku.com/config-vars config variables} using the command line (we only have to do this once).
38
+
39
+ From your app's directory:
40
+
41
+ heroku config:add S3_KEY=XXXXXXXXX S3_SECRET=XXXXXXXXXX
42
+
43
+ Obviously replace 'XXXXXXXXX' with your access key and secret.
44
+
45
+ Now you can benefit from super-fast images served straight from Heroku's cache!
46
+
47
+ NOTE: HEROKU'S CACHE IS CLEARED EVERY TIME YOU DEPLOY!!!
48
+
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.
50
+ It won't be a problem for most sites though.
@@ -0,0 +1,125 @@
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.convert('-scale 30x30') # same as image.process(:convert, '-scale 30x30')
24
+
25
+ `thumb` and `convert` can optionally take a format (e.g. :gif) as the second argument.
26
+ Bang methods like `image.thumb!('40x30')`, `image.png!` etc. will operate on `self`.
27
+
28
+ Below are some examples of geometry strings for `thumb`:
29
+
30
+ '400x300' # resize, maintain aspect ratio
31
+ '400x300!' # force resize, don't maintain aspect ratio
32
+ '400x' # resize width, maintain aspect ratio
33
+ 'x300' # resize height, maintain aspect ratio
34
+ '400x300>' # resize only if the image is larger than this
35
+ '400x300<' # resize only if the image is smaller than this
36
+ '50x50%' # resize width and height to 50%
37
+ '400x300^' # resize width, height to minimum 400,300, maintain aspect ratio
38
+ '2000@' # resize so max area in pixels is 2000
39
+ '400x300#' # resize, crop if necessary to maintain aspect ratio (centre gravity)
40
+ '400x300#ne' # as above, north-east gravity
41
+ '400x300se' # crop, with south-east gravity
42
+ '400x300+50+100' # crop from the point 50,100 with width, height 400,300
43
+
44
+ Processor
45
+ ---------
46
+
47
+ image.process(:crop, :width => 40, :height => 50, :x => 20, :y => 30)
48
+ image.process(:crop, :width => 40, :height => 50, :gravity => 'ne')
49
+
50
+ image.process(:flip) # flips it vertically
51
+ image.process(:flop) # flips it horizontally
52
+
53
+ image.process(:greyscale, :depth => 128) # default depth 256
54
+
55
+ image.process(:resize, '40x40')
56
+ image.process(:resize_and_crop, :width => 40, :height=> 50, :gravity => 'ne')
57
+
58
+ image.process(:rotate, 45, :background_colour => 'transparent') # default bg black
59
+
60
+ The method `thumb` takes a geometry string and calls `resize`, `resize_and_crop` or `crop` accordingly.
61
+
62
+ image.process(:thumb, '400x300') # calls resize
63
+
64
+ Encoder
65
+ -------
66
+ The {Dragonfly::ImageMagick::Encoder ImageMagick Encoder} gives us:
67
+
68
+ image.encode(:jpg)
69
+ image.encode(:gif)
70
+ image.encode(:png)
71
+ image.encode(:tiff)
72
+
73
+ and various other formats (see {Dragonfly::ImageMagick::Encoder ImageMagick Encoder}).
74
+
75
+ You can also pass additional options to the imagemagick command line:
76
+
77
+ image.encode(:jpg, '-quality 10')
78
+
79
+ Analyser
80
+ --------
81
+ The {Dragonfly::ImageMagick::Analyser ImageMagick Analyser} gives us these methods:
82
+
83
+ image.width # => 280
84
+ image.height # => 355
85
+ image.aspect_ratio # => 0.788732394366197
86
+ image.portrait? # => true
87
+ image.landscape? # => false
88
+ image.depth # => 8
89
+ image.number_of_colours # => 34703
90
+ image.format # => :png
91
+ image.image? # => true - will return true or false for any content
92
+
93
+ Generator
94
+ ---------
95
+ The {Dragonfly::ImageMagick::Generator ImageMagick Generator} gives us these methods:
96
+
97
+ image = app.generate(:plain, 600, 400, 'rgba(40,200,30,0.5)')
98
+ image = app.generate(:plain, 600, 400, '#ccc', :format => :gif)
99
+ # generate a 600x400 plain image
100
+ # any css-style colour should work
101
+
102
+ image = app.generate(:plasma, 600, 400, :gif) # generate a 600x400 plasma image
103
+ # last arg defaults to :png
104
+
105
+ image = app.generate(:text, "Hello there") # an image of the text "Hello there"
106
+
107
+ image = app.generate(:text, "Hello there",
108
+ :font_size => 30, # defaults to 12
109
+ :font_family => 'Monaco',
110
+ :stroke_color => '#ddd',
111
+ :color => 'red',
112
+ :font_style => 'italic',
113
+ :font_stretch => 'expanded',
114
+ :font_weight => 'bold',
115
+ :padding => '30 20 10',
116
+ :background_color => '#efefef', # defaults to transparent
117
+ :format => :gif # defaults to png
118
+ )
119
+
120
+ 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`.
121
+
122
+ You can use `padding-top`, `padding-left`, etc., as well as the standard css shortcuts for `padding` (it assumes unit is px).
123
+
124
+ An alternative for `:font_family` is `:font` (see {http://www.imagemagick.org/script/command-line-options.php#font}), which could be a complete filename.
125
+ Available fonts are those available on your system.
@@ -0,0 +1,33 @@
1
+ Dragonfly is a {http://rack.rubyforge.org Rack} framework for on-the-fly image handling in Ruby.
2
+
3
+ It is suitable for using with web frameworks such as Rails(2.3 and 3), Sinatra, etc.
4
+
5
+ I actually lied about image handling - it can be used for any type of content.
6
+
7
+ See the links on the right for more info.
8
+
9
+ Installation
10
+ ------------
11
+
12
+ gem install dragonfly
13
+
14
+ Add-ons
15
+ -------
16
+ For third-party add-ons, see [the Add-ons wiki](http://github.com/markevans/dragonfly/wiki/Dragonfly-add-ons)
17
+
18
+ Issues
19
+ ------
20
+ Please use the <a href="http://github.com/markevans/dragonfly/issues">github issue tracker</a>.
21
+
22
+ Suggestions/Questions
23
+ ---------------------
24
+ {http://groups.google.com/group/dragonfly-users}
25
+
26
+ Credits
27
+ -------
28
+ - [Mark Evans](http://github.com/markevans) (author)
29
+ - Loads of helpful comments, issues, questions, suggestions and insults from others - you know who you are!
30
+
31
+ Copyright
32
+ ---------
33
+ Copyright (c) 2009-2010 Mark Evans. See LICENSE for details.
@@ -0,0 +1,40 @@
1
+ Mime Types
2
+ ==========
3
+
4
+ Responses from the Dragonfly app have the HTTP 'Content-Type' header set.
5
+
6
+ This is decided by the first found from:
7
+
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.
16
+
17
+ Registered mime-types
18
+ ---------------------
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}).
20
+
21
+ To register a mime-type for the format 'egg':
22
+
23
+ Dragonfly[:my_app].register_mime_type(:egg, 'fried/egg')
24
+
25
+ You can also do this inside a configuration block.
26
+
27
+ Analysers
28
+ ---------
29
+ The {Dragonfly::Analysis::FileCommandAnalyser FileCommandAnalyser} has a `mime_type` method and the
30
+ {Dragonfly::ImageMagick::Analyser ImageMagick Analyser} has a `format` method.
31
+
32
+ These are both registered by default when you use the preconfigured 'dragonfly/rails/images' file.
33
+
34
+ Fallback mime-type
35
+ ------------------
36
+ By default this is 'application/octet-stream', but it can be changed using
37
+
38
+ Dragonfly[:my_app].fallback_mime_type = 'meaty/beef'
39
+
40
+ This can also be done inside a configuration block.
@@ -0,0 +1,272 @@
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
+ CouchRest::Model
28
+ -------
29
+ app.define_macro(CouchRest::Model::Base, :image_accessor)
30
+
31
+ defines the macro `image_accessor` on any models that include `CouchRest::Model::Base`
32
+
33
+ Adding accessors
34
+ ----------------
35
+ Now we have the method `image_accessor` available in our model classes, which we can use as many times as we like
36
+
37
+ class Album
38
+ image_accessor :cover_image
39
+ image_accessor :band_photo # Note: this is a different image altogether, not a thumbnail of cover_image
40
+ end
41
+
42
+ Each accessor (e.g. `cover_image`) depends on a string field to actually hold the datastore uid,
43
+ named by appending the suffix `_uid` (e.g. `cover_image_uid`).
44
+
45
+ For example, ActiveRecord models need a migration such as:
46
+
47
+ class MyMigration < ActiveRecord::Migration
48
+
49
+ def self.up
50
+ add_column :albums, :cover_image_uid, :string
51
+ add_column :albums, :band_photo_uid, :string
52
+ end
53
+
54
+ def self.down
55
+ remove_column :albums, :cover_image_uid
56
+ remove_column :albums, :band_photo_uid
57
+ end
58
+
59
+ end
60
+
61
+ Using the accessors
62
+ -------------------
63
+
64
+ We can use the attribute much like other other model attributes:
65
+
66
+ @album = Album.new
67
+
68
+ @album.cover_image = "\377???JFIF\000\..." # can assign as a string...
69
+ @album.cover_image = File.new('path/to/my_image.png') # ... or as a file...
70
+ @album.cover_image = some_tempfile # ... or as a tempfile...
71
+ @album.cover_image = @album.band_photo # ... or as another Dragonfly attachment
72
+
73
+ @album.cover_image # => #<Dragonfly::ActiveModelExtensions::Attachment:0x103ef6128...
74
+
75
+ @album.cover_image = nil
76
+ @album.cover_image # => nil
77
+
78
+ We can inspect properties of the attribute
79
+
80
+ @album.cover_image.width # => 280
81
+ @album.cover_image.height # => 140
82
+ @album.cover_image.number_of_colours # => 34703
83
+ @album.cover_image.mime_type # => 'image/png'
84
+
85
+ The properties available (i.e. 'width', etc.) come from the app's registered analysers - see {file:Analysers.md Analysers}.
86
+
87
+ We can play around with the data
88
+
89
+ @album.cover_image.data # => "\377???JFIF\000\..."
90
+ @album.cover_image.to_file('out.png') # writes to file 'out.png' and returns a readable file object
91
+ @album.cover_image.tempfile # => #<File:/var/folders/st/strHv74sH044JPabSiODz... a closed Tempfile object
92
+ @album.cover_image.file # => #<File:/var/folders/st/strHv74sH044JPabSiODz... a readable (open) File object
93
+ @album.cover_image.file do |f| # Yields an open file object, returns the return value of
94
+ data = f.read(256) # the block, and closes the file object
95
+ end
96
+ @album.cover_image.path # => '/var/folders/st/strHv74sH044JPabSiODz...' i.e. the path of the tempfile
97
+ @album.cover_image.size # => 134507 (size in bytes)
98
+
99
+ We can process the data
100
+
101
+ image = @album.cover_image.process(:thumb, '20x20') # returns a 'Job' object, with similar properties
102
+ image.width # => 20
103
+ @album.cover_image.width # => 280 (no change)
104
+
105
+ The available processing methods available (i.e. 'thumb', etc.) come from the {Dragonfly} app's registered processors - see {file:Processing.md Processing}
106
+
107
+ We can encode the data
108
+
109
+ image = @album.cover_image.encode(:gif) # returns a 'Job' object, with similar properties
110
+ image.format # => :gif
111
+ @album.cover_image.format # => :png (no change)
112
+
113
+ The encoding is implemented by the {Dragonfly} app's registered encoders (which will usually just be one) - see {file:Encoding.md Encoding}
114
+
115
+ We can use configured shortcuts for processing/encoding, and chain them:
116
+
117
+ @album.cover_image.thumb('300x200#ne') # => returns a 'Job' object, with similar properties
118
+
119
+ We can chain all these things much like ActiveRecord scopes:
120
+
121
+ @album.cover_image.png.thumb('300x200#ne').process(:greyscale).encode(:tiff)
122
+
123
+ 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.
124
+ You can force the processing to be done if you must by then calling `apply`.
125
+
126
+ @album.cover_image.process(:greyscale).apply
127
+
128
+ Persisting
129
+ ----------
130
+ 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})
131
+ The uid column is then filled in.
132
+
133
+ @album = Album.new
134
+
135
+ @album.cover_image_uid # => nil
136
+
137
+ @album.cover_image = File.new('path/to/my_image.png')
138
+ @album.cover_image_uid # => nil
139
+
140
+ @album.save
141
+ @album.cover_image_uid # => '2009/12/05/file.png' (some unique uid, used by the datastore)
142
+
143
+ URLs
144
+ ----
145
+ 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:
146
+
147
+ @album.cover_image.url # => '/media/BAhbBlsHOgZmIhgy...'
148
+ @album.cover_image.thumb('300x200#nw').url # => '/media/BAhbB1sYusgZhgyM...'
149
+ @album.cover_image.process(:greyscale).jpg.url # => '/media/BnA6CnRodW1iIg8z...'
150
+
151
+ Because the processing/encoding methods (including shortcuts like `thumb` and `jpg`) are lazy, no processing or encoding is actually done.
152
+
153
+ Validations
154
+ -----------
155
+ `validates_presence_of` and `validates_size_of` work out of the box, and Dragonfly also provides `validates_property`.
156
+
157
+ class Album
158
+
159
+ validates_presence_of :cover_image
160
+ validates_size_of :cover_image, :maximum => 500.kilobytes
161
+
162
+ validates_property :format, :of => :cover_image, :in => [:jpeg, :png, :gif]
163
+ # ..or..
164
+ validates_property :mime_type, :of => :cover_image, :in => %w(image/jpeg image/png image/gif)
165
+
166
+ validates_property :width, :of => :cover_image, :in => (0..400), :message => "é demais cara!"
167
+
168
+ # ...
169
+ end
170
+
171
+ The property argument of `validates_property` will generally be one of the registered analyser properties as described in {file:Analysers.md Analysers}.
172
+ However it would actually work for arbitrary properties, including those of non-dragonfly model attributes.
173
+
174
+ Name and extension
175
+ ------------------
176
+ 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.
177
+
178
+ @album.cover_image = File.new('path/to/my_image.png')
179
+
180
+ @album.cover_image.name # => 'my_image.png'
181
+ @album.cover_image.ext # => 'png'
182
+
183
+ Meta data
184
+ ---------
185
+ You can store metadata along with the content data of your attachment:
186
+
187
+ @album.cover_image = File.new('path/to/my_image.png')
188
+ @album.cover_image.meta = {:taken => Date.yesterday}
189
+ @album.save!
190
+
191
+ @album.cover_image.meta # => {:model_class=>"Album",
192
+ # :model_attachment=>:cover_image,
193
+ # :taken=>Sat, 11 Sep 2010}
194
+
195
+ As you can see, a couple of things are added by the model. You can also access this directly on the {Dragonfly::Job Job} object.
196
+
197
+ app.fetch(@album.cover_image_uid).meta # => {:model_class=>"Album", ...}
198
+
199
+ "Magic" Attributes
200
+ ------------------
201
+ An accessor like `cover_image` only relies on the accessor `cover_image_uid` to work.
202
+ However, in some cases you may want to record some other properties, whether it be for using in queries, or
203
+ for caching an attribute for performance reasons, etc.
204
+
205
+ For the properties `name`, `ext`, `size` and any of the registered analysis methods (e.g. `width`, etc. in the examples above),
206
+ this is done automatically for you, if the corresponding accessor exists.
207
+
208
+ For example - with ActiveRecord, given the migration:
209
+
210
+ add_column :albums, :cover_image_width, :integer
211
+
212
+ This will automatically be set when assigned:
213
+
214
+ @album.cover_image = File.new('path/to/my_image.png')
215
+
216
+ @album.cover_image_width # => 280
217
+
218
+ They can be used to avoid retrieving data from the datastore for analysis
219
+
220
+ @album = Album.first
221
+
222
+ @album.cover_image.width # => 280 - no need to retrieve data - takes it from `cover_image_width`
223
+ @album.cover_image.size # => 134507 - but this needs to retrieve data from the data store, then analyse
224
+
225
+
226
+ Custom Model
227
+ ------------
228
+ The accessors only require that your model class implements `before_save`, `before_destroy` and `validates_each`
229
+ (if using validations), as well as of course the `..._uid` field for storing the datastore uid.
230
+
231
+ Here is an example of a minimal ActiveModel `Album` model:
232
+
233
+ class CustomModel::Base
234
+
235
+ extend ActiveModel::Callbacks
236
+ define_model_callbacks :save, :destroy
237
+
238
+ include ActiveModel::Validations # if needed
239
+
240
+ def save
241
+ _run_save_callbacks {
242
+ # do some saving!
243
+ }
244
+ end
245
+
246
+ def destroy
247
+ _run_destroy_callbacks {
248
+ # do some destroying!
249
+ }
250
+ end
251
+
252
+ end
253
+
254
+ Define our `image_accessor` macro...
255
+
256
+ app.define_macro(CustomModel::Base, :image_accessor)
257
+
258
+ ...which is used by `Album`:
259
+
260
+ class Album < CustomModel::Base
261
+
262
+ def cover_image_uid=
263
+ # ...
264
+ end
265
+
266
+ def cover_image_uid
267
+ # ...
268
+ end
269
+
270
+ image_accessor :cover_image
271
+
272
+ end