dynamic_image 1.0.4 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/{LICENSE → MIT-LICENSE} +1 -1
  3. data/README.md +57 -80
  4. data/Rakefile +7 -30
  5. data/lib/dynamic_image/belongs_to.rb +22 -0
  6. data/lib/dynamic_image/controller.rb +94 -0
  7. data/lib/dynamic_image/digest_verifier.rb +62 -0
  8. data/lib/dynamic_image/errors.rb +10 -0
  9. data/lib/dynamic_image/helper.rb +139 -85
  10. data/lib/dynamic_image/image_sizing.rb +156 -0
  11. data/lib/dynamic_image/metadata.rb +84 -0
  12. data/lib/dynamic_image/model/dimensions.rb +95 -0
  13. data/lib/dynamic_image/model/validations.rb +94 -0
  14. data/lib/dynamic_image/model.rb +130 -0
  15. data/lib/dynamic_image/processed_image.rb +119 -0
  16. data/lib/dynamic_image/railtie.rb +18 -0
  17. data/lib/dynamic_image/routing.rb +24 -0
  18. data/lib/dynamic_image/version.rb +5 -0
  19. data/lib/dynamic_image.rb +14 -71
  20. data/lib/rails/generators/dynamic_image/resource/resource_generator.rb +74 -0
  21. metadata +130 -97
  22. data/VERSION +0 -1
  23. data/app/controllers/images_controller.rb +0 -79
  24. data/app/models/image.rb +0 -188
  25. data/config/routes.rb +0 -16
  26. data/dynamic_image.gemspec +0 -62
  27. data/dynamic_image.sublime-project +0 -9
  28. data/dynamic_image.sublime-workspace +0 -1599
  29. data/init.rb +0 -1
  30. data/install.rb +0 -1
  31. data/lib/binary_storage/active_record_extensions.rb +0 -144
  32. data/lib/binary_storage/blob.rb +0 -104
  33. data/lib/binary_storage.rb +0 -28
  34. data/lib/dynamic_image/active_record_extensions.rb +0 -60
  35. data/lib/dynamic_image/engine.rb +0 -6
  36. data/lib/dynamic_image/filterset.rb +0 -79
  37. data/lib/generators/dynamic_image/USAGE +0 -5
  38. data/lib/generators/dynamic_image/dynamic_image_generator.rb +0 -38
  39. data/lib/generators/dynamic_image/templates/migrations/create_images.rb +0 -21
  40. data/uninstall.rb +0 -1
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fa4635730b705cb0d3d48024139359bc7bbbfef6
4
+ data.tar.gz: ededec9d42f262502983c22c8c753b8de8498a9b
5
+ SHA512:
6
+ metadata.gz: 3558ebbfb2e0f5e2744bbdc028da4e73515134fa88100749bf072af97f61707dfb56a969d31a482d409e24818548dab1cd09207b46a4925552b5d051ce714cb5
7
+ data.tar.gz: be91b398098093248afd99558fe3101eedd1917193efba2632d36e3617d5929f5c3d1709745e2ddd8427b612b4a7024ad4c148e1e4adcf95044c0feb19564995
@@ -1,4 +1,4 @@
1
- Copyright (c) 2006-2010 Inge Jørgensen
1
+ Copyright 2006-2014 Inge Jørgensen
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,109 +1,86 @@
1
- # DynamicImage
2
-
3
- DynamicImage is a Rails plugin that simplifies image uploading and processing.
4
- No configuration is necessary, as resizing and processing is done on demand and
5
- cached rather than on upload.
6
-
7
- Note: This version is currently Rails 3 specific, although the final 1.0
8
- version will also be compatible with 2.x.
1
+ # DynamicImage [![Build Status](https://travis-ci.org/elektronaut/dynamic_image.png)](https://travis-ci.org/elektronaut/dynamic_image) [![Code Climate](https://codeclimate.com/github/elektronaut/dynamic_image.png)](https://codeclimate.com/github/elektronaut/dynamic_image) [![Code Climate](https://codeclimate.com/github/elektronaut/dynamic_image/coverage.png)](https://codeclimate.com/github/elektronaut/dynamic_image)
9
2
 
