mugshot 0.2.0 → 0.3.0
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/README.md +1 -1
- data/features/crop_image.feature +8 -0
- data/features/step_definitions/all_steps.rb +12 -2
- data/features/support/env.rb +6 -2
- data/features/support/files/test.crop.300x200.jpg +0 -0
- data/lib/mugshot.rb +2 -1
- data/lib/mugshot/application.rb +27 -8
- data/lib/mugshot/fs_storage.rb +5 -4
- data/lib/mugshot/image.rb +15 -4
- data/lib/mugshot/proxy.rb +32 -0
- data/lib/mugshot/storage.rb +1 -2
- data/spec/mugshot/application_spec.rb +65 -17
- data/spec/mugshot/fs_storage_spec.rb +6 -5
- data/spec/mugshot/image_spec.rb +10 -4
- data/spec/mugshot/proxy_spec.rb +67 -0
- data/spec/spec_helper.rb +1 -0
- metadata +6 -2
data/README.md
CHANGED
@@ -1,15 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
When /^I upload an image$/ do
|
2
3
|
post '/', "file" => Rack::Test::UploadedFile.new("features/support/files/test.jpg", "image/jpeg")
|
3
4
|
@image_id = last_response.body
|
4
5
|
end
|
5
6
|
|
6
7
|
When /^I ask for the (.*) resized image$/ do |size|
|
7
|
-
get "/#{size}/#{@image_id}.jpg"
|
8
|
+
get "/resize/#{size}/#{@image_id}.jpg"
|
8
9
|
@retrieved_image = last_response.body
|
9
10
|
end
|
10
11
|
|
11
12
|
When /^I ask for a (.*) resized image that doesn't exist$/ do |size|
|
12
|
-
get "/#{size}/nonexistant.jpg"
|
13
|
+
get "/resize/#{size}/nonexistant.jpg"
|
14
|
+
end
|
15
|
+
|
16
|
+
When /^I ask for the (.*) cropped image$/ do |size|
|
17
|
+
get "/crop/#{size}/#{@image_id}.jpg"
|
18
|
+
@retrieved_image = last_response.body
|
13
19
|
end
|
14
20
|
|
15
21
|
Then /^I should get a (\d+) response$/ do |response_code|
|
@@ -23,3 +29,7 @@ end
|
|
23
29
|
Then /^I should get the (.*) resized image keeping the aspect ratio$/ do |size|
|
24
30
|
@retrieved_image.should be_same_image_as("test.#{size}.jpg")
|
25
31
|
end
|
32
|
+
|
33
|
+
Then /^I should get the (.*) cropped image$/ do |size|
|
34
|
+
@retrieved_image.should be_same_image_as("test.crop.#{size}.jpg")
|
35
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
require 'mugshot'
|
2
3
|
|
3
4
|
require 'rack/test'
|
@@ -22,7 +23,10 @@ end
|
|
22
23
|
Spec::Matchers.define :be_same_image_as do |expected_filename|
|
23
24
|
match do |actual_blob|
|
24
25
|
actual = Magick::Image.from_blob(actual_blob).first
|
25
|
-
expected = Magick::Image.read("
|
26
|
-
|
26
|
+
expected = Magick::Image.read(File.expand_path(__FILE__ + "/../files/#{expected_filename}")).first
|
27
|
+
|
28
|
+
actual.columns == expected.columns &&
|
29
|
+
actual.rows == expected.rows &&
|
30
|
+
actual.difference(expected)[1] < 0.01
|
27
31
|
end
|
28
32
|
end
|
Binary file
|
data/lib/mugshot.rb
CHANGED
data/lib/mugshot/application.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
require 'sinatra/base'
|
3
|
+
|
2
4
|
class Mugshot::Application < Sinatra::Base
|
3
5
|
|
4
6
|
set :static, true
|
@@ -6,16 +8,13 @@ class Mugshot::Application < Sinatra::Base
|
|
6
8
|
|
7
9
|
before do
|
8
10
|
response['Cache-Control'] = "public, max-age=#{1.year.to_i}"
|
9
|
-
content_type :jpg
|
10
11
|
end
|
11
|
-
|
12
|
+
|
12
13
|
get '/?' do
|
13
|
-
content_type :html
|
14
14
|
'ok'
|
15
15
|
end
|
16
16
|
|
17
17
|
post '/?' do
|
18
|
-
content_type :html
|
19
18
|
@storage.write(params['file'][:tempfile].read)
|
20
19
|
end
|
21
20
|
|
@@ -23,8 +22,21 @@ class Mugshot::Application < Sinatra::Base
|
|
23
22
|
image = @storage.read(id)
|
24
23
|
halt 404 if image.blank?
|
25
24
|
|
25
|
+
image.resize!(size)
|
26
|
+
|
26
27
|
begin
|
27
|
-
|
28
|
+
send_image(image, format.to_sym)
|
29
|
+
ensure
|
30
|
+
image.destroy!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
get '/*/:id.:format' do |splat, id, format|
|
35
|
+
image = @storage.read(id)
|
36
|
+
halt 404 if image.blank?
|
37
|
+
|
38
|
+
begin
|
39
|
+
process_operations(image, splat)
|
28
40
|
send_image(image, format.to_sym)
|
29
41
|
ensure
|
30
42
|
image.destroy!
|
@@ -32,12 +44,19 @@ class Mugshot::Application < Sinatra::Base
|
|
32
44
|
end
|
33
45
|
|
34
46
|
protected
|
47
|
+
|
35
48
|
def initialize(storage)
|
36
49
|
@storage = storage
|
37
50
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def process_operations(image, splat)
|
55
|
+
operations = Hash[*splat.split('/')]
|
56
|
+
operations.assert_valid_keys('crop', 'resize') rescue halt 404
|
57
|
+
operations.each do |op, op_params|
|
58
|
+
image.send("#{op}!", op_params)
|
59
|
+
end
|
41
60
|
end
|
42
61
|
|
43
62
|
def send_image(image, format)
|
data/lib/mugshot/fs_storage.rb
CHANGED
@@ -1,20 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
class Mugshot::FSStorage < Mugshot::Storage
|
2
3
|
def write(bin)
|
3
4
|
returning asset_id do |id|
|
4
|
-
File.open(File.join(@root_path, id), "w") do |fw|
|
5
|
+
File.open(File.join(@root_path, id), "w") do |fw|
|
5
6
|
fw.write(bin)
|
6
7
|
end
|
7
8
|
end
|
8
9
|
end
|
9
|
-
|
10
|
+
|
10
11
|
def read(id)
|
11
12
|
file = File.join(@root_path, id)
|
12
13
|
return nil unless File.exist? file
|
13
14
|
Mugshot::Image.new File.open(file)
|
14
15
|
end
|
15
|
-
|
16
|
+
|
16
17
|
protected
|
17
|
-
|
18
|
+
|
18
19
|
def initialize(root_path)
|
19
20
|
@root_path = root_path
|
20
21
|
FileUtils.mkdir_p(root_path)
|
data/lib/mugshot/image.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
2
|
class Mugshot::Image
|
3
|
-
|
3
|
+
|
4
4
|
def width
|
5
5
|
@image.columns
|
6
6
|
end
|
@@ -10,8 +10,8 @@ class Mugshot::Image
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def resize!(size)
|
13
|
-
w, h = size
|
14
|
-
|
13
|
+
w, h = parse_size(size)
|
14
|
+
|
15
15
|
if [w, h].include?(nil)
|
16
16
|
@image.resize_to_fit! w, h
|
17
17
|
else
|
@@ -21,6 +21,12 @@ class Mugshot::Image
|
|
21
21
|
self
|
22
22
|
end
|
23
23
|
|
24
|
+
def crop!(size)
|
25
|
+
w, h = parse_size(size)
|
26
|
+
@image.resize_to_fill! w, h
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
24
30
|
def destroy!
|
25
31
|
@image.destroy!
|
26
32
|
self
|
@@ -40,4 +46,9 @@ class Mugshot::Image
|
|
40
46
|
@image = Magick::Image.read(file).first
|
41
47
|
end
|
42
48
|
|
49
|
+
private
|
50
|
+
|
51
|
+
def parse_size(size)
|
52
|
+
size.to_s.split("x").map{|i| i.blank? ? nil : i.to_i}
|
53
|
+
end
|
43
54
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
class Mugshot::Proxy < Sinatra::Base
|
6
|
+
get '/*' do
|
7
|
+
url = URI.parse "#{@host}/#{params[:splat]}"
|
8
|
+
res = Net::HTTP.start(url.host, url.port) {|http|
|
9
|
+
http.get(url.path)
|
10
|
+
}
|
11
|
+
|
12
|
+
status res.code.to_i
|
13
|
+
|
14
|
+
headers_hash = {}
|
15
|
+
res.each_header do |k,v|
|
16
|
+
headers_hash[k] = v unless k.to_s =~ /status/i
|
17
|
+
end
|
18
|
+
headers headers_hash
|
19
|
+
|
20
|
+
res.body
|
21
|
+
end
|
22
|
+
|
23
|
+
before do
|
24
|
+
halt 405 unless request.get?
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def initialize(host)
|
30
|
+
@host = host
|
31
|
+
end
|
32
|
+
end
|
data/lib/mugshot/storage.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
3
|
|
3
4
|
describe Mugshot::Application do
|
@@ -7,7 +8,7 @@ describe Mugshot::Application do
|
|
7
8
|
Mugshot::Application.new(@storage)
|
8
9
|
end
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
describe "POST /" do
|
12
13
|
it "should create image" do
|
13
14
|
file_read = nil
|
@@ -18,46 +19,93 @@ describe Mugshot::Application do
|
|
18
19
|
|
19
20
|
last_response.status.should == 200
|
20
21
|
end
|
21
|
-
|
22
|
+
|
22
23
|
it "should return image id" do
|
23
24
|
@storage.stub!(:write).and_return("batata")
|
24
25
|
|
25
26
|
post "/", "file" => Rack::Test::UploadedFile.new("spec/files/test.jpg", "image/jpeg")
|
26
|
-
|
27
|
+
|
27
28
|
last_response.body.should == "batata"
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
image.should_receive(:destroy!)
|
37
|
-
@storage.stub!(:read).with("batata").and_return(image)
|
38
|
-
|
39
|
-
get "/200x200/batata.jpg"
|
32
|
+
shared_examples_for 'any GET image' do
|
33
|
+
before :each do
|
34
|
+
@image = mock(Mugshot::Image, :null_object => true)
|
35
|
+
@storage.stub!(:read).with("image_id").and_return(@image)
|
36
|
+
end
|
40
37
|
|
41
|
-
|
38
|
+
it "should convert image to format" do
|
39
|
+
@image.should_receive(:to_blob).with(:format => :jpg)
|
40
|
+
perform_get
|
42
41
|
last_response.content_type == "image/jpg"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should destroy image" do
|
45
|
+
@image.should_receive(:destroy!)
|
46
|
+
|
47
|
+
perform_get
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should return image" do
|
51
|
+
@image.stub!(:to_blob).and_return("image data")
|
52
|
+
|
53
|
+
perform_get
|
54
|
+
|
55
|
+
last_response.should be_ok
|
43
56
|
last_response.body.should == "image data"
|
44
57
|
end
|
45
58
|
|
46
59
|
it "should halt 404 when image doesn't exist" do
|
47
|
-
@storage.stub!(:read).with("
|
60
|
+
@storage.stub!(:read).with("image_id").and_return(nil)
|
48
61
|
|
49
|
-
|
62
|
+
perform_get
|
50
63
|
|
51
|
-
last_response.
|
64
|
+
last_response.should be_not_found
|
52
65
|
last_response.body.should be_empty
|
53
66
|
end
|
54
67
|
end
|
68
|
+
|
69
|
+
describe "GET /:size/:id.:format" do
|
70
|
+
def perform_get
|
71
|
+
get "/200x200/image_id.jpg"
|
72
|
+
end
|
73
|
+
|
74
|
+
it_should_behave_like 'any GET image'
|
75
|
+
|
76
|
+
it "should resize image" do
|
77
|
+
@image.should_receive(:resize!).with("200x200")
|
78
|
+
|
79
|
+
perform_get
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "GET /:ops/:ops_params/:id.:format" do
|
84
|
+
def perform_get
|
85
|
+
get "/crop/140x105/image_id.jpg"
|
86
|
+
end
|
87
|
+
|
88
|
+
it_should_behave_like 'any GET image'
|
89
|
+
|
90
|
+
it "should perform operations on image" do
|
91
|
+
@image.should_receive(:resize!).with("140x140")
|
92
|
+
@image.should_receive(:crop!).with("140x105")
|
93
|
+
|
94
|
+
get "/resize/140x140/crop/140x105/image_id.jpg"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should halt 404 on operations that are not allowed" do
|
98
|
+
@image.should_not_receive(:operation!)
|
99
|
+
|
100
|
+
get "/operation/140x105/image_id.jpg"
|
101
|
+
end
|
102
|
+
end
|
55
103
|
|
56
104
|
describe "GET /" do
|
57
105
|
it "should return ok as healthcheck" do
|
58
106
|
get "/"
|
59
107
|
|
60
|
-
last_response.
|
108
|
+
last_response.should be_ok
|
61
109
|
last_response.body.should == "ok"
|
62
110
|
end
|
63
111
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
3
|
|
3
4
|
describe Mugshot::FSStorage do
|
@@ -9,19 +10,19 @@ describe Mugshot::FSStorage do
|
|
9
10
|
require 'fileutils'
|
10
11
|
FileUtils.rm_rf("/tmp/mugshot/spec")
|
11
12
|
end
|
12
|
-
|
13
|
+
|
13
14
|
it "should write an image to the filesystem and read it back" do
|
14
15
|
bin = File.open("spec/files/test.jpg").read
|
15
|
-
|
16
|
+
|
16
17
|
image = Mugshot::Image.new File.open("spec/files/test.jpg")
|
17
18
|
Mugshot::Image.stub!(:new).and_return(image)
|
18
19
|
|
19
20
|
id = @fs.write(bin)
|
20
21
|
image2 = @fs.read(id)
|
21
|
-
|
22
|
+
|
22
23
|
image2.should == image
|
23
|
-
end
|
24
|
-
|
24
|
+
end
|
25
|
+
|
25
26
|
it "should return nil when an image with the given id doesn't exist on the filesystem" do
|
26
27
|
@fs.read('nonexistant-id').should be_nil
|
27
28
|
end
|
data/spec/mugshot/image_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
3
|
|
3
4
|
describe Mugshot::Image do
|
@@ -7,7 +8,7 @@ describe Mugshot::Image do
|
|
7
8
|
|
8
9
|
@image = Mugshot::Image.new(File.open("spec/files/test.jpg"))
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
it 'should return image width and height' do
|
12
13
|
@magick_image.stub!(:columns).and_return(100)
|
13
14
|
@magick_image.stub!(:rows).and_return(200)
|
@@ -56,7 +57,7 @@ describe Mugshot::Image do
|
|
56
57
|
end
|
57
58
|
|
58
59
|
end
|
59
|
-
|
60
|
+
|
60
61
|
it "should resize image to given width and height" do
|
61
62
|
@magick_image.should_receive(:resize!).with(300, 200)
|
62
63
|
@image.resize! "300x200"
|
@@ -71,10 +72,15 @@ describe Mugshot::Image do
|
|
71
72
|
@magick_image.should_receive(:resize_to_fit!).with(nil, 200)
|
72
73
|
@image.resize! "x200"
|
73
74
|
end
|
74
|
-
|
75
|
+
|
76
|
+
it "should crop image to given width and height" do
|
77
|
+
@magick_image.should_receive(:resize_to_fill!).with(140, 105)
|
78
|
+
@image.crop! "140x105"
|
79
|
+
end
|
80
|
+
|
75
81
|
it 'should destroy image' do
|
76
82
|
@magick_image.should_receive(:'destroy!')
|
77
83
|
@image.destroy!
|
78
84
|
end
|
79
|
-
|
85
|
+
|
80
86
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
3
|
+
|
4
|
+
describe Mugshot::Proxy do
|
5
|
+
before :each do
|
6
|
+
def app
|
7
|
+
Mugshot::Proxy.new("http://localhost:9292")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'GET /*' do
|
12
|
+
before :each do
|
13
|
+
@http = stub(Net::HTTP, :null_object => true)
|
14
|
+
@res = stub(Net::HTTPResponse, :null_object => true)
|
15
|
+
@http.stub!(:get).with('/request/path').and_return(@res)
|
16
|
+
Net::HTTP.stub!(:start).with("localhost", 9292).and_yield(@http)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should proxy the request to configured host' do
|
20
|
+
@res.stub!(:body).and_return('body')
|
21
|
+
|
22
|
+
get '/request/path'
|
23
|
+
|
24
|
+
last_response.body.should == 'body'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should return original response headers' do
|
28
|
+
@res.stub!(:each_header)\
|
29
|
+
.and_yield('content-type', 'image/jpg')\
|
30
|
+
.and_yield('cache-control', 'public, max-age=31557600')
|
31
|
+
|
32
|
+
get '/request/path'
|
33
|
+
|
34
|
+
last_response.headers['content-type'].should == 'image/jpg'
|
35
|
+
last_response.headers['cache-control'].should == 'public, max-age=31557600'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return original status' do
|
39
|
+
@res.stub!(:code).and_return('404')
|
40
|
+
|
41
|
+
get '/request/path'
|
42
|
+
|
43
|
+
last_response.status.should == 404
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should not return status header' do
|
47
|
+
@res.stub!(:each_header).and_yield('status', '200')
|
48
|
+
|
49
|
+
get '/request/path'
|
50
|
+
|
51
|
+
last_response.headers.should_not include('status')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'other methods' do
|
56
|
+
it 'should not be allowed' do
|
57
|
+
post '/request/path'
|
58
|
+
last_response.status.should == 405
|
59
|
+
|
60
|
+
put '/request/path'
|
61
|
+
last_response.status.should == 405
|
62
|
+
|
63
|
+
delete '/request/path'
|
64
|
+
last_response.status.should == 405
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mugshot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "Cain\xC3\xA3 Nunes"
|
@@ -12,7 +12,7 @@ autorequire:
|
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
14
|
|
15
|
-
date: 2010-
|
15
|
+
date: 2010-02-03 00:00:00 -02:00
|
16
16
|
default_executable:
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- lib/mugshot/application.rb
|
104
104
|
- lib/mugshot/fs_storage.rb
|
105
105
|
- lib/mugshot/image.rb
|
106
|
+
- lib/mugshot/proxy.rb
|
106
107
|
- lib/mugshot/public/crossdomain.xml
|
107
108
|
- lib/mugshot/storage.rb
|
108
109
|
- LICENSE
|
@@ -140,14 +141,17 @@ test_files:
|
|
140
141
|
- spec/mugshot/application_spec.rb
|
141
142
|
- spec/mugshot/fs_storage_spec.rb
|
142
143
|
- spec/mugshot/image_spec.rb
|
144
|
+
- spec/mugshot/proxy_spec.rb
|
143
145
|
- spec/spec.opts
|
144
146
|
- spec/spec_helper.rb
|
145
147
|
- spec/test.html
|
148
|
+
- features/crop_image.feature
|
146
149
|
- features/retrieve_resized_image.feature
|
147
150
|
- features/retrieve_resized_image_keeping_aspect_ratio.feature
|
148
151
|
- features/step_definitions/all_steps.rb
|
149
152
|
- features/support/env.rb
|
150
153
|
- features/support/files/test.200x.jpg
|
151
154
|
- features/support/files/test.200x200.jpg
|
155
|
+
- features/support/files/test.crop.300x200.jpg
|
152
156
|
- features/support/files/test.jpg
|
153
157
|
- features/support/files/test.x200.jpg
|