httpthumbnailer 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/bin/httpthumbnailer +15 -5
- data/features/httpthumbnailer.feature +35 -2
- data/features/step_definitions/httpthumbnailer_steps.rb +26 -19
- data/features/support/env.rb +36 -29
- data/features/support/test-transparent.png +0 -0
- data/httpthumbnailer.gemspec +3 -2
- data/lib/httpthumbnailer/thumbnailer.rb +8 -1
- metadata +5 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/bin/httpthumbnailer
CHANGED
@@ -24,6 +24,16 @@ sinatra.before do
|
|
24
24
|
@thumbnailer.method('crop') do |image, spec|
|
25
25
|
image.resize_to_fill(spec.width, spec.height)
|
26
26
|
end
|
27
|
+
|
28
|
+
@thumbnailer.method('fit') do |image, spec|
|
29
|
+
image.resize_to_fit(spec.width, spec.height)
|
30
|
+
end
|
31
|
+
|
32
|
+
@thumbnailer.method('pad') do |image, spec|
|
33
|
+
Magick::Image.new(spec.width, spec.height) {
|
34
|
+
self.background_color = Magick::Pixel.new(Magick::MaxRGB, Magick::MaxRGB, Magick::MaxRGB, Magick::MaxRGB) # transparent
|
35
|
+
}.composite!(image.resize_to_fit(spec.width, spec.height), Magick::CenterGravity, Magick::OverCompositeOp)
|
36
|
+
end
|
27
37
|
end
|
28
38
|
end
|
29
39
|
|
@@ -51,17 +61,17 @@ sinatra.put %r{/thumbnail/(.*)} do |specs|
|
|
51
61
|
status 200
|
52
62
|
headers "Content-Type" => "multipart/mixed; boundary=\"#{settings.boundary}\""
|
53
63
|
stream do |out|
|
54
|
-
thumbnail_specs.each do |
|
55
|
-
logger.info "Thumbnailing: #{
|
64
|
+
thumbnail_specs.each do |spec|
|
65
|
+
logger.info "Thumbnailing: #{spec}"
|
56
66
|
out << "--#{settings.boundary}\r\n"
|
57
67
|
|
58
68
|
begin
|
59
|
-
thumbnail = @thumbnailer.thumbnail('current',
|
69
|
+
thumbnail = @thumbnailer.thumbnail('current', spec)
|
60
70
|
thumbnail_data = thumbnail.to_blob do |inf|
|
61
|
-
inf.format =
|
71
|
+
inf.format = spec.format
|
62
72
|
end
|
63
73
|
|
64
|
-
out << "Content-Type: #{
|
74
|
+
out << "Content-Type: #{spec.mime}\r\n\r\n"
|
65
75
|
out << thumbnail_data
|
66
76
|
|
67
77
|
thumbnail_data = nil
|
@@ -3,16 +3,19 @@ Feature: Generating set of thumbnails with single PUT request
|
|
3
3
|
A user must PUT original image to URL in format
|
4
4
|
/thumbnail[/<thumbnail type>,<width>,<height>,<format>[,<option key>:<option value>]+]+
|
5
5
|
|
6
|
+
Background:
|
7
|
+
Given httpthumbnailer server is running at http://localhost:3100/
|
8
|
+
|
6
9
|
Scenario: Single thumbnail
|
7
10
|
Given test.jpg file content as request body
|
8
|
-
When I do PUT request /thumbnail/crop,16,16,PNG
|
11
|
+
When I do PUT request http://localhost:3100/thumbnail/crop,16,16,PNG
|
9
12
|
Then I will get multipart response
|
10
13
|
Then first part mime type will be image/png
|
11
14
|
And first part will contain PNG image of size 16x16
|
12
15
|
|
13
16
|
Scenario: Multiple thumbnails
|
14
17
|
Given test.jpg file content as request body
|
15
|
-
When I do PUT request /thumbnail/crop,16,16,PNG/crop,4,8,JPG/crop,16,32,JPEG
|
18
|
+
When I do PUT request http://localhost:3100/thumbnail/crop,16,16,PNG/crop,4,8,JPG/crop,16,32,JPEG
|
16
19
|
Then I will get multipart response
|
17
20
|
Then first part mime type will be image/png
|
18
21
|
And first part will contain PNG image of size 16x16
|
@@ -21,3 +24,33 @@ Feature: Generating set of thumbnails with single PUT request
|
|
21
24
|
Then third part mime type will be image/jpeg
|
22
25
|
And third part will contain JPEG image of size 16x32
|
23
26
|
|
27
|
+
Scenario: Transparent image to JPEG handling - default background color white
|
28
|
+
Given test-transparent.png file content as request body
|
29
|
+
When I do PUT request http://localhost:3100/thumbnail/fit,128,128,JPEG
|
30
|
+
Then I will get multipart response
|
31
|
+
And first part body will be saved as test-transparent-default.png for human inspection
|
32
|
+
And first part will contain JPEG image of size 128x128
|
33
|
+
And that image pixel at 32x32 will be of color white
|
34
|
+
|
35
|
+
Scenario: Fit thumbnailing method
|
36
|
+
Given test.jpg file content as request body
|
37
|
+
When I do PUT request http://localhost:3100/thumbnail/fit,128,128,PNG
|
38
|
+
Then I will get multipart response
|
39
|
+
And first part will contain PNG image of size 91x128
|
40
|
+
|
41
|
+
Scenario: Pad thumbnailing method - default background color white
|
42
|
+
Given test.jpg file content as request body
|
43
|
+
When I do PUT request http://localhost:3100/thumbnail/pad,128,128,PNG
|
44
|
+
Then I will get multipart response
|
45
|
+
And first part body will be saved as test-pad.png for human inspection
|
46
|
+
And first part will contain PNG image of size 128x128
|
47
|
+
And that image pixel at 2x2 will be of color white
|
48
|
+
|
49
|
+
Scenario: Pad thumbnailing method with specified background color
|
50
|
+
Given test.jpg file content as request body
|
51
|
+
When I do PUT request http://localhost:3100/thumbnail/pad,128,128,PNG,background-color:green
|
52
|
+
Then I will get multipart response
|
53
|
+
And first part body will be saved as test-pad-background-color.png for human inspection
|
54
|
+
And first part will contain PNG image of size 128x128
|
55
|
+
And that image pixel at 2x2 will be of color green
|
56
|
+
|
@@ -1,20 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
After do
|
9
|
-
server_stop
|
1
|
+
Given /httpthumbnailer server is running at (.*)/ do |url|
|
2
|
+
start_server(
|
3
|
+
"bundle exec #{script('httpthumbnailer')}",
|
4
|
+
'/tmp/httpthumbnailer.pid',
|
5
|
+
support_dir + 'server.log',
|
6
|
+
url
|
7
|
+
)
|
10
8
|
end
|
11
9
|
|
12
10
|
Given /(.*) file content as request body/ do |file|
|
13
11
|
@request_body = File.open(support_dir + file){|f| f.read }
|
14
12
|
end
|
15
13
|
|
16
|
-
When /I do (.*) request (.*)/ do |method,
|
17
|
-
@response =
|
14
|
+
When /I do (.*) request (.*)/ do |method, url|
|
15
|
+
@response = HTTPClient.new.request(method, url, nil, @request_body)
|
18
16
|
end
|
19
17
|
|
20
18
|
Then /I will get multipart response/ do
|
@@ -26,16 +24,25 @@ Then /(.*) part mime type will be (.*)/ do |part, mime|
|
|
26
24
|
@response_multipart.part[part_no(part)].header['Content-Type'].should == mime
|
27
25
|
end
|
28
26
|
|
29
|
-
Then /(.*) part will contain (.*) image of size (.*)/ do |part,
|
27
|
+
Then /(.*) part will contain (.*) image of size (.*)x(.*)/ do |part, format, width, height|
|
28
|
+
mime = @response_multipart.part[part_no(part)].header['Content-Type']
|
30
29
|
data = @response_multipart.part[part_no(part)].body
|
30
|
+
fail("expecte image got #{mime}: #{data}") unless mime =~ /^image\//
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
32
|
+
@image.destroy! if @image
|
33
|
+
@image = Magick::Image.from_blob(data).first
|
34
|
+
|
35
|
+
@image.format.should == format
|
36
|
+
@image.columns.should == width.to_i
|
37
|
+
@image.rows.should == height.to_i
|
39
38
|
end
|
40
39
|
|
40
|
+
And /(.*) part body will be saved as (.*) for human inspection/ do |part, file|
|
41
|
+
data = @response_multipart.part[part_no(part)].body
|
42
|
+
(support_dir + file).open('w'){|f| f.write(data)}
|
43
|
+
end
|
44
|
+
|
45
|
+
And /that image pixel at (.*)x(.*) will be of color (.*)/ do |x, y, color|
|
46
|
+
@image.pixel_color(x.to_i, y.to_i).to_color.sub(/^#/, '0x').should == color
|
47
|
+
end
|
41
48
|
|
data/features/support/env.rb
CHANGED
@@ -16,6 +16,7 @@ require 'httpclient'
|
|
16
16
|
require 'httpthumbnailer/multipart_response'
|
17
17
|
require "open3"
|
18
18
|
require "thread"
|
19
|
+
require 'RMagick'
|
19
20
|
|
20
21
|
def gem_dir
|
21
22
|
Pathname.new(__FILE__).dirname + '..' + '..'
|
@@ -33,24 +34,37 @@ def script(file)
|
|
33
34
|
gem_dir + 'bin' + file
|
34
35
|
end
|
35
36
|
|
36
|
-
def
|
37
|
-
|
37
|
+
def part_no(part)
|
38
|
+
case part
|
39
|
+
when 'first' then 0
|
40
|
+
when 'second' then 1
|
41
|
+
when 'third' then 2
|
42
|
+
else fail "add more parts?"
|
43
|
+
end
|
38
44
|
end
|
39
45
|
|
40
|
-
|
41
|
-
|
46
|
+
|
47
|
+
def get(url)
|
48
|
+
HTTPClient.new.get_content(url)
|
42
49
|
end
|
43
50
|
|
44
|
-
def
|
45
|
-
|
51
|
+
def start_server(cmd, pid_file, log_file, test_url)
|
52
|
+
stop_server(pid_file)
|
53
|
+
|
46
54
|
fork do
|
47
|
-
Daemon.daemonize(
|
48
|
-
exec(
|
55
|
+
Daemon.daemonize(pid_file, log_file)
|
56
|
+
exec(cmd)
|
57
|
+
end
|
58
|
+
Process.wait
|
59
|
+
|
60
|
+
ppid = Process.pid
|
61
|
+
at_exit do
|
62
|
+
stop_server(pid_file) if Process.pid == ppid
|
49
63
|
end
|
50
64
|
|
51
65
|
Timeout.timeout(10) do
|
52
66
|
begin
|
53
|
-
|
67
|
+
get test_url
|
54
68
|
rescue Errno::ECONNREFUSED
|
55
69
|
sleep 0.1
|
56
70
|
retry
|
@@ -58,28 +72,21 @@ def server_start
|
|
58
72
|
end
|
59
73
|
end
|
60
74
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
75
|
+
def stop_server(pid_file)
|
76
|
+
pid_file = Pathname.new(pid_file)
|
77
|
+
return unless pid_file.exist?
|
78
|
+
|
79
|
+
pid = pid_file.read.strip.to_i
|
80
|
+
|
81
|
+
Timeout.timeout(20) do
|
82
|
+
begin
|
83
|
+
loop do
|
84
|
+
Process.kill("TERM", pid)
|
85
|
+
sleep 0.1
|
72
86
|
end
|
87
|
+
rescue Errno::ESRCH
|
88
|
+
pid_file.unlink
|
73
89
|
end
|
74
90
|
end
|
75
91
|
end
|
76
92
|
|
77
|
-
def part_no(part)
|
78
|
-
case part
|
79
|
-
when 'first' then 0
|
80
|
-
when 'second' then 1
|
81
|
-
when 'third' then 2
|
82
|
-
else fail "add more parts?"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
Binary file
|
data/httpthumbnailer.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "httpthumbnailer"
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jakub Pastuszek"]
|
12
|
-
s.date = "2011-11-
|
12
|
+
s.date = "2011-11-28"
|
13
13
|
s.description = "Provides HTTP API for thumbnailing images"
|
14
14
|
s.email = "jpastuszek@gmail.com"
|
15
15
|
s.executables = ["httpthumbnailer"]
|
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
|
|
30
30
|
"features/httpthumbnailer.feature",
|
31
31
|
"features/step_definitions/httpthumbnailer_steps.rb",
|
32
32
|
"features/support/env.rb",
|
33
|
+
"features/support/test-transparent.png",
|
33
34
|
"features/support/test.jpg",
|
34
35
|
"httpthumbnailer.gemspec",
|
35
36
|
"lib/httpthumbnailer/multipart_response.rb",
|
@@ -67,7 +67,14 @@ class Thumbnailer
|
|
67
67
|
|
68
68
|
def thumbnail(id, spec)
|
69
69
|
image = @images[id] or raise ImageNotFound.new(id)
|
70
|
-
process_image(image, spec)
|
70
|
+
thumb = process_image(image, spec)
|
71
|
+
replace_transparency(thumb, spec)
|
72
|
+
end
|
73
|
+
|
74
|
+
def replace_transparency(image, spec)
|
75
|
+
Magick::Image.new(image.columns, image.rows) {
|
76
|
+
self.background_color = (spec.options['background-color'] or 'white').sub(/^0x/, '#')
|
77
|
+
}.composite!(image, Magick::CenterGravity, Magick::OverCompositeOp)
|
71
78
|
end
|
72
79
|
|
73
80
|
def process_image(image, spec)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpthumbnailer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jakub Pastuszek
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-11-
|
18
|
+
date: 2011-11-28 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
type: :runtime
|
@@ -219,6 +219,7 @@ files:
|
|
219
219
|
- features/httpthumbnailer.feature
|
220
220
|
- features/step_definitions/httpthumbnailer_steps.rb
|
221
221
|
- features/support/env.rb
|
222
|
+
- features/support/test-transparent.png
|
222
223
|
- features/support/test.jpg
|
223
224
|
- httpthumbnailer.gemspec
|
224
225
|
- lib/httpthumbnailer/multipart_response.rb
|