scale_down 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.bundle/config ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_DISABLE_SHARED_GEMS: "1"
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ scale_down (0.0.1)
5
+ rake (>= 0.8.7)
6
+ rmagick (>= 2.1)
7
+ rmagick (>= 2.1)
8
+ ruby-hmac (>= 0.4.0)
9
+ sinatra (>= 1.0)
10
+
11
+ GEM
12
+ remote: http://rubygems.org/
13
+ specs:
14
+ contest (0.1.2)
15
+ mocha (0.9.8)
16
+ rake
17
+ rack (1.2.1)
18
+ rack-test (0.5.6)
19
+ rack (>= 1.0)
20
+ rake (0.8.7)
21
+ rmagick (2.13.1)
22
+ ruby-hmac (0.4.0)
23
+ sinatra (1.0)
24
+ rack (>= 1.0)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ contest (>= 0.1.2)
31
+ mocha (= 0.9.8)
32
+ rack-test (= 0.5.6)
33
+ rake (>= 0.8.7)
34
+ rmagick (>= 2.1)
35
+ ruby-hmac (>= 0.4.0)
36
+ scale_down!
37
+ sinatra (>= 1.0)
data/README.rdoc ADDED
@@ -0,0 +1,73 @@
1
+ = ScaleDown
2
+
3
+ A RESTful server for quickly scaling and serving images. Nothing more.
4
+
5
+ Supports cropping images and converts CMYK to RGB.
6
+
7
+ Images are scaled based upon their URL. An HMAC signature is used to prevent malicious scaling of images (ie: bring your server down.)
8
+
9
+ For example
10
+
11
+ [ image server address ][ image source ][ size ][ hmac ]
12
+ http://images.example.com/images/logo.png/400x300/A3SDACEDF
13
+
14
+ would redirect to (via 301) the scaled image
15
+
16
+ http://images.example.com/images/scaled/logo-400x300.png
17
+
18
+ To crop an image include the 'cropped' option
19
+
20
+ http://images.example.com/images/logo.png/400x300-cropped/A3SDACEDF
21
+
22
+ Use 'auto' to have an image scale to one dimension or another.
23
+
24
+ For example, to ensure an image is 300 pixels wide
25
+
26
+ http://images.example.com/images/logo.png/300xauto/A3SDACEDF
27
+
28
+
29
+ == Configuration
30
+
31
+ ScaleDown::Scaler.hmac_key = "secret"
32
+ ScaleDown::Scaler.hmac_method = HMAC::MD5
33
+ ScaleDown::Scaler.hmac_length = 8
34
+ ScaleDown::Scaler.root_path = "/tmp/scale_down"
35
+
36
+ == Example URL generator
37
+
38
+ require 'HMAC::MD5'
39
+
40
+ # ie scaled_image_src('/images/picture.png/400x300-cropped')
41
+ def scaled_image_src(url)
42
+ hmac = HMAC::MD5.new("secret").update(url).to_s[0...8]
43
+ "http://images.myserver.com#{url}/#{hmac}"
44
+ end
45
+
46
+ == TODO
47
+
48
+ This thing is brand spankin new and is in flux - expect changes.
49
+
50
+ Define a max image size (do not scale images over this size).
51
+ Support DELETE requests to remove scaled images.
52
+ Custom Read/Write methods. Allow for more than just local storage.
53
+
54
+ == Dependencies
55
+
56
+ Sinatra
57
+ RMagick
58
+ Ruby-HMAC
59
+
60
+ == License
61
+
62
+ LICENSE:
63
+
64
+ (The MIT License)
65
+
66
+ Copyright © 2010 John Weir & Fame Driver LLC
67
+
68
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
69
+
70
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
71
+
72
+ THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
73
+
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+
7
+ desc 'Default: run unit tests.'
8
+ task :default => :test
9
+
10
+ desc 'Test'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = true
15
+ end
Binary file
data/config_sample.ru ADDED
@@ -0,0 +1,8 @@
1
+ require 'scale_down'
2
+
3
+ ScaleDown::Scaler.hmac_key = "secret"
4
+ ScaleDown::Scaler.hmac_method = HMAC::MD5
5
+ ScaleDown::Scaler.hmac_length = 8
6
+ ScaleDown::Scaler.root_path = File.expand_path(File.dirname(__FILE__))+"/public"
7
+
8
+ run ScaleDown::Controller
data/gen_tags ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ ctags -R . /Users/johnweir/.rvm/gems/ruby-1.8.7-p302\@famedriver-images/gems/
@@ -0,0 +1,29 @@
1
+ class ScaleDown::Controller < Sinatra::Application
2
+
3
+ set :raise_errors, true
4
+ set :show_exceptions, false
5
+ set :static, true
6
+ set :public, "/Users/johnweir/Sites/famedriver/site/public"
7
+
8
+ get '/' do
9
+ "<b>ScaleDown version #{ScaleDown::VERSION}<b/>"
10
+ end
11
+
12
+ get '/*/:filename/:geometry/:hmac' do
13
+ path, status = scaler(params)
14
+ unless status == 403
15
+ redirect path, status
16
+ else
17
+ [status, "Error: this image could not be processed"]
18
+ end
19
+ end
20
+
21
+ protected
22
+ def scaler(params)
23
+ ScaleDown::Scaler.process \
24
+ :path => params[:splat].join("/"),
25
+ :filename => params[:filename],
26
+ :geometry => params[:geometry],
27
+ :hmac => params[:hmac]
28
+ end
29
+ end
@@ -0,0 +1,72 @@
1
+ require 'pathname'
2
+
3
+ class ScaleDown::Image
4
+ include Magick
5
+
6
+ class << self
7
+ def scale(properties)
8
+ new(properties).valid?
9
+ end
10
+
11
+ def geometry(properties)
12
+ Magick::Geometry.new(properties[:width], properties[:height], nil, nil, ">")
13
+ end
14
+ end
15
+
16
+ def initialize(properties = {})
17
+ @file = load_file(properties[:file])
18
+ @out = properties[:out]
19
+ @geometry = geometry(properties[:options])
20
+ @options = properties[:options]
21
+ @wrote = false
22
+
23
+ save if @file
24
+ end
25
+
26
+ def load_file(filepath)
27
+ begin
28
+ Magick::Image.read(filepath).first
29
+ rescue Magick::ImageMagickError => e
30
+ return nil
31
+ end
32
+ end
33
+
34
+ def geometry(properties)
35
+ self.class.geometry properties
36
+ end
37
+
38
+ def valid?
39
+ @wrote
40
+ end
41
+
42
+ protected
43
+
44
+ def save
45
+ path = Pathname.new(@out).dirname.to_s
46
+ FileUtils.mkdir_p path unless FileTest.directory? path
47
+ resize
48
+ fix_color_space
49
+ write
50
+ end
51
+
52
+ def fix_color_space
53
+ if @file.colorspace == Magick::CMYKColorspace
54
+ @file.add_profile "#{File.expand_path(File.dirname(__FILE__))}/../../color_profiles/sRGB.icm"
55
+ @file = @file.quantize 2**24, Magick::RGBColorspace
56
+ end
57
+ end
58
+
59
+ def resize
60
+ @file.auto_orient!
61
+ if @options[:crop]
62
+ @file.crop_resized!(@geometry.width, @geometry.height, Magick::CenterGravity)
63
+ else
64
+ @file.change_geometry!(@geometry) {|cols, rows, img| img.resize!(cols,rows)}
65
+ end
66
+ end
67
+
68
+ def write
69
+ @file.write(@out) { self.quality = 85 }
70
+ @wrote = true
71
+ end
72
+ end
@@ -0,0 +1,89 @@
1
+ require 'RMagick'
2
+ require 'hmac-md5'
3
+ require 'hmac-sha1'
4
+
5
+ class ScaleDown::Scaler
6
+
7
+ class << self
8
+ attr_accessor :hmac_method, :hmac_key, :hmac_length
9
+ attr_accessor :root_path
10
+
11
+ def process(params)
12
+ scaler = new(params)
13
+
14
+ return ["Missing file", 404] unless scaler.root_file_exists?
15
+ return [scaler.redirect_path, 301] if scaler.scaled_file_exists?
16
+
17
+ if scaler.valid_hmac? && scaler.scale
18
+ [scaler.redirect_path, 301]
19
+ else
20
+ ["Error message", 403]
21
+ end
22
+ end
23
+
24
+ def valid_hmac?(params)
25
+ str = [params[:path], "/", params[:filename], "/", params[:geometry]].join
26
+ hmac(str) == params[:hmac]
27
+ end
28
+
29
+ def hmac(string)
30
+ hmac_method.new(hmac_key).update(string).to_s[0...hmac_length]
31
+ end
32
+ end
33
+
34
+ def initialize(params)
35
+ @params = params
36
+ end
37
+
38
+ def image_options
39
+ dimensions, *options = @params[:geometry].split("-")
40
+ width, height = dimensions.split("x")
41
+ {:height => height.to_i, :width => width.to_i}.tap do |o|
42
+ options.each {|k| o[k.to_sym] = true}
43
+ end
44
+ end
45
+
46
+ def scale
47
+ ScaleDown::Image.scale \
48
+ :file => root_path,
49
+ :out => scaled_file_path,
50
+ :options => image_options
51
+ end
52
+
53
+ def valid_hmac?
54
+ self.class.valid_hmac?(@params)
55
+ end
56
+
57
+ def redirect_path
58
+ ["/"+@params[:path], "scaled", scaled_filename].join("/")
59
+ end
60
+
61
+ def root_file_exists?
62
+ File.exists? root_path
63
+ end
64
+
65
+ def scaled_file_exists?
66
+ File.exists? scaled_file_path
67
+ end
68
+
69
+ def root_path
70
+ File.join(self.class.root_path, @params[:path], @params[:filename])
71
+ end
72
+
73
+ def scaled_file_path
74
+ File.join(self.class.root_path, redirect_path)
75
+ end
76
+
77
+ def scaled_filename
78
+ "#{filename}-#{@params[:geometry]}.#{scaled_extension}"
79
+ end
80
+
81
+ def filename
82
+ @params[:filename].split(".")[0...-1].join(".")
83
+ end
84
+
85
+ def scaled_extension
86
+ ext = @params[:filename].split(".").last
87
+ ext == "png" ? ext : "jpg"
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module ScaleDown
2
+ VERSION = "0.0.2"
3
+ end
data/lib/scale_down.rb ADDED
@@ -0,0 +1,12 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
4
+ require 'rubygems'
5
+ require 'sinatra'
6
+
7
+ module ScaleDown
8
+ require 'scale_down/version'
9
+ require 'scale_down/controller'
10
+ require 'scale_down/scaler'
11
+ require 'scale_down/image'
12
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "scale_down/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "scale_down"
7
+ s.version = ScaleDown::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["John Weir"]
10
+ s.email = ["john@famedriver.com"]
11
+ s.homepage = "http://github.com/jweir/ScaleDown"
12
+ s.summary = %q{A Sinatra based server for quickly scaling and serving images. Nothing more.}
13
+ s.description = %q{}
14
+
15
+ s.rubyforge_project = "scale_down"
16
+
17
+ s.add_dependency "rmagick", ">= 2.1"
18
+ s.add_dependency "rake", ">= 0.8.7"
19
+ s.add_dependency "sinatra", ">= 1.0"
20
+ s.add_dependency "rmagick", ">= 2.1"
21
+ s.add_dependency "ruby-hmac", ">= 0.4.0"
22
+
23
+ s.add_development_dependency "contest", ">= 0.1.2"
24
+ s.add_development_dependency "mocha", "0.9.8"
25
+ s.add_development_dependency "rack-test", "0.5.6"
26
+
27
+ s.files = `git ls-files`.split("\n")
28
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
29
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
30
+ s.require_paths = ["lib"]
31
+ end
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,45 @@
1
+ require File.expand_path(File.dirname(__FILE__))+'/../test_helper'
2
+
3
+ class ScaleDown::Controller::Test < Test::Unit::TestCase
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ ScaleDown::Controller
8
+ end
9
+
10
+ context "parsing a request" do
11
+
12
+ should "have an image path" do
13
+ ScaleDown::Scaler.expects(:process).with(
14
+ :path => "user/path",
15
+ :filename => "filename.png",
16
+ :geometry => "400x300-cropped-grayscale",
17
+ :hmac => "HMAC").
18
+ returns ["path","status"]
19
+
20
+ get '/user/path/filename.png/400x300-cropped-grayscale/HMAC'
21
+ end
22
+ end
23
+
24
+ context "a valid request" do
25
+
26
+ should "redirect to the image path" do
27
+ ScaleDown::Scaler.expects(:process).returns ["/image-path", 301]
28
+ get "/path/filename/geo/hmac"
29
+
30
+ assert_equal 301, last_response.status
31
+ assert_equal "/image-path", last_response["Location"]
32
+ end
33
+ end
34
+
35
+ context "an invalid request" do
36
+ should "respond with a 403 and error message" do
37
+ ScaleDown::Scaler.expects(:process).returns ["Error description", 403]
38
+
39
+ get "/path/filename/geo/hmac"
40
+
41
+ assert_equal 403, last_response.status
42
+ assert_match "Error", last_response.body
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,119 @@
1
+ require File.expand_path(File.dirname(__FILE__))+'/../test_helper'
2
+
3
+ class ScaleDown::Image::Test < Test::Unit::TestCase
4
+
5
+ def create(file_path, out_path, options)
6
+ ScaleDown::Image.scale \
7
+ :file => file_path,
8
+ :out => out_path,
9
+ :options => options
10
+ end
11
+
12
+ teardown do
13
+ FileUtils.rm_r(tests_path("scaled_test")) if File.directory?(tests_path('scaled_test'))
14
+ end
15
+
16
+ context "scaling a valid image" do
17
+ setup do
18
+ @subject = create \
19
+ tests_path("files/graphic.png"),
20
+ tests_path("scaled_test/graphic_scaled.png"),
21
+ { :width => 100, :height => 180}
22
+ end
23
+
24
+ should "save the file (and generate the path)" do
25
+ assert File.exists?(tests_path('scaled_test/graphic_scaled.png'))
26
+ end
27
+
28
+ should "scale the image" do
29
+ image = Magick::Image.read(tests_path("scaled_test/graphic_scaled.png")).first
30
+ assert_equal 90, image.columns
31
+ assert_equal 180, image.rows
32
+ end
33
+
34
+ should "be true" do
35
+ assert @subject
36
+ end
37
+ end
38
+
39
+ context "#geometry" do
40
+ should "convert 'auto' to nil" do
41
+ geo = ScaleDown::Image.geometry(:width => "auto", :height => "400")
42
+ assert_equal 0.0, geo.width
43
+ assert_equal 400, geo.height
44
+ end
45
+
46
+ should "auto scale" do
47
+ assert create \
48
+ tests_path("files/graphic.png"),
49
+ tests_path("scaled_test/graphic_scaled.png"),
50
+ { :width => "auto", :height => 150 }
51
+
52
+ image = Magick::Image.read(tests_path("scaled_test/graphic_scaled.png")).first
53
+ assert_equal 150, image.rows
54
+ assert_equal 75, image.columns
55
+ end
56
+ end
57
+
58
+ context "an invalid file" do
59
+ setup do
60
+ @subject = create \
61
+ tests_path("files/invalid_jpeg.jpg"),
62
+ tests_path("scaled_test/graphic_scaled.jpg"),
63
+ { :width => 100, :height => 105 }
64
+ end
65
+
66
+ should "return nil" do
67
+ assert !@subject
68
+ end
69
+
70
+ should "not create a scaled image" do
71
+ assert !File.exists?(tests_path("scaled_test/graphic_scaled.jpg"))
72
+ end
73
+ end
74
+
75
+ context "cropping" do
76
+ setup do
77
+ @subject = create \
78
+ tests_path("files/graphic.png"),
79
+ tests_path("scaled_test/graphic_scaled.png"),
80
+ { :width => 25, :height => 25, :crop => true }
81
+ end
82
+
83
+ should "crop the image to the dimensions" do
84
+ image = Magick::Image.read(tests_path("scaled_test/graphic_scaled.png")).first
85
+ assert_equal 25, image.columns
86
+ assert_equal 25, image.rows
87
+ end
88
+ end
89
+
90
+ context "orientation" do
91
+ setup do
92
+ @subject = create \
93
+ tests_path("files/orient.jpg"),
94
+ tests_path("scaled_test/graphic_scaled.jpg"),
95
+ { :width => "auto", :height => 800}
96
+ end
97
+
98
+ should "be automatic" do
99
+ image = Magick::Image.read(tests_path("scaled_test/graphic_scaled.jpg")).first
100
+ assert_equal 600, image.columns
101
+ assert_equal 800, image.rows
102
+ end
103
+ end
104
+
105
+ context "color correction" do
106
+ setup do
107
+ @subject = create \
108
+ tests_path("files/cmyk.tif"),
109
+ tests_path("scaled_test/graphic_scaled.jpg"),
110
+ { :width => "auto", :height => 200}
111
+ end
112
+
113
+ should "be automatic" do
114
+ image = Magick::Image.read(tests_path("scaled_test/graphic_scaled.jpg")).first
115
+ assert_equal Magick::RGBColorspace, image.colorspace
116
+ end
117
+ end
118
+
119
+ end
@@ -0,0 +1,135 @@
1
+ require File.expand_path(File.dirname(__FILE__))+'/../test_helper'
2
+
3
+ class ScaleDown::Scaler::Test < Test::Unit::TestCase
4
+
5
+ context "Scaler::Test" do
6
+ setup do
7
+ ScaleDown::Scaler.hmac_key = "secret"
8
+ ScaleDown::Scaler.hmac_method = HMAC::MD5
9
+ ScaleDown::Scaler.hmac_length = 8
10
+ ScaleDown::Scaler.root_path = "/tmp"
11
+
12
+ hmac = HMAC::MD5.new("secret").update("file/path/filename.png/400x300-crop").to_s
13
+
14
+ @params = {
15
+ :path => "file/path",
16
+ :filename => "filename.png",
17
+ :geometry => "400x300-crop",
18
+ :hmac => hmac[0...8]
19
+ }
20
+ end
21
+
22
+ context "HMAC" do
23
+ should "validate when the params match the HMAC signature" do
24
+ assert ScaleDown::Scaler.valid_hmac?(@params)
25
+ end
26
+
27
+ should "not validate when the params do not match the HMAC signature" do
28
+ assert !ScaleDown::Scaler.valid_hmac?(@params.merge(:path => "file/different"))
29
+ end
30
+ end
31
+
32
+ context "instance" do
33
+ setup do
34
+ @scaler = ScaleDown::Scaler.new @params
35
+ end
36
+
37
+ should "validate the HMAC" do
38
+ ScaleDown::Scaler.expects(:valid_hmac?).with(@params).returns true
39
+ assert @scaler.valid_hmac?
40
+ end
41
+
42
+ should "determine root file existance" do
43
+ File.expects(:exists?).with("/tmp/file/path/filename.png").returns true
44
+ assert @scaler.root_file_exists?
45
+ end
46
+
47
+ should "deterimine the scaled image's existance" do
48
+ File.expects(:exists?).with("/tmp/file/path/scaled/filename-400x300-crop.png").returns true
49
+ assert @scaler.scaled_file_exists?
50
+ end
51
+
52
+ should "parse the geometry params into options" do
53
+ assert_equal({:width => 400, :height => 300, :crop => true}, @scaler.image_options)
54
+ end
55
+
56
+ should "have a redirect path" do
57
+ assert_equal "/file/path/scaled/filename-400x300-crop.png", @scaler.redirect_path
58
+ end
59
+
60
+ should "process the image" do
61
+ ScaleDown::Image.expects(:scale).with(
62
+ :file => @scaler.root_path,
63
+ :out => @scaler.scaled_file_path,
64
+ :options => @scaler.image_options).returns true
65
+
66
+ assert @scaler.scale
67
+ end
68
+
69
+ should "default to a jpg out file" do
70
+ ["jpg", "tga", "tif", "pdf", "psd"].each do |ext|
71
+ scaler = ScaleDown::Scaler.new @params.merge(:filename => "test.#{ext}")
72
+ assert_match /\.jpg$/, scaler.scaled_file_path
73
+ end
74
+ end
75
+
76
+ should "use a png for png graphics" do
77
+ scaler = ScaleDown::Scaler.new @params.merge(:filename => "test.png")
78
+ assert_match /\.png$/, scaler.scaled_file_path
79
+ end
80
+ end
81
+
82
+ context "process response" do
83
+
84
+ context "for an existing, unscaled image" do
85
+ setup do
86
+ File.expects(:exists?).with("/tmp/file/path/filename.png").returns true
87
+ File.expects(:exists?).with("/tmp/file/path/scaled/filename-400x300-crop.png").returns false
88
+ end
89
+
90
+ context "with a valid HMAC" do
91
+ setup do
92
+ ScaleDown::Scaler.expects(:valid_hmac?).returns true
93
+ ScaleDown::Image.expects(:scale).returns true
94
+ end
95
+
96
+ should "scale the image" do
97
+ ScaleDown::Scaler.process(@params)
98
+ end
99
+
100
+ should "return a 301 redirect to the processed image's URL" do
101
+ assert_equal ["/file/path/scaled/filename-400x300-crop.png", 301], ScaleDown::Scaler.process(@params)
102
+ end
103
+ end
104
+
105
+ context "without a valid HMAC" do
106
+ should "return a 403 Forbidden response" do
107
+ ScaleDown::Scaler.expects(:valid_hmac?).returns false
108
+ assert_equal 403, ScaleDown::Scaler.process(@params)[1]
109
+ end
110
+ end
111
+ end
112
+
113
+ context "for an existing, scaled, image" do
114
+ setup do
115
+ File.expects(:exists?).with("/tmp/file/path/filename.png").returns true
116
+ File.expects(:exists?).with("/tmp/file/path/scaled/filename-400x300-crop.png").returns true
117
+ end
118
+
119
+ should "return a 301 redirect to the processed image's URL" do
120
+ assert_equal ["/file/path/scaled/filename-400x300-crop.png", 301], ScaleDown::Scaler.process(@params)
121
+ end
122
+ end
123
+
124
+ context "for a missing image" do
125
+ setup do
126
+ File.expects(:exists?).with("/tmp/file/path/filename.png").returns false
127
+ end
128
+
129
+ should "return a 404" do
130
+ assert_equal 404, ScaleDown::Scaler.process(@params)[1]
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,50 @@
1
+ require File.expand_path(File.dirname(__FILE__))+'/test_helper'
2
+
3
+ class ScaleDown::Test < Test::Unit::TestCase
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ ScaleDown::Controller
8
+ end
9
+
10
+ def valid_get(path)
11
+ get "/#{path}/#{hmac(path, "secret")[0...8]}"
12
+ end
13
+
14
+ context "integration test" do
15
+ setup do
16
+ ScaleDown::Scaler.hmac_key = "secret"
17
+ ScaleDown::Scaler.hmac_method = HMAC::MD5
18
+ ScaleDown::Scaler.hmac_length = 8
19
+ ScaleDown::Scaler.root_path = "/tmp/scale_down"
20
+
21
+ FileUtils.mkdir_p("/tmp/scale_down/test_images/example_1")
22
+ FileUtils.cp tests_path("files/graphic.png"), "/tmp/scale_down/test_images/example_1/graphic.png"
23
+ FileUtils.mkdir_p("/tmp/scale_down/test_images/example_2")
24
+ FileUtils.cp tests_path("files/invalid_jpeg.jpg"), "/tmp/scale_down/test_images/example_2/invalid_jpeg.jpg"
25
+ end
26
+
27
+ teardown do
28
+ FileUtils.rm_r("/tmp/scale_down")
29
+ end
30
+
31
+ should "get an image and scale it" do
32
+ valid_get 'test_images/example_1/graphic.png/400x300-cropped'
33
+ assert_equal 301, last_response.status
34
+ assert_equal "/test_images/example_1/scaled/graphic-400x300-cropped.png", last_response["Location"]
35
+ assert File.exists?("/tmp/scale_down/test_images/example_1/scaled/graphic-400x300-cropped.png")
36
+ end
37
+
38
+ should "get a nonexistant image and return a 404" do
39
+ valid_get "test_exmaples/example_none/image.jpg"
40
+ assert_equal 404, last_response.status
41
+ end
42
+
43
+ should "get an invalid image and return a 403" do
44
+ valid_get 'test_images/example_2/invalid_jpeg.jpg/400x300-cropped'
45
+
46
+ assert_equal 403, last_response.status
47
+ assert !File.exists?("/tmp/scale_down/test_images/example_2/scaled/invalid_jpeg-400x300-cropped.jpg")
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.dirname(__FILE__))+'/test_helper'
2
+
3
+ class ScaleDown::Test < Test::Unit::TestCase
4
+
5
+ def scaled_image_src(url)
6
+ hmac = HMAC::MD5.new("secret").update(url).to_s[0...8]
7
+ "http://images.myserver.com#{url}/#{hmac}"
8
+ end
9
+
10
+ setup do
11
+ ScaleDown::Scaler.hmac_key = "secret"
12
+ ScaleDown::Scaler.hmac_method = HMAC::MD5
13
+ ScaleDown::Scaler.hmac_length = 8
14
+ ScaleDown::Scaler.root_path = "/tmp"
15
+ end
16
+
17
+ should "create a URL with the HMAC signature" do
18
+ hmac = ScaleDown::Scaler.hmac("/images/graphic.png/400x300-cropped")
19
+ assert_equal "http://images.myserver.com/images/graphic.png/400x300-cropped/#{hmac}", scaled_image_src("/images/graphic.png/400x300-cropped")
20
+ end
21
+ end
22
+
23
+
@@ -0,0 +1,21 @@
1
+ require 'lib/scale_down'
2
+ require 'rack/test'
3
+ require 'contest'
4
+ require 'mocha'
5
+ require 'ruby-debug'
6
+
7
+ ENV['RACK_ENV'] = 'test'
8
+
9
+ require 'forwardable'
10
+
11
+ class Test::Unit::TestCase
12
+
13
+ def hmac(path, secret)
14
+ hmac = HMAC::MD5.new(secret).update(path).to_s
15
+ end
16
+
17
+ def tests_path(append)
18
+ File.join(File.expand_path(File.dirname(__FILE__)), append)
19
+ end
20
+
21
+ end
metadata ADDED
@@ -0,0 +1,225 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scale_down
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - John Weir
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-19 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rmagick
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 1
30
+ segments:
31
+ - 2
32
+ - 1
33
+ version: "2.1"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 49
45
+ segments:
46
+ - 0
47
+ - 8
48
+ - 7
49
+ version: 0.8.7
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: sinatra
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 15
61
+ segments:
62
+ - 1
63
+ - 0
64
+ version: "1.0"
65
+ type: :runtime
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: rmagick
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 1
76
+ segments:
77
+ - 2
78
+ - 1
79
+ version: "2.1"
80
+ type: :runtime
81
+ version_requirements: *id004
82
+ - !ruby/object:Gem::Dependency
83
+ name: ruby-hmac
84
+ prerelease: false
85
+ requirement: &id005 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 15
91
+ segments:
92
+ - 0
93
+ - 4
94
+ - 0
95
+ version: 0.4.0
96
+ type: :runtime
97
+ version_requirements: *id005
98
+ - !ruby/object:Gem::Dependency
99
+ name: contest
100
+ prerelease: false
101
+ requirement: &id006 !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 31
107
+ segments:
108
+ - 0
109
+ - 1
110
+ - 2
111
+ version: 0.1.2
112
+ type: :development
113
+ version_requirements: *id006
114
+ - !ruby/object:Gem::Dependency
115
+ name: mocha
116
+ prerelease: false
117
+ requirement: &id007 !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - "="
121
+ - !ruby/object:Gem::Version
122
+ hash: 43
123
+ segments:
124
+ - 0
125
+ - 9
126
+ - 8
127
+ version: 0.9.8
128
+ type: :development
129
+ version_requirements: *id007
130
+ - !ruby/object:Gem::Dependency
131
+ name: rack-test
132
+ prerelease: false
133
+ requirement: &id008 !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - "="
137
+ - !ruby/object:Gem::Version
138
+ hash: 7
139
+ segments:
140
+ - 0
141
+ - 5
142
+ - 6
143
+ version: 0.5.6
144
+ type: :development
145
+ version_requirements: *id008
146
+ description: ""
147
+ email:
148
+ - john@famedriver.com
149
+ executables: []
150
+
151
+ extensions: []
152
+
153
+ extra_rdoc_files: []
154
+
155
+ files:
156
+ - .bundle/config
157
+ - .gitignore
158
+ - Gemfile
159
+ - Gemfile.lock
160
+ - README.rdoc
161
+ - Rakefile
162
+ - color_profiles/sRGB.icm
163
+ - config_sample.ru
164
+ - gen_tags
165
+ - lib/scale_down.rb
166
+ - lib/scale_down/controller.rb
167
+ - lib/scale_down/image.rb
168
+ - lib/scale_down/scaler.rb
169
+ - lib/scale_down/version.rb
170
+ - scale_down.gemspec
171
+ - test/files/cmyk.tif
172
+ - test/files/graphic.png
173
+ - test/files/invalid_jpeg.jpg
174
+ - test/files/orient.jpg
175
+ - test/scale_down/controller_test.rb
176
+ - test/scale_down/image_test.rb
177
+ - test/scale_down/scaler_test.rb
178
+ - test/scale_down_test.rb
179
+ - test/scaled_image_tag_test.rb
180
+ - test/test_helper.rb
181
+ has_rdoc: true
182
+ homepage: http://github.com/jweir/ScaleDown
183
+ licenses: []
184
+
185
+ post_install_message:
186
+ rdoc_options: []
187
+
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ none: false
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ hash: 3
196
+ segments:
197
+ - 0
198
+ version: "0"
199
+ required_rubygems_version: !ruby/object:Gem::Requirement
200
+ none: false
201
+ requirements:
202
+ - - ">="
203
+ - !ruby/object:Gem::Version
204
+ hash: 3
205
+ segments:
206
+ - 0
207
+ version: "0"
208
+ requirements: []
209
+
210
+ rubyforge_project: scale_down
211
+ rubygems_version: 1.3.7
212
+ signing_key:
213
+ specification_version: 3
214
+ summary: A Sinatra based server for quickly scaling and serving images. Nothing more.
215
+ test_files:
216
+ - test/files/cmyk.tif
217
+ - test/files/graphic.png
218
+ - test/files/invalid_jpeg.jpg
219
+ - test/files/orient.jpg
220
+ - test/scale_down/controller_test.rb
221
+ - test/scale_down/image_test.rb
222
+ - test/scale_down/scaler_test.rb
223
+ - test/scale_down_test.rb
224
+ - test/scaled_image_tag_test.rb
225
+ - test/test_helper.rb