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 CHANGED
@@ -1 +1 @@
1
- 0.0.2
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 |ts|
55
- logger.info "Thumbnailing: #{ts}"
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', ts)
69
+ thumbnail = @thumbnailer.thumbnail('current', spec)
60
70
  thumbnail_data = thumbnail.to_blob do |inf|
61
- inf.format = ts.format
71
+ inf.format = spec.format
62
72
  end
63
73
 
64
- out << "Content-Type: #{ts.mime}\r\n\r\n"
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
- Before do
2
- server_start
3
- @request_body = nil
4
- @response = nil
5
- @response_multipart = nil
6
- end
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, uri|
17
- @response = server_request(method, uri, nil, @request_body)
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, image_type, image_size|
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
- Open3.popen3('identify -') do |stdin, stdout, stderr|
33
- stdin.write data
34
- stdin.close
35
- path, type, size, *rest = *stdout.read.split(' ')
36
- type.should == image_type
37
- size.should == image_size
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
 
@@ -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 server_get(uri)
37
- HTTPClient.new.get_content("http://localhost:3100#{uri}")
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
- def server_request(method, uri, query = nil, body = nil)
41
- HTTPClient.new.request(method, "http://localhost:3100#{uri}", query, body)
46
+
47
+ def get(url)
48
+ HTTPClient.new.get_content(url)
42
49
  end
43
50
 
44
- def server_start
45
- File.exist?("/tmp/httpthumbnailer.pid") and server_stop
51
+ def start_server(cmd, pid_file, log_file, test_url)
52
+ stop_server(pid_file)
53
+
46
54
  fork do
47
- Daemon.daemonize("/tmp/httpthumbnailer.pid", support_dir + 'server.log')
48
- exec("bundle exec #{script('httpthumbnailer')} -p 3100")
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
- server_get '/'
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 server_stop
62
- File.open("/tmp/httpthumbnailer.pid") do |pidf|
63
- pid = pidf.read
64
-
65
- Timeout.timeout(10) do
66
- begin
67
- loop do
68
- ret = Process.kill("TERM", pid.strip.to_i)
69
- sleep 0.1
70
- end
71
- rescue Errno::ESRCH
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
-
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "httpthumbnailer"
8
- s.version = "0.0.2"
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-23"
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: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
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-23 00:00:00 Z
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