rack-thumb-proxy 0.0.8

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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack-thumb-proxy.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'ruby-debug19'
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Lee Hambley
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # Rack::Thumb::Proxy
2
+
3
+ Resize remotely hosted images not hosted on your servers dynamically.
4
+ Safely proxy request
5
+
6
+ `Rack::Thumb::Proxy` is a project in the spirit of `Rack::Thumb`, but
7
+ for use in situations when one doesn't host the images statically on
8
+ one's own server, consider such an example:
9
+
10
+ <img src="http://a-site-which-doesnt-run-ssl.com/the/product/image.png" />
11
+
12
+ One could download, resize, host and be responsible for this image, but
13
+ in the days of realtime systems, massive data, clustered storage,
14
+ etcetera, why bother? One could hot-link the image, but then this
15
+ doesn't work for cross-protocol, with https in the mix.
16
+
17
+ <img src="/media/thumbs/http%3A%2F%2Fa-site-which-doesnt-run-ssl.com%2Fthe%2Fproduct%2Fimage.png" />
18
+
19
+ Where, in this case one has predefined `thumbs` as a category. One could
20
+ also do something such as:
21
+
22
+ <img src="/media/50x50/http%3A%2F%2Fa-site-which-doesnt-run-ssl.com%2Fthe%2Fproduct%2Fimage.png" />
23
+
24
+ or even
25
+
26
+ <img src="/media/50x50/http%3A%2F%2Fa-site-which-doesnt-run-ssl.com%2Fthe%2Fproduct%2Fimage.png" />
27
+
28
+ When combined with a CDN or Rack::Cache, this shouldn't cause too heavy
29
+ a performance penalty, and images from upstream will even be cached
30
+ locally.
31
+
32
+ ## Safety
33
+
34
+ To ensure that someone doesn't decide to use your server resources to resize
35
+ their entire collection of cat pictures, there's a hash mechanism which
36
+ is also available. This works very much like `Rack::Thumb`.
37
+
38
+ To use this feature, simply configure `Rack::Thumb::Proxy` with a
39
+ `secret`, and a `key_length` (the latter may be ommitted and defaults
40
+ to 10), urls will be then generated with the following appearance:
41
+
42
+ <img src="/media/15a5683b74/50x50/http%3A%2F%2Fa-site-which-doesnt-run-ssl.com%2Fthe%2Fproduct%2Fimage.png" />
43
+
44
+ The key is calculated as a result of the following pattern:
45
+
46
+ "%s\t%s\t%s" % <secret>, <options>, <url encoded image source>
47
+
48
+ For example with a terrible secret of `secret`:
49
+
50
+ echo "secret\t50x50\thttp%3A%2F%2Fa-site-which-doesnt-run-ssl.com%2Fthe%2Fproduct%2Fimage.png" | openssl dgst -sha1
51
+
52
+ The resulting SHA is as you see above. it is recommended that you choose
53
+ a secret using a token generation tool, if you are using Rails, you have
54
+ one baked-in simply use `rake secret` from your Rails project root.
55
+
56
+ Requests which do not match the expected format will receive a `400 Bad
57
+ Request` response.
58
+
59
+ ## (Rails) Helpers
60
+
61
+ A helper module is provided which can be used in Rails, Sinatra, or your
62
+ unit tests. This is loaded automatically via a Railtie into Rails,
63
+ available from all views. The following methods are defined:
64
+
65
+ proxied_image_url("image url", options)
66
+ proxied_image_tag("image url", options)
67
+
68
+ Somewhat of a private API are the following, which you may find useful:
69
+
70
+ signature_hash_for("image url", options)
71
+
72
+ The image url passed here should not be URL encoded, as
73
+ `Rack::Thumb::Proxy` will encode it correctly for you.
74
+
75
+ ## `options`
76
+
77
+ "50x" `[String]`Constrain to 50 pixels height, maintaining
78
+ original aspect ratio
79
+
80
+ "x100" `[String]`Constrain to 100 pixels width, maintaining
81
+ original aspect ratio
82
+
83
+ "50x75n" `[String]` Constrain to 50 pixels height, distorting the
84
+ image to acheive a 75 pixels width with *northern* gravity
85
+ (see below)
86
+
87
+ "50x75" `[String]` Crop to 50 pixels height, without distorting the
88
+ image to acheive a 75 pixels width
89
+
90
+ :label `[Symbol]` Take the options specified in
91
+ the label (see below)
92
+
93
+ {width: 123, height: } `[Hash]` The keys `width`, `height`, and
94
+ `gravity` are accepted
95
+
96
+ ## Option Labels
97
+
98
+ One can use the configuration API as such to name a label:
99
+
100
+ Rack::Thumb::Proxy.configure do
101
+ option_label :product_thumbnail, "100x100"
102
+ end
103
+
104
+ ## Gravity
105
+
106
+ Gravity can be specified which will pull the crop (in the case that both
107
+ width, and height are given), it will focus the cropped area of the
108
+ image, valid options are `n`, `ne`, `e`, `se`, `s`, `sw`, `w`, `nw`. The
109
+ default gracity is `c`, which will focus the crop on the centre of the
110
+ image.
111
+
112
+ ## No Magic
113
+
114
+ If you don't need to resize the image, specifying a magical option of
115
+ `noop` disables any kind of resizing, this is useful if you just need to
116
+ use the software a as a proxy. When operating in this mode there is no
117
+ dependency on imagemagick.
118
+
119
+ ## Installation
120
+
121
+ Add this line to your application's Gemfile:
122
+
123
+ gem 'rack-thumb-proxy'
124
+
125
+ And then execute:
126
+
127
+ $ bundle
128
+
129
+ Or install it yourself as:
130
+
131
+ $ gem install rack-thumb-proxy
132
+
133
+ ## Usage
134
+
135
+ The included railtie will ensure that this is available in your Rails
136
+ application, you can simply use something like:
137
+
138
+ match '/media', :to => Rack::Thumb::Proxy
139
+
140
+ If you need to configure additional options, this can be done in an
141
+ initializer, or by passing a configuaation hash to the
142
+ Rack::Thumb::Proxy initialzer. The former is preferred.
143
+
144
+ ## Example Configuration
145
+
146
+ Rack::Thumb::Proxy.configure do
147
+ prefix "/media/"
148
+ secret "d94bba3d2e0b4809a570158506"
149
+ key_length 10
150
+ end
151
+
152
+ When one doesn't want to use the configuration API, the more succinct
153
+ version would be to do something like:
154
+
155
+ # ./config/routes.rb
156
+ match '/media' => Rack::Thumb::Proxy { prefix: "/",
157
+ secret: "ABC1234", key_length: 10 }
158
+
159
+ # config.ru
160
+ use Rack::Thumb::Proxy { prefix: "/", secret: "ABC1234", key_length: 10 }
161
+
162
+ One complication is that when using the link generator functions, one
163
+ **must** use the configuration API, otherwise the default path will be
164
+ `/`.
165
+
166
+ ## To Do
167
+
168
+ * Implement Railtie/helpers.
169
+ * Ensure the hash signatures are checked.
170
+ * Make it possible to control the cache control header.
171
+ * Don't use open-uri.
172
+ * Check earlier in the process that upstream is an image,
173
+ don't rely on MiniMagick to blow up on non-image content.
174
+ * Take the cache-control headers from upstream
175
+ if we can.
176
+ * Allow a local cache for the images, perhaps somewhere
177
+ in `/tmp`.
178
+ * Actually support option labels, it just looks good in
179
+ the readme right now, alas.
180
+
181
+ ## Contributing
182
+
183
+ 1. Fork it
184
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
185
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
186
+ 4. Push to the branch (`git push origin my-new-feature`)
187
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "test"
8
+ t.test_files = FileList['test/test*.rb']
9
+ end
10
+
11
+ task :default => [:test]
data/example/config.ru ADDED
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'rack-thumb-proxy'
5
+
6
+ run Rack::Thumb::Proxy
@@ -0,0 +1,53 @@
1
+ module Rack
2
+ module Thumb
3
+ class Proxy
4
+
5
+ class Configuration
6
+
7
+ attr_reader :cache_control_headers
8
+ attr_reader :option_labels
9
+
10
+ def initialize
11
+ initialize_defaults!
12
+ end
13
+
14
+ def mount_point(new_mount_point = nil)
15
+ @mount_point = new_mount_point if new_mount_point
16
+ return @mount_point
17
+ end
18
+
19
+ def key_length(new_key_length = nil)
20
+ @key_length = new_key_length if new_key_length
21
+ return @key_length
22
+ end
23
+
24
+ def secret(new_secret = nil)
25
+ @secret = new_secret if new_secret
26
+ return @secret
27
+ end
28
+
29
+ def option_label(label, options)
30
+ option_labels.merge!(label.to_sym => options)
31
+ end
32
+
33
+ def hash_signatures_in_use?
34
+ !!@secret
35
+ end
36
+
37
+ def initialize_defaults!
38
+ @secret = nil
39
+ @key_length = 10
40
+ @mount_point = '/'
41
+ @option_labels = {}
42
+ @cache_control_headers = {'Cache-Control' => 'max-age=86400, public, must-revalidate'}
43
+ end
44
+ alias :reset_defaults! :initialize_defaults!
45
+ private :initialize_defaults!
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,11 @@
1
+ module Rack
2
+ module Thumb
3
+ class Proxy
4
+ class Railtie < Rails::Railtie
5
+ initializer "rack-thumb-proxy.view_helpers" do
6
+ ActionView::Base.send :include, ViewHelpers
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Rack
2
+ module Thumb
3
+ class Proxy
4
+ VERSION = "0.0.8"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,55 @@
1
+ module Rack
2
+ module Thumb
3
+ class Proxy
4
+ module ViewHelpers
5
+
6
+ def proxied_image_url(image_url, options = nil)
7
+
8
+ if rack_thumb_proxy_hash_signatures_enabled?
9
+ signature = hash_signature_for(image_url, options)
10
+ end
11
+
12
+ options = rack_thumb_proxy_options_to_s(options)
13
+ escaped_image_url = escape_image_url(image_url)
14
+ mount_point = rack_thumb_proxy_configuration.mount_point
15
+
16
+ mount_point + [signature, options, escaped_image_url].compact.join("/")
17
+
18
+ end
19
+
20
+ def hash_signature_for(image_url, options = nil)
21
+ return nil unless rack_thumb_proxy_hash_signatures_enabled?
22
+ key_length = rack_thumb_proxy_configuration.key_length
23
+ secret = rack_thumb_proxy_configuration.secret
24
+ ('%s\t%s\t%s' % [secret, options, escape_image_url(image_url)])[0..key_length-1]
25
+ end
26
+
27
+ private
28
+
29
+ def escape_image_url(image_url)
30
+ CGI.escape(image_url)
31
+ end
32
+
33
+ def rack_thumb_proxy_options_to_s(options = nil)
34
+ return nil if options.nil?
35
+ if options.is_a?(String)
36
+ options
37
+ elsif options.is_a?(Hash)
38
+ sprintf("%sx%s%s", options[:width], options[:height], options[:gravity])
39
+ else
40
+ raise RuntimeError, 'Not implemented yet, see the TODO in README.md'
41
+ end
42
+ end
43
+
44
+ def rack_thumb_proxy_configuration
45
+ Rack::Thumb::Proxy.configuration
46
+ end
47
+
48
+ def rack_thumb_proxy_hash_signatures_enabled?
49
+ rack_thumb_proxy_configuration.hash_signatures_in_use?
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,240 @@
1
+ require 'cgi'
2
+ require 'open-uri'
3
+ require 'tempfile'
4
+ require 'rack-thumb-proxy/version'
5
+ require 'rack-thumb-proxy/configuration'
6
+ require 'rack-thumb-proxy/view_helpers'
7
+
8
+ require 'rack-thumb-proxy/railtie' if defined?(Rails)
9
+
10
+ module Rack
11
+
12
+ module Thumb
13
+
14
+ class Proxy
15
+
16
+ class << self
17
+
18
+ attr_writer :configuration
19
+
20
+ def configure(&block)
21
+ configuration.instance_eval(&block)
22
+ configuration
23
+ end
24
+
25
+ def configuration
26
+ @configuration ||= Configuration.new
27
+ end
28
+
29
+ def call(env)
30
+ new(env).call
31
+ end
32
+
33
+ end
34
+
35
+ def initialize(env)
36
+ @env = env
37
+ @path = env['PATH_INFO']
38
+ end
39
+
40
+ def call
41
+ if request_matches?
42
+ validate_signature! &&
43
+ retreive_upstream! &&
44
+ transform_image! &&
45
+ format_response!
46
+ response.finish
47
+ else
48
+ [404, {'Content-Length' => 9.to_s, 'Content-Type' => 'text/plain'}, ['Not Found']]
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def validate_signature!
55
+ true
56
+ end
57
+
58
+ def retreive_upstream!
59
+ begin
60
+ open(request_url, 'rb') do |f|
61
+ tempfile.binmode
62
+ tempfile.write(f.read)
63
+ tempfile.flush
64
+ end
65
+ rescue
66
+ write_error_to_response!
67
+ return false
68
+ end
69
+ return true
70
+ end
71
+
72
+ def format_response!
73
+ response.status = 200
74
+ response.headers["Content-Type"] = mime_type_from_request_url
75
+ response.headers["Content-Length"] = transformed_image_file_size_in_bytes.to_s
76
+ response.body << read_tempfile
77
+ true
78
+ end
79
+
80
+ def read_tempfile
81
+ tempfile.rewind
82
+ tempfile.read
83
+ end
84
+
85
+ def tempfile
86
+ @_tempfile ||= Tempfile.new('rack_thumb_proxy')
87
+ end
88
+
89
+ def tempfile_path
90
+ tempfile.path
91
+ end
92
+
93
+ def transform_image!
94
+
95
+ return true unless should_resize?
96
+
97
+ begin
98
+ require 'mapel'
99
+
100
+ width, height = dimensions_from_request_options
101
+ owidth, oheight = dimensions_from_tempfile
102
+
103
+ width = [width, owidth].min if width
104
+ height = [height, oheight].min if height
105
+
106
+ cmd = Mapel(tempfile_path)
107
+
108
+ if width && height
109
+ cmd.gravity(request_gravity)
110
+ cmd.resize!(width, height)
111
+ else
112
+ cmd.resize(width, height, 0, 0, '>')
113
+ end
114
+
115
+ cmd.to(tempfile_path).run
116
+
117
+ rescue
118
+ puts $!, $@
119
+ write_error_to_response!
120
+ return false
121
+ end
122
+
123
+ true
124
+ end
125
+
126
+ def should_resize?
127
+ !request_options.empty?
128
+ end
129
+
130
+ def should_verify_hash_signature?
131
+ configuration.hash_signatures_in_use?
132
+ end
133
+
134
+ def configuration
135
+ self.class.configuration
136
+ end
137
+
138
+ def request_hash_signature
139
+ @_request_match_data["hash_signature"]
140
+ end
141
+
142
+ def request_options
143
+ @_request_match_data["options"]
144
+ end
145
+
146
+ def request_gravity
147
+ {
148
+ 'nw' => :northwest,
149
+ 'n' => :north,
150
+ 'ne' => :northeast,
151
+ 'w' => :west,
152
+ 'c' => :center,
153
+ 'e' => :east,
154
+ 'sw' => :southwest,
155
+ 's' => :south,
156
+ 'se' => :southeast
157
+ }.fetch(request_gravity_shorthand, :center)
158
+ end
159
+
160
+ def request_gravity_shorthand
161
+ @_request_match_data["gravity"]
162
+ end
163
+
164
+ def request_url
165
+ CGI.unescape(escaped_request_url)
166
+ end
167
+
168
+ def escaped_request_url
169
+ @_request_match_data["escaped_url"]
170
+ end
171
+
172
+ def request_matches?
173
+ @_request_match_data = @path.match(routing_pattern)
174
+ end
175
+
176
+ def witdh_from_tempfile
177
+ dimensions_from_tempfile.first
178
+ end
179
+
180
+ def height_from_tempfile
181
+ dimensions_from_tempfile.last
182
+ end
183
+
184
+ def dimensions_from_tempfile
185
+ require 'mapel' unless defined?(Mapel)
186
+ Mapel.info(tempfile_path)[:dimensions]
187
+ end
188
+
189
+ def width_from_request_options
190
+ dimensions_from_request_options.first
191
+ end
192
+
193
+ def height_from_request_options
194
+ dimensions_from_request_options.last
195
+ end
196
+
197
+ def dimensions_from_request_options
198
+ width, height = request_options.split('x').map(&:to_i).collect { |dim| dim == 0 ? nil : dim }
199
+ [width, height]
200
+ end
201
+
202
+ def transformed_image_file_size_in_bytes
203
+ ::File.size(tempfile_path)
204
+ end
205
+
206
+ # Examples: http://rubular.com/r/oPRK1t31yv
207
+ def routing_pattern
208
+ /^\/(?<hash_signature>[a-z0-9]{10}|)\/?(?<options>(:?[0-9]*x+[0-9]*|))(?<gravity>c|n|ne|e|s|sw|w|nw|)\/?(?<escaped_url>https?.*)$/
209
+ end
210
+
211
+ def response
212
+ @_response ||= Rack::Response.new
213
+ end
214
+
215
+ def write_error_to_response!
216
+ response.status = 500
217
+ response.headers['Content-Type'] = 'text/plain'
218
+ response.body << $!.message
219
+ response.body << "\n\n"
220
+ response.body << $!.backtrace.join("\n")
221
+ end
222
+
223
+ def request_url_file_extension
224
+ ::File.extname(request_url)
225
+ end
226
+
227
+ def mime_type_from_request_url
228
+ {
229
+ '.png' => 'image/png',
230
+ '.gif' => 'image/gif',
231
+ '.jpg' => 'image/jpeg',
232
+ '.jpeg' => 'image/jpeg'
233
+ }.fetch(request_url_file_extension, 'application/octet-stream')
234
+ end
235
+
236
+ end
237
+
238
+ end
239
+
240
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/rack-thumb-proxy/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+
6
+ gem.authors = ["Lee Hambley"]
7
+ gem.email = ["lee.hambley@gmail.com"]
8
+ gem.description = %q{ Rack middleware for resizing proxied requests for images which don't reside on your own servers. }
9
+ gem.summary = %q{ For more information see https://github.com/leehambley/rack-thumb-proxy }
10
+ gem.homepage = ""
11
+
12
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
+ gem.files = `git ls-files`.split("\n")
14
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ gem.name = "rack-thumb-proxy"
16
+ gem.require_paths = ["lib"]
17
+
18
+ gem.version = Rack::Thumb::Proxy::VERSION
19
+
20
+ gem.add_dependency 'rack'
21
+
22
+ gem.add_development_dependency 'minitest', '~> 2.11'
23
+ gem.add_development_dependency 'webmock', '~> 1.8.0'
24
+ gem.add_development_dependency 'rack-test', '~> 0.6.1'
25
+ gem.add_development_dependency 'mapel'
26
+ gem.add_development_dependency 'dimensions'
27
+
28
+ end
Binary file
Binary file
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ class TestConfigurationApi < MiniTest::Unit::TestCase
4
+
5
+ def test_it_should_return_a_configuration_object
6
+ assert Rack::Thumb::Proxy.configuration.is_a?(Rack::Thumb::Proxy::Configuration)
7
+ end
8
+
9
+ def test_default_options
10
+ Rack::Thumb::Proxy.configuration.reset_defaults!
11
+ default_configuration = Rack::Thumb::Proxy.configuration
12
+ assert_equal nil, default_configuration.secret
13
+ assert_equal 10, default_configuration.key_length
14
+ assert_equal '/', default_configuration.mount_point
15
+ assert_equal({'Cache-Control' => 'max-age=86400, public, must-revalidate'}, default_configuration.cache_control_headers)
16
+ end
17
+
18
+ def test_it_should_take_a_block_which_is_class_evaluated_keeping_all_options
19
+ Rack::Thumb::Proxy.configuration.reset_defaults!
20
+ configuration = Rack::Thumb::Proxy.configure do
21
+ secret '123'
22
+ key_length 10
23
+ mount_point '/'
24
+ end
25
+ assert_equal '123', configuration.secret
26
+ end
27
+
28
+ def test_setting_option_labels_via_the_configuration_api
29
+ Rack::Thumb::Proxy.configuration.reset_defaults!
30
+ configuration = Rack::Thumb::Proxy.configure do
31
+ option_label :thumbnail, '50x75c'
32
+ end
33
+ assert configuration.option_labels.has_key?(:thumbnail)
34
+ end
35
+
36
+ def test_hash_signatures_are_correctly_enabled_and_disabled_based_on_the_presense_of_the_secret
37
+ Rack::Thumb::Proxy.configuration.reset_defaults!
38
+ configuration = Rack::Thumb::Proxy.configuration
39
+ refute configuration.secret
40
+ refute configuration.hash_signatures_in_use?
41
+ configuration.secret('something truthy')
42
+ assert configuration.secret
43
+ assert configuration.hash_signatures_in_use?
44
+ end
45
+
46
+ end
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'cgi'
5
+
6
+ require 'rack-thumb-proxy'
7
+
8
+ require 'rack/test'
9
+
10
+ require 'dimensions'
11
+
12
+ require 'digest/md5'
13
+
14
+ require 'minitest/autorun'
15
+ require 'minitest/pride'
16
+ require 'minitest/mock'
17
+
18
+ require 'webmock/minitest'
19
+
20
+ WebMock.disable_net_connect!
21
+
22
+ require 'ruby-debug'
23
+ require 'mapel'
24
+
25
+ class ViewHelperSurrogate
26
+ include Rack::Thumb::Proxy::ViewHelpers
27
+ end
@@ -0,0 +1,124 @@
1
+ require 'test_helper'
2
+
3
+ class TestRackThumbProxy < MiniTest::Unit::TestCase
4
+
5
+ include Rack::Test::Methods
6
+
7
+ def app
8
+ Rack::Thumb::Proxy
9
+ end
10
+
11
+ def test_it_should_repond_with_not_found_when_the_upstream_url_is_bunk
12
+ get "/something/that/doesnt/even/match/a/little/bit"
13
+ assert last_response.not_found?
14
+ end
15
+
16
+ def test_it_should_return_success_with_the_correct_content_length_when_a_matching_url_is_found_for_a_noop_image
17
+ stub_image_request!('250x.gif', 'http://www.example.com/images/noop-kittens.gif')
18
+ get '/' + escape('http://www.example.com/images/noop-kittens.gif')
19
+ assert last_response.ok?
20
+ assert_equal file_size_for_fixture('250x.gif').to_s, last_response.headers.fetch('Content-Length')
21
+ assert_equal file_hash_for_fixture('250x.gif'), file_hash_from_string(last_response.body)
22
+ assert_dimensions last_response.body, 250, 250
23
+ end
24
+
25
+ def test_it_should_return_a_smaller_image_when_resizing_with_minimagick
26
+ stub_image_request!('250x.gif', 'http://www.example.com/images/kittens.gif')
27
+ get '/50x50/' + escape('http://www.example.com/images/kittens.gif')
28
+ assert last_response.ok?
29
+ assert_dimensions last_response.body, 50, 50
30
+ end
31
+
32
+ def test_it_should_crop_when_the_ratio_cannot_be_maintained
33
+ stub_image_request!('200x100.gif', 'http://www.example.com/images/sharkjumping.gif')
34
+ get '/50x50/' + escape('http://www.example.com/images/sharkjumping.gif')
35
+ assert last_response.ok?
36
+ assert_dimensions last_response.body, 50, 50
37
+ end
38
+
39
+ def test_it_should_retain_the_aspec_ratio
40
+ stub_image_request!('200x100.gif', 'http://www.example.com/images/sharkjumping.gif')
41
+ get '/50x/' + escape('http://www.example.com/images/sharkjumping.gif')
42
+ assert last_response.ok?
43
+ assert_dimensions last_response.body, 50, 25
44
+ end
45
+
46
+ def test_it_should_accept_the_gravity_setting_without_breaking_the_resize
47
+ stub_image_request!('200x100.gif', 'http://www.example.com/images/sharkjumping.gif')
48
+ get '/50x50e/' + escape('http://www.example.com/images/sharkjumping.gif')
49
+ assert last_response.ok?
50
+ east = last_response.body
51
+ get '/50x50w/' + escape('http://www.example.com/images/sharkjumping.gif')
52
+ assert last_response.ok?
53
+ west = last_response.body
54
+ refute_equal file_hash_from_string(east), file_hash_from_string(west)
55
+ end
56
+
57
+ def test_default_gravity_is_center_when_cropping
58
+ stub_image_request!('200x100.gif', 'http://www.example.com/images/sharkjumping.gif')
59
+ get '/75x75/' + escape('http://www.example.com/images/sharkjumping.gif')
60
+ no_gravity = last_response.body
61
+ get '/75x75c/' + escape('http://www.example.com/images/sharkjumping.gif')
62
+ c_gravity = last_response.body
63
+ assert_equal file_hash_from_string(no_gravity), file_hash_from_string(c_gravity)
64
+ end
65
+
66
+ def test_should_respond_with_the_proper_mimetype_for_known_image_types
67
+ skip
68
+ end
69
+
70
+ def test_should_respond_with_application_octet_stream_mimetype_for_unknown
71
+ skip
72
+ end
73
+
74
+ private
75
+
76
+ def stub_image_request!(file, url)
77
+ stub_request(:any, url).to_return(
78
+ body: File.read(filename_for_fixture(file)),
79
+ headers: {
80
+ 'Content-Length' => file_size_for_fixture(file)
81
+ },
82
+ status: 200
83
+ )
84
+ end
85
+
86
+ def escape(string)
87
+ CGI.escape(string)
88
+ end
89
+
90
+ def file_size_for_fixture(file)
91
+ File.size(filename_for_fixture(file))
92
+ end
93
+
94
+ def file_hash_from_string(string)
95
+ Digest::MD5.hexdigest(string)
96
+ end
97
+
98
+ def file_hash_for_fixture(file)
99
+ file_hash_from_string(File.read(filename_for_fixture(file)))
100
+ end
101
+
102
+ def filename_for_fixture(file)
103
+ File.expand_path('test/fixtures/' + file)
104
+ end
105
+
106
+ def fixture_file_exists?(file)
107
+ File.exists?(file) rescue false
108
+ end
109
+
110
+ def assert_dimensions(input, expected_width, expected_height)
111
+ unless fixture_file_exists?(input)
112
+ tf = Tempfile.new('assert_dimensions')
113
+ tf.write input
114
+ tf.flush
115
+ tf.rewind
116
+ input = tf.path
117
+ end
118
+ actual_width, actual_height = Dimensions.dimensions(input)
119
+ assert_equal "#{expected_width}x#{expected_height}", "#{actual_width}x#{actual_height}"
120
+ ensure
121
+ tf.close if tf rescue nil
122
+ end
123
+
124
+ end
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+
3
+ class TestViewHelpers < MiniTest::Unit::TestCase
4
+
5
+ def test_without_any_options_or_special_configuration_the_view_helper_encodes_the_image_url_correctly
6
+ Rack::Thumb::Proxy.configuration.reset_defaults!
7
+ assert_equal "/http%3A%2F%2Fwww.example.com%2F1.png", vh.proxied_image_url("http://www.example.com/1.png")
8
+ end
9
+
10
+ def test_without_any_options_with_a_mount_point_configured_the_view_helper_encodes_the_image_url_correctly
11
+ Rack::Thumb::Proxy.configuration.reset_defaults!
12
+ Rack::Thumb::Proxy.configuration.mount_point("/mount/")
13
+ assert_equal "/mount/http%3A%2F%2Fwww.example.com%2F1.png", vh.proxied_image_url("http://www.example.com/1.png")
14
+ end
15
+
16
+ def test_with_a_width_option_only_configured_the_view_helper_encodes_the_image_url_correctly
17
+ Rack::Thumb::Proxy.configuration.reset_defaults!
18
+ assert_equal "/50x/http%3A%2F%2Fwww.example.com%2F1.png", vh.proxied_image_url("http://www.example.com/1.png", '50x')
19
+ end
20
+
21
+ def test_with_a_height_option_only_configured_the_view_helper_encodes_the_image_url_correctly
22
+ Rack::Thumb::Proxy.configuration.reset_defaults!
23
+ assert_equal "/x50/http%3A%2F%2Fwww.example.com%2F1.png", vh.proxied_image_url("http://www.example.com/1.png", 'x50')
24
+ end
25
+
26
+ def test_with_a_width_and_height_option_configured_the_view_helper_encodes_the_image_url_correctly
27
+ Rack::Thumb::Proxy.configuration.reset_defaults!
28
+ assert_equal "/75x50/http%3A%2F%2Fwww.example.com%2F1.png", vh.proxied_image_url("http://www.example.com/1.png", '75x50')
29
+ end
30
+
31
+ def test_with_a_width_and_height_option_as_a_hash_configured_the_view_helper_encodes_the_image_url_correctly
32
+ Rack::Thumb::Proxy.configuration.reset_defaults!
33
+ assert_equal "/45x90/http%3A%2F%2Fwww.example.com%2F1.png", vh.proxied_image_url("http://www.example.com/1.png", {width: 45, height: 90})
34
+ end
35
+
36
+ def test_with_a_width_height_and_gravity_options_as_a_hash_configured_the_view_helper_encodes_the_image_url_correctly
37
+ Rack::Thumb::Proxy.configuration.reset_defaults!
38
+ assert_equal "/45x90s/http%3A%2F%2Fwww.example.com%2F1.png", vh.proxied_image_url("http://www.example.com/1.png", {width: 45, height: 90, gravity: :s})
39
+ end
40
+
41
+ private
42
+
43
+ def vh
44
+ ViewHelperSurrogate.new
45
+ end
46
+
47
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-thumb-proxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.8
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lee Hambley
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-22 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: &70327349437160 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70327349437160
25
+ - !ruby/object:Gem::Dependency
26
+ name: minitest
27
+ requirement: &70327349436340 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '2.11'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70327349436340
36
+ - !ruby/object:Gem::Dependency
37
+ name: webmock
38
+ requirement: &70327349435680 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.8.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70327349435680
47
+ - !ruby/object:Gem::Dependency
48
+ name: rack-test
49
+ requirement: &70327349435200 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.6.1
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70327349435200
58
+ - !ruby/object:Gem::Dependency
59
+ name: mapel
60
+ requirement: &70327349434720 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70327349434720
69
+ - !ruby/object:Gem::Dependency
70
+ name: dimensions
71
+ requirement: &70327349434020 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70327349434020
80
+ description: ! ' Rack middleware for resizing proxied requests for images which don''t
81
+ reside on your own servers. '
82
+ email:
83
+ - lee.hambley@gmail.com
84
+ executables: []
85
+ extensions: []
86
+ extra_rdoc_files: []
87
+ files:
88
+ - .gitignore
89
+ - Gemfile
90
+ - LICENSE
91
+ - README.md
92
+ - Rakefile
93
+ - example/config.ru
94
+ - lib/rack-thumb-proxy.rb
95
+ - lib/rack-thumb-proxy/configuration.rb
96
+ - lib/rack-thumb-proxy/railtie.rb
97
+ - lib/rack-thumb-proxy/version.rb
98
+ - lib/rack-thumb-proxy/view_helpers.rb
99
+ - rack-thumb-proxy.gemspec
100
+ - test/fixtures/200x100.gif
101
+ - test/fixtures/250x.gif
102
+ - test/test_configuration_api.rb
103
+ - test/test_helper.rb
104
+ - test/test_rack_thumb_proxy.rb
105
+ - test/test_view_helpers.rb
106
+ homepage: ''
107
+ licenses: []
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 1.8.10
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: For more information see https://github.com/leehambley/rack-thumb-proxy
130
+ test_files:
131
+ - test/fixtures/200x100.gif
132
+ - test/fixtures/250x.gif
133
+ - test/test_configuration_api.rb
134
+ - test/test_helper.rb
135
+ - test/test_rack_thumb_proxy.rb
136
+ - test/test_view_helpers.rb