3
+ Requires Rails 4.1+ and Ruby 1.9.3+.
10
4
 
11
5
  ## Installation
12
6
 
13
- Install the gem:
14
-
15
- gem install dynamic_image
16
-
17
- Add the gem to your Gemfile:
18
-
19
- gem 'dynamic_image'
20
-
21
- Do the migrations:
22
-
23
- rails generate dynamic_image migrations
24
- rake db:migrate
25
-
26
-
27
- ## Getting started
7
+ Add the gem to your Gemfile and run `bundle install`.
28
8
 
29
- Let's create a model with an image:
9
+ ```ruby
10
+ gem "dynamic_image"
11
+ ```
30
12
 
31
- class User
32
- belongs_to_image :mugshot
33
- end
13
+ Run the `dis:install` generator to set up your storage.
34
14
 
35
- Uploading files is pretty straightforward, just add a <tt>file_field</tt>
36
- to your form and update your record as usual:
15
+ ```sh
16
+ bin/rails generate dis:install
17
+ ```
37
18
 
38
- <%= form_for @user, :html => {:multipart => true} do |f| %>
39
- Name: <%= f.text_field :name %>
40
- Mugshot: <%= f.file_field :mugshot %>
41
- <%= submit_tag "Save" %>
42
- <% end %>
19
+ You can edit the generated initializer to configure your storage, by default it
20
+ will store files in `db/dis`. See the
21
+ [Dis](https://github.com/elektronaut/dis) documentation for more
22
+ information.
43
23
 
44
- You can now use the <tt>dynamic_image_tag</tt> helper to show off your
45
- new image:
24
+ ## Creating your resource
46
25
 
47
- <% if @user.mugshot? %>
48
- <%= dynamic_image_tag @user.profile_picture, :size => '64x64' %>
49
- <% end %>
26
+ Run the `dynamic_image:resource` generator to create your resource.
50
27
 
28
+ ```sh
29
+ bin/rails generate dynamic_image:resource image
30
+ ```
51
31
 
52
- ## Filters
32
+ This will create an `Image` model and a controller, along with a migration and
33
+ the necessary routes.
53
34
 
54
- I'm cleaning up the filters syntax, watch this space.
35
+ Note that in this case, the route with collide with any static images stored
36
+ in `public/images`. You can customize the path if you want in the route
37
+ declaration.
55
38
 
39
+ ```ruby
40
+ image_resources :images, path: "dynamic_images/:digest(/:size)"
41
+ ```
56
42
 
57
- ## Technical
43
+ ## Rendering images in your views
58
44
 
59
- The original master files are stored in the file system and identified a
60
- SHA-1 hash of the contents. If you're familiar with the internal workings
61
- of git, this should seem familiar.
45
+ You should use the provided helpers for displaying images, this will ensure
46
+ that the generated URLs are properly signed and timestamped.
62
47
 
63
- Processing images on the fly is expensive. Therefore, page caching is enabled
64
- by default, even in development mode. To disable page caching, add the following
65
- line in your initializers:
48
+ To display the image at it's original size, use `dynamic_image_tag` without
49
+ any options.
66
50
 
67
- DynamicImage.page_caching = false
51
+ ```html
52
+ <%= dynamic_image_tag image %>
53
+ ```
68
54
 
55
+ To resize it, specify a max size. This will scale the image down to fit, but
56
+ no cropping will occur.
69
57
 
70
- ## History
58
+ ```html
59
+ <%= dynamic_image_tag image, size: '400x400' %>
60
+ ```
71
61
 
72
- DynamicImage was originally created in early 2006 to handle images
73
- for the Pages CMS. It was later extracted as a Rails Engine for Rails
74
- 1.2 in 2007, which also marked the first public release as
75
- dynamic_image_engine.
62
+ Setting `crop: true` will crop the image to the exact size.
76
63
 
77
- The API has remained more or less unchanged, but the internal workings
78
- have been refactored a few times over the years, most notably dropping
79
- the Engines dependency and transitioning from database storage to file
80
- system.
64
+ ```html
65
+ <%= dynamic_image_tag image, size: '400x400', crop: true %>
66
+ ```
81
67
 
82
- The current version is based on an internal branch targeting Rails
83
- 2.3, and modified to work as a Rails 3 plugin. It's not directly
84
- compatible with earlier versions, but upgrading shouldn't be more
85
- trouble than migrating the files out of the database.
68
+ Omitting either dimension will render the image at an exact width or height.
86
69
 
70
+ ```html
71
+ <%= dynamic_image_tag image, size: '400x' %>
72
+ ```
87
73
 
88
- ## Copyright
74
+ `dynamic_image_path` and `dynamic_image_url` act pretty much like regular URL
75
+ helpers.
89
76
 
90
- Copyright © 2006 Inge Jørgensen.
77
+ ```html
78
+ <%= link_to "See image", dynamic_image_path(image) %>
79
+ ```
91
80
 
92
- Permission is hereby granted, free of charge, to any person obtaining
93
- a copy of this software and associated documentation files (the
94
- "Software"), to deal in the Software without restriction, including
95
- without limitation the rights to use, copy, modify, merge, publish,
96
- distribute, sublicense, and/or sell copies of the Software, and to
97
- permit persons to whom the Software is furnished to do so, subject to
98
- the following conditions:
81
+ ## License
99
82
 
100
- The above copyright notice and this permission notice shall be
101
- included in all copies or substantial portions of the Software.
83
+ Copyright 2006-2014 Inge Jørgensen
102
84
 
103
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
104
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
105
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
106
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
107
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
108
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
109
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
85
+ DynamicImage is released under the
86
+ [MIT License](http://www.opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,33 +1,10 @@
1
- require 'rake'
2
- require 'rake/testtask'
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
3
 
4
- require "rake"
4
+ APP_RAKEFILE = "spec/internal/Rakefile"
5
+ load 'rails/tasks/engine.rake'
5
6
 
6
- begin
7
- require "jeweler"
8
- Jeweler::Tasks.new do |gem|
9
- gem.name = "dynamic_image"
10
- gem.summary = "DynamicImage is a rails plugin providing transparent uploading and processing of image files."
11
- gem.email = "inge@elektronaut.no"
12
- gem.homepage = "http://github.com/elektronaut/dynamic_image"
13
- gem.authors = ["Inge Jørgensen"]
14
- gem.files = Dir["*", "{lib}/**/*", "{app}/**/*", "{config}/**/*"]
15
- gem.add_dependency("rmagick", "~> 2.13.2")
16
- gem.add_dependency("vector2d", "~> 1.0.0")
17
- end
18
- Jeweler::GemcutterTasks.new
19
- rescue LoadError
20
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
21
- end
22
-
23
- desc 'Default: run unit tests.'
24
- task :default => :test
25
-
26
- desc 'Test the dynamic_image plugin.'
27
- Rake::TestTask.new(:test) do |t|
28
- t.libs << 'lib'
29
- t.libs << 'test'
30
- t.pattern = 'test/**/*_test.rb'
31
- t.verbose = true
32
- end
7
+ RSpec::Core::RakeTask.new
33
8
 
9
+ task :default => :spec
10
+ task :test => :spec
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ module DynamicImage
4
+ # = DynamicImage Belongs To
5
+ #
6
+ module BelongsTo
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ def belongs_to_image(name, scope=nil, options={})
11
+ belongs_to(name, scope, options)
12
+
13
+ define_method "#{name}=" do |new_image|
14
+ if new_image.present? && !new_image.kind_of?(DynamicImage::Model)
15
+ new_image = self.send("build_#{name}", file: new_image)
16
+ end
17
+ super(new_image)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+
3
+ module DynamicImage
4
+ # = DynamicImage Controller
5
+ #
6
+ # Generating images is rather expensive, so all requests must be
7
+ # signed with a HMAC digest in order to avoid denial of service attacks.
8
+ # The methods in +DynamicImage::Helper+ handles this transparently.
9
+ # As a bonus, this also prevents unauthorized URL enumeration.
10
+ module Controller
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ before_action :verify_signed_params
15
+ before_action :find_record
16
+ after_action :cache_expiration_header
17
+ respond_to :gif, :jpeg, :png, :tiff
18
+ end
19
+
20
+ # Renders the image.
21
+ def show
22
+ render_image(format: requested_format)
23
+ end
24
+
25
+ # Same as +show+, but renders the image without any pre-cropping applied.
26
+ def uncropped
27
+ render_image(format: requested_format, uncropped: true)
28
+ end
29
+
30
+ # Renders the original image data, without any processing.
31
+ def original
32
+ if stale?(@record)
33
+ respond_with(@record) do |format|
34
+ format.any(:gif, :jpeg, :png, :tiff) do
35
+ send_data(
36
+ @record.data,
37
+ content_type: @record.content_type,
38
+ disposition: 'inline'
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def cache_expiration_header
48
+ expires_in 30.days, public: true
49
+ end
50
+
51
+ def find_record
52
+ @record = model.find(params[:id])
53
+ end
54
+
55
+ def render_image(options)
56
+ processed_image = DynamicImage::ProcessedImage.new(@record, options)
57
+ if stale?(@record)
58
+ respond_with(@record) do |format|
59
+ format.any(:gif, :jpeg, :png, :tiff) do
60
+ send_data(
61
+ processed_image.cropped_and_resized(requested_size),
62
+ content_type: processed_image.content_type,
63
+ disposition: 'inline'
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ def requested_format
71
+ params[:format]
72
+ end
73
+
74
+ def requested_size
75
+ Vector2d.parse(params[:size])
76
+ end
77
+
78
+ def signed_params
79
+ case request[:action]
80
+ when "show", "uncropped"
81
+ [:action, :id, :size]
82
+ else
83
+ [:action, :id]
84
+ end
85
+ end
86
+
87
+ def verify_signed_params
88
+ key = signed_params.map { |k|
89
+ k == :id ? params.require(k).to_i : params.require(k)
90
+ }.join('-')
91
+ DynamicImage.digest_verifier.verify(key, params[:digest])
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ module DynamicImage
4
+ # = DynamicImage Digest Verifier
5
+ #
6
+ # ==== Usage
7
+ #
8
+ # verifier = DynamicImage::DigestVerifier.new("super secret!")
9
+ #
10
+ # digest = verifier.generate("foo")
11
+ #
12
+ # digest.verify("foo", digest)
13
+ # # => true
14
+ # digest.verify("bar", digest)
15
+ # # => raises DynamicImage::Errors::InvalidSignature
16
+ #
17
+ # Credit where credit is due: adapted and simplified from
18
+ # +ActiveSupport::MessageVerifier+, since we don't need to handle
19
+ # arbitrary data structures and ship the serialized data to the client.
20
+ class DigestVerifier
21
+ def initialize(secret, options = {})
22
+ @secret = secret
23
+ @digest = options[:digest] || 'SHA1'
24
+ end
25
+
26
+ # Generates a digest for a string.
27
+ def generate(data)
28
+ generate_digest(data)
29
+ end
30
+
31
+ # Verifies that <tt>digest</tt> is valid for <tt>data</tt>.
32
+ # Raises a +DynamicImage::Errors::InvalidSignature+ error if not.
33
+ def verify(data, digest)
34
+ if valid_digest?(data, digest)
35
+ true
36
+ else
37
+ raise DynamicImage::Errors::InvalidSignature
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def secure_compare(a, b)
44
+ return false unless a.bytesize == b.bytesize
45
+
46
+ l = a.unpack "C#{a.bytesize}"
47
+
48
+ res = 0
49
+ b.each_byte { |byte| res |= byte ^ l.shift }
50
+ res == 0
51
+ end
52
+
53
+ def generate_digest(data)
54
+ require 'openssl' unless defined?(OpenSSL)
55
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data)
56
+ end
57
+
58
+ def valid_digest?(data, digest)
59
+ data.present? && digest.present? && secure_compare(digest, generate_digest(data))
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ module DynamicImage
4
+ module Errors
5
+ class Error < StandardError; end
6
+ class InvalidImage < DynamicImage::Errors::Error; end
7
+ class InvalidSignature < DynamicImage::Errors::Error; end
8
+ class InvalidSizeOptions < DynamicImage::Errors::Error; end
9
+ end
10
+ end
@@ -1,107 +1,161 @@
1
- require 'dynamic_image'
1
+ # encoding: utf-8
2
2
 
3
3
  module DynamicImage
4
+ # = DynamicImage Helper
5
+ #
6
+ # Provides helper methods for rendering and linking to images.
4
7
  module Helper
8
+ # Returns the path for a DynamicImage::Model record.
9
+ # Takes the same options as +dynamic_image_url+
10
+ def dynamic_image_path(record_or_array, options={})
11
+ dynamic_image_url(record_or_array, { routing_type: :path }.merge(options))
12
+ end
5
13
 
6
- # Returns an hash consisting of the URL to the dynamic image and parsed options. This is mostly for internal use by
7
- # dynamic_image_tag and dynamic_image_url.
8
- def dynamic_image_options(image, options = {})
9
- options.symbolize_keys!
10
-
11
- options = {:crop => false}.merge(options)
12
- url_options = {:controller => "/images", :action => :render_dynamic_image, :id => image}
14
+ # Returns an HTML image tag for the record. If no size is given, it will
15
+ # render at the original size.
16
+ #
17
+ # ==== Options
18
+ # * <tt>:alt</tt>: If no alt text is given, it will default to the
19
+ # filename of the uploaded image.
20
+ #
21
+ # See +dynamic_image_url+ for info on how to size and cropping. Options
22
+ # supported by +polymorphic_url+ will be passed to the router. Any other
23
+ # options will be added as HTML attributes.
24
+ #
25
+ # ==== Examples
26
+ #
27
+ # image = Image.find(params[:id])
28
+ # dynamic_image_tag(image)
29
+ # # => <img alt="My file" height="200" src="..." width="320" />
30
+ # dynamic_image_tag(image, size: "100x100", alt="Avatar")
31
+ # # => <img alt="Avatar" height="62" src="..." width="100" />
32
+ def dynamic_image_tag(record_or_array, options={})
33
+ record = extract_record(record_or_array)
34
+ options = {
35
+ alt: image_alt(record.filename)
36
+ }.merge(options)
37
+
38
+ size = fit_size!(record_or_array, options)
39
+ url_options = options.extract!(*allowed_dynamic_image_url_options)
40
+ html_options = { size: size }.merge(options)
41
+
42
+ image_tag(
43
+ dynamic_image_path_with_size(
44
+ record_or_array,
45
+ size,
46
+ url_options
47
+ ),
48
+ html_options
49
+ )
50
+ end
13
51
 
14
- if options[:original]
15
- url_options[:original] = 'original'
16
- options.delete(:original)
17
- end
52
+ # Returns the URL for a DynamicImage::Model record.
53
+ #
54
+ # ==== Options
55
+ #
56
+ # * <tt>:size</tt> - Desired image size, supplied as "{width}x{height}".
57
+ # The image will be scaled to fit. A partial size like "100x" or "x100"
58
+ # can be given, if you want a fixed width or height.
59
+ # * <tt>:crop</tt> - If true, the image will be cropped to the given size.
60
+ # * <tt>:upscale</tt> - By default, DynamicImage only scale images down,
61
+ # never up. Pass <tt>upscale: true</tt> to force upscaling.
62
+ #
63
+ # Any options supported by +polymorphic_url+ are also accepted.
64
+ #
65
+ # ==== Examples
66
+ #
67
+ # image = Image.find(params[:id])
68
+ # dynamic_image_url(image)
69
+ # # => "http://example.com/images/96...d1/300x187/1-2014062020...00.jpg"
70
+ # dynamic_image_url(image, size: '100x100')
71
+ # # => "http://example.com/images/72...c2/100x62/1-2014062020...00.jpg"
72
+ # dynamic_image_url(image, size: '100x100', crop: true)
73
+ # # => "http://example.com/images/a4...6b/100x100/1-2014062020...00.jpg"
74
+ def dynamic_image_url(record_or_array, options={})
75
+ size = fit_size!(record_or_array, options)
76
+ dynamic_image_url_with_size(record_or_array, size, options)
77
+ end
18
78
 
19
- # Image sizing
20
- if options[:size]
21
- new_size = Vector2d.new(options[:size])
22
- image_size = Vector2d.new(image.size)
79
+ # Returns a path to the original uploaded file, without any processing
80
+ # applied. Sizing options are not supported.
81
+ def original_dynamic_image_path(record_or_array, options={})
82
+ dynamic_image_path(record_or_array, { action: :original }.merge(options))
83
+ end
23
84
 
24
- unless options[:upscale]
25
- new_size.x = image_size.x if new_size.x > 0 && new_size.x > image_size.x
26
- new_size.y = image_size.y if new_size.y > 0 && new_size.y > image_size.y
27
- end
85
+ # Returns a URL to the original uploaded file, without any processing
86
+ # applied. Sizing options are not supported.
87
+ def original_dynamic_image_url(record_or_array, options={})
88
+ dynamic_image_url(record_or_array, { action: :original }.merge(options))
89
+ end
28
90
 
29
- unless options[:crop]
30
- new_size = image_size.constrain_both(new_size)
31
- end
91
+ # Same as +dynamic_image_path+, but points to an image with any
92
+ # pre-cropping disabled.
93
+ def uncropped_dynamic_image_path(record_or_array, options={})
94
+ dynamic_image_path(record_or_array, { action: :uncropped }.merge(options))
95
+ end
32
96
 
33
- options[:size] = new_size.round.to_s
34
- url_options[:size] = options[:size]
35
- end
36
- options.delete :crop
97
+ # Same as +dynamic_image_tag+, but renders an image with any
98
+ # pre-cropping disabled.
99
+ def uncropped_dynamic_image_tag(record_or_array, options={})
100
+ dynamic_image_tag(record_or_array, { action: :uncropped }.merge(options))
101
+ end
37
102
 
38
- if options[:no_size_attr]
39
- options.delete :no_size_attr
40
- options.delete :size
41
- end
103
+ # Same as +dynamic_image_url+, but points to an image with any
104
+ # pre-cropping disabled.
105
+ def uncropped_dynamic_image_url(record_or_array, options={})
106
+ dynamic_image_url(record_or_array, { action: :uncropped }.merge(options))
107
+ end
42
108
 
43
- # Filterset
44
- if options[:filterset]
45
- url_options[:filterset] = options[:filterset]
46
- options.delete :filterset
47
- end
109
+ private
48
110
 
49
- # Filename
50
- if options[:filename]
51
- filename = options[:filename]
52
- unless filename =~ /\.[\w]{1,4}$/
53
- filename += "." + image.filename.split(".").last
54
- end
55
- url_options[:filename] = filename
56
- else
57
- url_options[:filename] = image.filename
58
- end
111
+ def allowed_dynamic_image_url_options
112
+ [
113
+ :format, :only_path, :protocol, :host, :subdomain, :domain,
114
+ :tld_length, :port, :anchor, :trailing_slash, :script_name,
115
+ :action, :routing_type
116
+ ]
117
+ end
59
118
 
60
- # Alt attribute
61
- options[:alt] ||= image.name if image.name?
62
- options[:alt] ||= image.filename.split('.').first.capitalize
119
+ def default_format_for_image(record)
120
+ Mime::Type.lookup(record.safe_content_type).to_sym
121
+ end
63
122
 
64
- if options.has_key?(:only_path)
65
- url_options[:only_path] = options[:only_path]
66
- options[:only_path] = nil
67
- end
68
- if options.has_key?(:host)
69
- url_options[:host] = options[:host]
70
- options[:host] = nil
71
- end
123
+ def dynamic_image_digest(record, action, size=nil)
124
+ key = [action || 'show', record.id, size].compact.join('-')
125
+ DynamicImage.digest_verifier.generate(key)
126
+ end
72
127
 
73
- {:url => url_for(url_options), :options => options}
128
+ def dynamic_image_path_with_size(record_or_array, size=nil, options={})
129
+ dynamic_image_url_with_size(record_or_array, size, { routing_type: :path }.merge(options))
74
130
  end
75
131
 
76
- # Returns an image tag for the provided image model, works similar to the rails <tt>image_tag</tt> helper.
77
- #
78
- # The following options are supported (the rest will be forwarded to <tt>image_tag</tt>):
79
- #
80
- # * :size - Resize the image to fit these proportions. Size is given as a string with the format
81
- # '100x100'. Either dimension can be omitted, for example: '100x'
82
- # * :crop - Crop the image to the size given. (Boolean, default: <tt>false</tt>)
83
- # * :no_size_attr - Do not include width and height attributes in the image tag. (Boolean, default: false)
84
- # * :filterset - Apply the given filterset to the image
85
- #
86
- # ==== Examples
87
- #
88
- # dynamic_image_tag(@image) # Original image
89
- # dynamic_image_tag(@image, :size => "100x") # Will be 100px wide
90
- # dynamic_image_tag(@image, :size => "100x100") # Will fit within 100x100
91
- # dynamic_image_tag(@image, :size => "100x100", :crop => true) # Will be cropped to 100x100
92
- #
93
- def dynamic_image_tag(image, options = {})
94
- parsed_options = dynamic_image_options(image, options)
95
- image_tag(parsed_options[:url], parsed_options[:options] ).gsub(/\?[\d]+/,'').html_safe
132
+ def dynamic_image_url_with_size(record_or_array, size=nil, options={})
133
+ record = extract_record(record_or_array)
134
+ options = {
135
+ routing_type: :url,
136
+ action: nil,
137
+ format: default_format_for_image(record),
138
+ size: size
139
+ }.merge(options)
140
+ options[:digest] = dynamic_image_digest(record, options[:action], options[:size])
141
+ polymorphic_url(record_or_array, options)
96
142
  end
97
143
 
98
- # Returns an url corresponding to the provided image model.
99
- # Special options are documented in ApplicationHelper.dynamic_image_tag, only <tt>:size</tt>, <tt>:filterset</tt> and <tt>:crop</tt> apply.
100
- def dynamic_image_url(image, options = {})
101
- parsed_options = dynamic_image_options(image, options)
102
- parsed_options[:url]
144
+ def fit_size!(record_or_array, options)
145
+ record = extract_record(record_or_array)
146
+ action = options[:action].try(:to_s)
147
+ size_opts = options.extract!(:size, :crop, :upscale)
148
+
149
+ if size_opts[:size]
150
+ DynamicImage::ImageSizing.new(
151
+ record,
152
+ uncropped: (action == "uncropped")
153
+ ).fit(size_opts[:size], size_opts).floor.to_s
154
+ elsif action != "original"
155
+ record.size.floor.to_s
156
+ else
157
+ nil
158
+ end
103
159
  end
104
160
  end
105
161
  end
106
-
107
- ActionView::Base.send(:include, DynamicImage::Helper)