akdubya-rack-thumb 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,81 @@
1
+ == Rack::Thumb: Drop-in image thumbnailing for your Rack stack
2
+
3
+ <tt>Rack::Thumb</tt> is drop-in dynamic thumbnailing middleware for Rack-based
4
+ applications, featuring simple configuration, optional security (via url-signing),
5
+ and maximum flexibility.
6
+
7
+ == Getting Started
8
+
9
+ You will need ImageMagick and the Mapel gem (http://github.com/akdubya/mapel).
10
+
11
+ gem install akdubya-rack-thumb
12
+
13
+ # rackup.ru
14
+ require 'myapp'
15
+ require 'rack/thumb'
16
+
17
+ use Rack::Thumb
18
+ use Rack::Static, :urls => ["/media"]
19
+
20
+ run MyApp.new
21
+
22
+ <tt>Rack::Thumb</tt> is file-server agnostic to provide maximum deployment
23
+ flexibility. Simply set it up in front of any application that's capable of
24
+ serving source images (I'm using it with an app that serves images from CouchDB).
25
+
26
+ See the example directory for more <tt>Rack</tt> configurations. Because
27
+ thumbnailing is an expensive operation, you should run <tt>Rack::Thumb</tt>
28
+ behind a cache, such as the excellent <tt>Rack::Cache</tt>.
29
+
30
+ == Rendering Options
31
+
32
+ <tt>Rack::Thumb</tt> intercepts requests for images that have urls of
33
+ the form <code>/path/to/image_{metadata}.ext</code> and returns rendered
34
+ thumbnails. Rendering options include +width+, +height+ and +gravity+. If
35
+ both +width+ and +height+ are supplied, images are cropped and resized
36
+ to fit the aspect ratio.
37
+
38
+ Link to thumbnails from your templates as follows:
39
+
40
+ /media/foobar_50x50.jpg # => Crop and resize to 50x50
41
+ /media/foobar_50x50-nw.jpg # => Crop and resize with northwest gravity
42
+ /media/foobar_50x.jpg # => Resize to a width of 50, preserving AR
43
+ /media/foobar_x50.jpg # => Resize to a height of 50, preserving AR
44
+
45
+ == URL Signing
46
+
47
+ To prevent pesky end-users and bots from flooding your application with
48
+ render requests you can set up <tt>Rack::Thumb</tt> to check for a <tt>SHA-1</tt>
49
+ signature that is unique to every url. Using this option, only thumbnails requested
50
+ by your templates will be valid. Example:
51
+
52
+ use Rack::Thumb(
53
+ :secret => "My secret", # => Don't tell anyone!
54
+ :key_length => "16" # => Only use 16 digits of the SHA-1 key
55
+ )
56
+
57
+ You can then use your +secret+ to generate secure links in your templates using
58
+ Ruby's built-in <tt>Digest::SHA1</tt> library:
59
+
60
+ /media/foobar_50x100-sw-a267c193a7eff046.jpg # => Successful
61
+ /media/foobar_120x250-a267c193a7eff046.jpg # => Returns a bad request error
62
+
63
+ There are no helper modules just yet but it's easy enough to roll your own.
64
+
65
+ == Deep Thoughts
66
+
67
+ <tt>Rack::Thumb</tt> respects any extra headers you set in your downstream app.
68
+ You are free to set caching policies, etc. however you like. Incoming HEAD requests
69
+ skip the rendering step.
70
+
71
+ There are a decent number of specs but the middleware isn't very strict at
72
+ checking options at the moment, and I'm sure there are a few edge cases that need
73
+ to be looked into. Comments, suggestions and bug reports are welcome.
74
+
75
+ == Meta
76
+
77
+ Written by Aleks Williams (http://github.com/akdubya)
78
+
79
+ Released under the MIT License: www.opensource.org/licenses/mit-license.php
80
+
81
+ github.com/akdubya/rack-thumb
@@ -0,0 +1,64 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+ require 'fileutils'
7
+
8
+ task :default => [:test]
9
+ task :spec => :test
10
+
11
+ name = 'rack-thumb'
12
+ version = '0.2.2'
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = name
16
+ s.version = version
17
+ s.summary = "Drop-in image thumbnailing for your Rack stack."
18
+ s.description = "Drop-in image thumbnailing middleware for your Rack stack (Merb, Sinatra, Rails, etc)."
19
+ s.author = "Aleksander Williams"
20
+ s.email = "alekswilliams@earthlink.net"
21
+ s.homepage = "http://github.com/akdubya/rack-thumb"
22
+ s.platform = Gem::Platform::RUBY
23
+ s.has_rdoc = true
24
+ s.files = %w(Rakefile README.rdoc) + Dir.glob("{lib,spec,example}/**/*")
25
+ s.require_path = "lib"
26
+ s.add_dependency("akdubya-mapel", ">= 0.1.1")
27
+ end
28
+
29
+ Rake::GemPackageTask.new(spec) do |p|
30
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
31
+ end
32
+
33
+ desc "Install as a system gem"
34
+ task :install => [ :package ] do
35
+ sh %{sudo gem install pkg/#{name}-#{version}.gem}
36
+ end
37
+
38
+ desc "Uninstall as a system gem"
39
+ task :uninstall => [ :clean ] do
40
+ sh %{sudo gem uninstall #{name}}
41
+ end
42
+
43
+ desc "Create a gemspec file"
44
+ task :make_spec do
45
+ File.open("#{name}.gemspec", "w") do |file|
46
+ file.puts spec.to_ruby
47
+ end
48
+ end
49
+
50
+ Rake::TestTask.new(:test) do |t|
51
+ t.libs << "spec"
52
+ t.test_files = FileList['spec/*_spec.rb']
53
+ t.verbose = true
54
+ end
55
+
56
+ Rake::RDocTask.new do |t|
57
+ t.rdoc_dir = 'rdoc'
58
+ t.title = "Rack Thumb: Drop-in image thumbnailing for your Rack stack"
59
+ t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
60
+ t.options << '--charset' << 'utf-8'
61
+ t.rdoc_files.include('README.rdoc')
62
+ t.rdoc_files.include('lib/rack/rack-thumb.rb')
63
+ t.rdoc_files.include('lib/rack/rack-thumb/*.rb')
64
+ end
@@ -0,0 +1,7 @@
1
+ require 'rack/thumb'
2
+
3
+ use Rack::ShowExceptions
4
+ use Rack::CommonLogger
5
+ use Rack::Thumb
6
+
7
+ run Rack::File.new(::File.dirname(__FILE__) + '/public')
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'thin'
3
+ require 'sinatra'
4
+ require 'rack/cache'
5
+ require 'rack/thumb'
6
+
7
+ use Rack::Cache,
8
+ :metastore => 'file:/var/cache/rack/meta',
9
+ :entitystore => 'file:/var/cache/rack/body'
10
+ use Rack::Thumb
11
+
12
+ get '/' do
13
+ "Hello World!"
14
+ end
@@ -0,0 +1,10 @@
1
+ require 'rack/thumb'
2
+
3
+ use Rack::ShowExceptions
4
+ use Rack::CommonLogger
5
+ use Rack::Thumb
6
+ use Rack::Static, :urls => ["/images"], :root => "public"
7
+
8
+ app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Hello World!"]] }
9
+
10
+ run app
@@ -0,0 +1,270 @@
1
+ require 'rack'
2
+ require 'mapel'
3
+ require 'digest/sha1'
4
+
5
+ require 'tempfile'
6
+
7
+ Tempfile.class_eval do
8
+ def make_tmpname(basename, n)
9
+ ext = nil
10
+ sprintf("%s%d-%d%s", basename.to_s.gsub(/\.\w+$/) { |s| ext = s; '' }, $$, n, ext)
11
+ end
12
+ end
13
+
14
+ module Rack
15
+
16
+ # The Rack::Thumb middleware intercepts requests for images that have urls of
17
+ # the form <code>/path/to/image_{metadata}.ext</code> and returns rendered
18
+ # thumbnails. Rendering options include +width+, +height+ and +gravity+. If
19
+ # both +width+ and +height+ are supplied, images are cropped and resized
20
+ # to fit the aspect ratio.
21
+ #
22
+ # Rack::Thumb is file-server agnostic to provide maximum deployment
23
+ # flexibility. Simply set it up in front of any downstream application that
24
+ # can serve the source images. Example:
25
+ #
26
+ # # rackup.ru
27
+ # require 'rack/thumb'
28
+ #
29
+ # use Rack::Thumb
30
+ # use Rack::Static, :urls => ["/media"]
31
+ #
32
+ # run MyApp.new
33
+ #
34
+ # See the example directory for more <tt>Rack</tt> configurations. Because
35
+ # thumbnailing is an expensive operation, you should run Rack::Thumb
36
+ # behind a cache, such as <tt>Rack::Cache</tt>.
37
+ #
38
+ # Link to thumbnails from your templates as follows:
39
+ #
40
+ # /media/foobar_50x50.jpg # => Crop and resize to 50x50
41
+ # /media/foobar_50x50-nw.jpg # => Crop and resize with northwest gravity
42
+ # /media/foobar_50x.jpg # => Resize to a width of 50, preserving AR
43
+ # /media/foobar_x50.jpg # => Resize to a height of 50, preserving AR
44
+ #
45
+ # To prevent pesky end-users and bots from flooding your application with
46
+ # render requests you can set up Rack::Thumb to check for a <tt>SHA-1</tt> signature
47
+ # that is unique to every url. Using this option, only thumbnails requested
48
+ # by your templates will be valid. Example:
49
+ #
50
+ # use Rack::Thumb(
51
+ # :secret => "My secret",
52
+ # :key_length => "16" # => Only use 16 digits of the SHA-1 key
53
+ # )
54
+ #
55
+ # You can then use your +secret+ to generate secure links in your templates:
56
+ #
57
+ # /media/foobar_50x100-sw-a267c193a7eff046.jpg # => Successful
58
+ # /media/foobar_120x250-a267c193a7eff046.jpg # => Returns a bad request error
59
+ #
60
+
61
+ class Thumb
62
+ RE_TH_BASE = /_([0-9]+x|x[0-9]+|[0-9]+x[0-9]+)(-(?:nw|n|ne|w|c|e|sw|s|se))?/
63
+ RE_TH_EXT = /(\.(?:jpg|jpeg|png|gif))/i
64
+ TH_GRAV = {
65
+ '-nw' => :northwest,
66
+ '-n' => :north,
67
+ '-ne' => :northeast,
68
+ '-w' => :west,
69
+ '-c' => :center,
70
+ '-e' => :east,
71
+ '-sw' => :southwest,
72
+ '-s' => :south,
73
+ '-se' => :southeast
74
+ }
75
+
76
+ def initialize(app, options={})
77
+ @app = app
78
+ @keylen = options[:keylength]
79
+ @secret = options[:secret]
80
+ @routes = generate_routes(options[:urls] || ["/"], options[:prefix])
81
+ end
82
+
83
+ # Generates routes given a list of prefixes.
84
+ def generate_routes(urls, prefix = nil)
85
+ urls.map do |url|
86
+ prefix = prefix ? Regexp.escape(prefix) : ''
87
+ url = url == "/" ? '' : Regexp.escape(url)
88
+ if @keylen
89
+ /^#{prefix}(#{url}\/.+)#{RE_TH_BASE}-([0-9a-f]{#{@keylen}})#{RE_TH_EXT}$/
90
+ else
91
+ /^#{prefix}(#{url}\/.+)#{RE_TH_BASE}#{RE_TH_EXT}$/
92
+ end
93
+ end
94
+ end
95
+
96
+ def call(env)
97
+ dup._call(env)
98
+ end
99
+
100
+ def _call(env)
101
+ response = catch(:halt) do
102
+ throw :halt unless %w{GET HEAD}.include? env["REQUEST_METHOD"]
103
+ @env = env
104
+ @path = env["PATH_INFO"]
105
+ @routes.each do |regex|
106
+ if match = @path.match(regex)
107
+ @source, dim, grav = extract_meta(match)
108
+ @image = get_source_image
109
+ @thumb = render_thumbnail(dim, grav) unless head?
110
+ serve
111
+ end
112
+ end
113
+ nil
114
+ end
115
+
116
+ response || @app.call(env)
117
+ end
118
+
119
+ # Extracts filename and options from the path.
120
+ def extract_meta(match)
121
+ result = if @keylen
122
+ extract_signed_meta(match)
123
+ else
124
+ extract_unsigned_meta(match)
125
+ end
126
+
127
+ throw :halt unless result
128
+ result
129
+ end
130
+
131
+ # Extracts filename and options from a signed path.
132
+ def extract_signed_meta(match)
133
+ base, dim, grav, sig, ext = match.captures
134
+ digest = Digest::SHA1.hexdigest("#{base}_#{dim}#{grav}#{ext}#{@secret}")[0..@keylen-1]
135
+ throw(:halt, forbidden) unless sig && (sig == digest)
136
+ [base + ext, dim, grav]
137
+ end
138
+
139
+ # Extracts filename and options from an unsigned path.
140
+ def extract_unsigned_meta(match)
141
+ base, dim, grav, ext = match.captures
142
+ [base + ext, dim, grav]
143
+ end
144
+
145
+ # Fetch the source image from the downstream app, returning the downstream
146
+ # app's response if it is not a success.
147
+ def get_source_image
148
+ status, headers, body = @app.call(@env.merge(
149
+ "PATH_INFO" => @source
150
+ ))
151
+
152
+ unless (status >= 200 && status < 300) &&
153
+ (headers["Content-Type"].split("/").first == "image")
154
+ throw :halt, [status, headers, body]
155
+ end
156
+
157
+ @source_headers = headers
158
+
159
+ if !head?
160
+ if body.respond_to?(:path)
161
+ ::File.open(body.path, 'rb')
162
+ elsif body.respond_to?(:each)
163
+ data = ''
164
+ body.each { |part| data << part.to_s }
165
+ Tempfile.new(::File.basename(@path)).tap do |f|
166
+ f.binmode
167
+ f.write(data)
168
+ f.close
169
+ end
170
+ end
171
+ else
172
+ nil
173
+ end
174
+ end
175
+
176
+ # Renders a thumbnail from the source image. Returns a Tempfile.
177
+ def render_thumbnail(dim, grav)
178
+ gravity = grav ? TH_GRAV[grav] : :center
179
+ dimensions = parse_dimensions(dim)
180
+ output = create_tempfile
181
+ cmd = Mapel(@image.path).gravity(gravity)
182
+ width, height = dimensions
183
+ if width && height
184
+ cmd.resize!(width, height)
185
+ else
186
+ cmd.resize(width, height, 0, 0, :>)
187
+ end
188
+ cmd.to(output.path).run
189
+ output
190
+ end
191
+
192
+ # Serves the thumbnail. If this is a HEAD request we strip the body as well
193
+ # as the content length because the render was never run.
194
+ def serve
195
+ lastmod = Time.now.httpdate
196
+ # Use origin content type?
197
+ ctype = Mime.mime_type(::File.extname(@path), 'text/plain')
198
+
199
+ response = if head?
200
+ @source_headers.delete("Content-Length")
201
+ [200, @source_headers.merge(
202
+ "Last-Modified" => lastmod,
203
+ "Content-Type" => ctype
204
+ ), []]
205
+ else
206
+ [200, @source_headers.merge(
207
+ "Last-Modified" => lastmod,
208
+ "Content-Type" => ctype,
209
+ "Content-Length" => ::File.size(@thumb.path).to_s
210
+ ), self]
211
+ end
212
+
213
+ throw :halt, response
214
+ end
215
+
216
+ # Parses the rendering options; returns false if rendering options are invalid
217
+ def parse_dimensions(meta)
218
+ dimensions = meta.split('x').map do |dim|
219
+ if dim.empty?
220
+ nil
221
+ elsif dim[0].to_i == 0
222
+ throw :halt, bad_request
223
+ else
224
+ dim.to_i
225
+ end
226
+ end
227
+ dimensions.any? ? dimensions : throw(:halt, bad_request)
228
+ end
229
+
230
+ def resolve(uri)
231
+ uri = URI.parse(uri) unless uri.respond_to?(:scheme)
232
+ if uri.scheme == "file"
233
+ ::File.expand_path(uri.opaque || uri.path)
234
+ else
235
+ uri.to_s
236
+ end
237
+ end
238
+
239
+ # Creates a new tempfile
240
+ def create_tempfile
241
+ Tempfile.new(::File.basename(@path)).tap { |f| f.close }
242
+ end
243
+
244
+ def bad_request
245
+ body = "Bad thumbnail parameters in #{@path}\n"
246
+ [400, {"Content-Type" => "text/plain",
247
+ "Content-Length" => body.size.to_s},
248
+ [body]]
249
+ end
250
+
251
+ def forbidden
252
+ body = "Bad thumbnail signature in #{@path}\n"
253
+ [403, {"Content-Type" => "text/plain",
254
+ "Content-Length" => body.size.to_s},
255
+ [body]]
256
+ end
257
+
258
+ def head?
259
+ @env["REQUEST_METHOD"] == "HEAD"
260
+ end
261
+
262
+ def each
263
+ ::File.open(@thumb.path, "rb") { |file|
264
+ while part = file.read(8192)
265
+ yield part
266
+ end
267
+ }
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,167 @@
1
+ require File.dirname(__FILE__) + '/helpers'
2
+
3
+ describe "Rack::Thumb Base" do
4
+ before do
5
+ @app = Rack::File.new(::File.dirname(__FILE__))
6
+ end
7
+
8
+ it "should render a thumbnail with width only" do
9
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
10
+
11
+ res = request.get("/media/imagick_50x.jpg")
12
+ res.should.be.ok
13
+ res.content_type.should == "image/jpeg"
14
+ res.content_length.should == 6221
15
+ res.body.bytesize.should == 6221
16
+ end
17
+
18
+ it "should render a thumbnail with height only" do
19
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
20
+
21
+ res = request.get("/media/imagick_x50.jpg")
22
+ res.should.be.ok
23
+ res.content_type.should == "image/jpeg"
24
+ res.content_length.should == 5912
25
+ res.body.bytesize.should == 5912
26
+ end
27
+
28
+ it "should render a thumbnail with width and height (crop-resize)" do
29
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
30
+
31
+ res = request.get("/media/imagick_50x50.jpg")
32
+ res.should.be.ok
33
+ res.content_type.should == "image/jpeg"
34
+ res.content_length.should == 6074
35
+ res.body.bytesize.should == 6074
36
+ end
37
+
38
+ it "should render a thumbnail with width, height and gravity (crop-resize)" do
39
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
40
+
41
+ res = request.get("/media/imagick_50x100-sw.jpg")
42
+ res.should.be.ok
43
+ res.content_type.should == "image/jpeg"
44
+ res.content_length.should == 6696
45
+ res.body.bytesize.should == 6696
46
+ end
47
+
48
+ it "should render a thumbnail with a signature" do
49
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app, :keylength => 16,
50
+ :secret => "test"))
51
+
52
+ sig = Digest::SHA1.hexdigest("/media/imagick_50x100-sw.jpgtest")[0..15]
53
+ res = request.get("/media/imagick_50x100-sw-#{sig}.jpg")
54
+ res.should.be.ok
55
+ res.content_type.should == "image/jpeg"
56
+ res.content_length.should == 6696
57
+ res.body.bytesize.should == 6696
58
+ end
59
+
60
+ it "should not render a thumbnail that exceeds the original image's dimensions" do
61
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
62
+
63
+ res = request.get("/media/imagick_1000x1000.jpg")
64
+ res.should.be.ok
65
+ res.content_type.should == "image/jpeg"
66
+ # There is a miniscule difference between this and the original
67
+ # because this is run through the processor.
68
+ res.content_length.should == 97373
69
+ res.body.bytesize.should == 97373
70
+ end
71
+
72
+ it "should work with non-file source bodies" do
73
+ app = lambda { |env| [200, {"Content-Type" => "image/jpeg"},
74
+ [::File.read(::File.dirname(__FILE__) + "/media/imagick.jpg")]] }
75
+
76
+ request = Rack::MockRequest.new(Rack::Thumb.new(app))
77
+
78
+ res = request.get("/media/imagick_50x.jpg")
79
+ res.should.be.ok
80
+ res.content_type.should == "image/jpeg"
81
+ res.content_length.should == 6221
82
+ res.body.bytesize.should == 6221
83
+ end
84
+
85
+ it "should return forbidden if the signature is invalid" do
86
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app, :keylength => 16,
87
+ :secret => "test"))
88
+
89
+ res = request.get("/media/imagick_50x100-sw-9922d04b14049f85.jpg")
90
+ res.should.be.forbidden
91
+ res.body.should == "Bad thumbnail signature in /media/imagick_50x100-sw-9922d04b14049f85.jpg\n"
92
+ end
93
+
94
+ it "should return bad request if the dimensions are bad" do
95
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
96
+
97
+ res = request.get("/media/imagick_0x50.jpg")
98
+ res.should.be.client_error
99
+ res.body.should == "Bad thumbnail parameters in /media/imagick_0x50.jpg\n"
100
+ end
101
+
102
+ it "should return bad request if dimensions contain leading zeroes" do
103
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
104
+
105
+ res = request.get("/media/imagick_50x050.jpg")
106
+ res.should.be.client_error
107
+ res.body.should == "Bad thumbnail parameters in /media/imagick_50x050.jpg\n"
108
+ end
109
+
110
+ it "should return the application's response if the source file is not found" do
111
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
112
+
113
+ res = request.get("/media/dummy_50x50.jpg")
114
+ res.should.be.not_found
115
+ res.body.should == "File not found: /media/dummy.jpg\n"
116
+ end
117
+
118
+ it "should return the application's response if it does not recognize render options" do
119
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app, :keylength => 16,
120
+ :secret => "test"))
121
+
122
+ res = request.get("/media/imagick_50x50!.jpg")
123
+ res.should.be.not_found
124
+ res.body.should == "File not found: /media/imagick_50x50!.jpg\n"
125
+ end
126
+
127
+ it "should pass non-thumbnail image requests to the application" do
128
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app, :keylength => 16,
129
+ :secret => "test"))
130
+
131
+ res = request.get("/media/imagick.jpg")
132
+ res.should.be.ok
133
+ res.content_type.should == "image/jpeg"
134
+ res.content_length.should == 97374
135
+ res.body.bytesize.should == 97374
136
+ end
137
+
138
+ it "should not render on a HEAD request" do
139
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
140
+
141
+ res = request.request("HEAD", "/media/imagick_50x50.jpg")
142
+ res.should.be.ok
143
+ res.content_type.should == "image/jpeg"
144
+ res.content_length.should.be.nil
145
+ res.body.bytesize.should == 0
146
+ end
147
+
148
+ it "should preserve any extra headers provided by the downstream app" do
149
+ app = lambda { |env| [200, {"X-Foo" => "bar", "Content-Type" => "image/jpeg"},
150
+ ::File.open(::File.dirname(__FILE__) + "/media/imagick.jpg")] }
151
+
152
+ request = Rack::MockRequest.new(Rack::Thumb.new(app))
153
+
154
+ res = request.request("HEAD", "/media/imagick_50x50.jpg")
155
+ res.should.be.ok
156
+ res.content_type.should == "image/jpeg"
157
+ res.content_length.should.be.nil
158
+ res.headers["X-Foo"].should == "bar"
159
+ end
160
+
161
+ it "should forward POST/PUT/DELETE requests to the downstream app" do
162
+ request = Rack::MockRequest.new(Rack::Thumb.new(@app))
163
+
164
+ res = request.post("/media/imagick_50x50.jpg")
165
+ res.should.not.be.successful
166
+ end
167
+ end
@@ -0,0 +1,9 @@
1
+ require 'bacon'
2
+ require File.dirname(File.dirname(__FILE__)) + '/lib/rack/thumb'
3
+ require 'rack/mock'
4
+
5
+ class String
6
+ def each(*args, &block)
7
+ each_line(*args, &block)
8
+ end
9
+ end
Binary file
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: akdubya-rack-thumb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Aleksander Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-11 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: akdubya-mapel
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.1
24
+ version:
25
+ description: Drop-in image thumbnailing middleware for your Rack stack (Merb, Sinatra, Rails, etc).
26
+ email: alekswilliams@earthlink.net
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - Rakefile
35
+ - README.rdoc
36
+ - lib/rack
37
+ - lib/rack/thumb.rb
38
+ - spec/media
39
+ - spec/media/imagick.jpg
40
+ - spec/base_spec.rb
41
+ - spec/helpers.rb
42
+ - example/frank.rb
43
+ - example/static.ru
44
+ - example/public
45
+ - example/public/images
46
+ - example/public/images/imagick.jpg
47
+ - example/file.ru
48
+ has_rdoc: true
49
+ homepage: http://github.com/akdubya/rack-thumb
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.2.0
71
+ signing_key:
72
+ specification_version: 2
73
+ summary: Drop-in image thumbnailing for your Rack stack.
74
+ test_files: []
75
+