httpthumbnailer 0.0.2 → 0.0.3

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/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