httpthumbnailer-client 0.1.1 → 1.0.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/Gemfile +10 -7
- data/Gemfile.lock +56 -49
- data/README.md +39 -15
- data/VERSION +1 -1
- data/bin/httpthumbnailer-client +73 -0
- data/httpthumbnailer-client.gemspec +38 -26
- data/lib/httpthumbnailer-client.rb +122 -48
- data/spec/httpthumbnailer-client_spec.rb +9 -138
- data/spec/spec_helper.rb +11 -4
- data/spec/{test-large.jpg → support/test-large.jpg} +0 -0
- data/spec/{test.jpg → support/test.jpg} +0 -0
- data/spec/{test.txt → support/test.txt} +0 -0
- data/spec/thumbnail_spec.rb +66 -0
- data/spec/thumbnails_spec.rb +110 -0
- data/spec/uri_builder_spec.rb +16 -0
- metadata +83 -32
- data/lib/httpthumbnailer-client/multipart_response.rb +0 -45
- data/spec/multipart_response_spec.rb +0 -95
data/Gemfile
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
|
+
ruby "1.9.3"
|
2
3
|
|
3
|
-
gem "httpclient", ">= 2.
|
4
|
+
gem "httpclient", ">= 2.3"
|
5
|
+
gem "cli", "~> 1.1.0"
|
6
|
+
gem "multipart-parser", "~> 0.1.1"
|
4
7
|
|
5
8
|
# Add dependencies to develop your gem here.
|
6
9
|
# Include everything needed to run rake, tests, features, etc.
|
7
10
|
group :development do
|
8
|
-
gem "rspec", "~> 2.
|
11
|
+
gem "rspec", "~> 2.13"
|
12
|
+
gem "rspec-mocks", "~> 2.13"
|
9
13
|
gem "cucumber", ">= 0"
|
10
|
-
gem "
|
11
|
-
gem "jeweler", "~> 1.6"
|
12
|
-
gem "simplecov", ">= 0"
|
14
|
+
gem "jeweler", "~> 1.8.4"
|
13
15
|
gem "rdoc", "~> 3.9"
|
14
16
|
gem "daemon", "~> 1"
|
15
|
-
gem "
|
17
|
+
gem "rmagick", "~> 2"
|
18
|
+
gem "httpthumbnailer", path: '../httpthumbnailer'
|
19
|
+
gem "unicorn-cuba-base", path: '../unicorn-cuba-base'
|
16
20
|
end
|
17
|
-
|
data/Gemfile.lock
CHANGED
@@ -1,75 +1,82 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../httpthumbnailer
|
3
|
+
specs:
|
4
|
+
httpthumbnailer (1.0.0)
|
5
|
+
rmagick (~> 2)
|
6
|
+
unicorn-cuba-base (~> 1.0)
|
7
|
+
|
8
|
+
PATH
|
9
|
+
remote: ../unicorn-cuba-base
|
10
|
+
specs:
|
11
|
+
unicorn-cuba-base (1.0.0)
|
12
|
+
cli (~> 1.1.0)
|
13
|
+
cuba (~> 3.0)
|
14
|
+
facter (~> 1.6.11)
|
15
|
+
raindrops (~> 0.11)
|
16
|
+
ruby-ip (~> 0.9)
|
17
|
+
unicorn (>= 4.6.2)
|
18
|
+
|
1
19
|
GEM
|
2
20
|
remote: http://rubygems.org/
|
3
21
|
specs:
|
4
22
|
builder (3.0.0)
|
5
23
|
cli (1.1.1)
|
6
|
-
|
24
|
+
cuba (3.1.0)
|
25
|
+
rack
|
26
|
+
cucumber (1.1.3)
|
7
27
|
builder (>= 2.1.2)
|
8
|
-
diff-lcs (>= 1.1.
|
9
|
-
gherkin (~> 2.
|
28
|
+
diff-lcs (>= 1.1.2)
|
29
|
+
gherkin (~> 2.6.7)
|
10
30
|
json (>= 1.4.6)
|
31
|
+
term-ansicolor (>= 1.0.6)
|
11
32
|
daemon (1.1.0)
|
12
|
-
daemons (1.0.10)
|
13
33
|
diff-lcs (1.1.3)
|
14
|
-
|
15
|
-
gherkin (2.
|
34
|
+
facter (1.6.18)
|
35
|
+
gherkin (2.6.7)
|
16
36
|
json (>= 1.4.6)
|
17
37
|
git (1.2.5)
|
18
|
-
|
19
|
-
httpclient (2.2.7)
|
20
|
-
httpthumbnailer (0.3.1)
|
21
|
-
cli (~> 1.1.0)
|
22
|
-
haml (~> 3)
|
23
|
-
mongrel (>= 1.2.0.pre2)
|
24
|
-
rmagick (~> 2)
|
25
|
-
ruby-ip (~> 0.9)
|
26
|
-
sinatra (>= 1.2.6)
|
38
|
+
httpclient (2.3.3)
|
27
39
|
jeweler (1.8.4)
|
28
40
|
bundler (~> 1.0)
|
29
41
|
git (>= 1.2.5)
|
30
42
|
rake
|
31
43
|
rdoc
|
32
|
-
json (1.
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
rack
|
40
|
-
rake (0.9.2.2)
|
41
|
-
rdoc (3.12)
|
42
|
-
json (~> 1.4)
|
44
|
+
json (1.6.1)
|
45
|
+
kgio (2.8.0)
|
46
|
+
multipart-parser (0.1.1)
|
47
|
+
rack (1.5.2)
|
48
|
+
raindrops (0.11.0)
|
49
|
+
rake (10.0.4)
|
50
|
+
rdoc (3.9.4)
|
43
51
|
rmagick (2.13.1)
|
44
|
-
rspec (2.
|
45
|
-
rspec-core (~> 2.
|
46
|
-
rspec-expectations (~> 2.
|
47
|
-
rspec-mocks (~> 2.
|
48
|
-
rspec-core (2.
|
49
|
-
rspec-expectations (2.
|
50
|
-
diff-lcs (
|
51
|
-
rspec-mocks (2.
|
52
|
+
rspec (2.13.0)
|
53
|
+
rspec-core (~> 2.13.0)
|
54
|
+
rspec-expectations (~> 2.13.0)
|
55
|
+
rspec-mocks (~> 2.13.0)
|
56
|
+
rspec-core (2.13.1)
|
57
|
+
rspec-expectations (2.13.0)
|
58
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
59
|
+
rspec-mocks (2.13.1)
|
52
60
|
ruby-ip (0.9.1)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
rack (~> 1.3, >= 1.3.6)
|
59
|
-
rack-protection (~> 1.2)
|
60
|
-
tilt (~> 1.3, >= 1.3.3)
|
61
|
-
tilt (1.3.3)
|
61
|
+
term-ansicolor (1.0.7)
|
62
|
+
unicorn (4.6.3)
|
63
|
+
kgio (~> 2.6)
|
64
|
+
rack
|
65
|
+
raindrops (~> 0.7)
|
62
66
|
|
63
67
|
PLATFORMS
|
64
68
|
ruby
|
65
69
|
|
66
70
|
DEPENDENCIES
|
67
|
-
|
71
|
+
cli (~> 1.1.0)
|
68
72
|
cucumber
|
69
73
|
daemon (~> 1)
|
70
|
-
httpclient (>= 2.
|
71
|
-
httpthumbnailer
|
72
|
-
jeweler (~> 1.
|
74
|
+
httpclient (>= 2.3)
|
75
|
+
httpthumbnailer!
|
76
|
+
jeweler (~> 1.8.4)
|
77
|
+
multipart-parser (~> 0.1.1)
|
73
78
|
rdoc (~> 3.9)
|
74
|
-
|
75
|
-
|
79
|
+
rmagick (~> 2)
|
80
|
+
rspec (~> 2.13)
|
81
|
+
rspec-mocks (~> 2.13)
|
82
|
+
unicorn-cuba-base!
|
data/README.md
CHANGED
@@ -1,44 +1,68 @@
|
|
1
|
-
#
|
1
|
+
# HTTP Thubnailer Client
|
2
2
|
|
3
3
|
Ruby client to [httpthumbnailer](http://github.com/jpastuszek/httpthumbnailer) image scaling and conversion HTTP API server.
|
4
4
|
|
5
5
|
## Installing
|
6
6
|
|
7
|
-
|
7
|
+
```bash
|
8
|
+
gem install httpthumbnailer-client
|
9
|
+
```
|
8
10
|
|
9
11
|
## Usage
|
10
12
|
|
11
|
-
|
13
|
+
### Ruby API
|
12
14
|
|
13
15
|
```ruby
|
14
16
|
require 'httpthumbnailer-client'
|
15
17
|
|
16
|
-
# read
|
18
|
+
# read original image data (may be any format supported by ImageMagick/GraphicsMagick installation on the server)
|
17
19
|
data = File.read('image_file.jpg')
|
18
20
|
|
19
|
-
#
|
21
|
+
# with API server listening on localhost port 3100
|
22
|
+
# see the API server documentation for available operations, formats and options
|
23
|
+
|
24
|
+
# generate single thumbnail from image data (single thumbnail API)
|
25
|
+
thumbnail = HTTPThumbnailerClient.new('http://localhost:3100').thumbnail(data, 'crop', 60, 30, 'jpeg')
|
26
|
+
thumbnail.mime_type # => 'image/jpeg'
|
27
|
+
thumbnail.data # => 60x30 thumbnail JPEG data String
|
28
|
+
|
29
|
+
# generate set of thumbnails from image data (multipart API)
|
20
30
|
thumbnails = HTTPThumbnailerClient.new('http://localhost:3100').thumbnail(data) do
|
21
|
-
|
22
|
-
thumbnail 'crop',
|
23
|
-
thumbnail '
|
24
|
-
thumbnail 'pad', 4, 4, 'PNG'
|
31
|
+
thumbnail 'crop', 60, 30, 'jpeg'
|
32
|
+
thumbnail 'crop', 80, 80, 'png'
|
33
|
+
thumbnail 'pad', 40, 40, 'png'
|
25
34
|
end
|
26
35
|
|
27
36
|
thumbnails[0].mime_type # => 'image/jpeg'
|
28
|
-
thumbnails[0].data # =>
|
37
|
+
thumbnails[0].data # => 60x30 thumbnail JPEG data String
|
29
38
|
|
30
39
|
thumbnails[1].mime_type # => 'image/png'
|
31
|
-
thumbnails[1].data # =>
|
40
|
+
thumbnails[1].data # => 80x80 thumbnail PNG data String
|
32
41
|
|
33
42
|
thumbnails[2].mime_type # => 'image/png'
|
34
|
-
thumbnails[2].data # =>
|
43
|
+
thumbnails[2].data # => 40x40 thumbnail PNG data String
|
35
44
|
|
36
45
|
thumbnails.input_mime_type # => 'image/jpeg' - detected input image format by API server (content based)
|
37
46
|
```
|
38
47
|
|
39
|
-
For more details see
|
48
|
+
For more details see RSpec for [single thumbnail API](http://github.com/jpastuszek/httpthumbnailer-client/blob/master/spec/thumbnail_spec.rb) and [multipart API](http://github.com/jpastuszek/httpthumbnailer-client/blob/master/spec/thumbnails_spec.rb).
|
49
|
+
|
50
|
+
### CLI tool
|
51
|
+
|
52
|
+
This gem provides `httpthumbnailer-client` command line tool that can be used to thumbnail images via [httpthumbnailer](http://github.com/jpastuszek/httpthumbnailer).
|
53
|
+
|
54
|
+
```bash
|
55
|
+
# start thumbnailing server (to stop: kill `cat httpthumbnailer.pid`)
|
56
|
+
httpthumbnailer
|
57
|
+
|
58
|
+
# thumbnail to standard output
|
59
|
+
cat ~/Pictures/compute.jpg | httpthumbnailer-client -t crop,100,200,png > thumbnail.png
|
60
|
+
|
61
|
+
# generate multiple thumbnails
|
62
|
+
cat ~/Pictures/compute.jpg | httpthumbnailer-client -t crop,100,200,jpeg,quality:100 -t pad,200,200,png thumbnail1.jpg thumbnail2.png
|
63
|
+
```
|
40
64
|
|
41
|
-
## Contributing to
|
65
|
+
## Contributing to HTTP Thubnailer Client
|
42
66
|
|
43
67
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
44
68
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
@@ -50,6 +74,6 @@ For more details see [RSpec tests](http://github.com/jpastuszek/httpthumbnailer-
|
|
50
74
|
|
51
75
|
## Copyright
|
52
76
|
|
53
|
-
Copyright (c)
|
77
|
+
Copyright (c) 2013 Jakub Pastuszek. See LICENSE.txt for
|
54
78
|
further details.
|
55
79
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'cli'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
require 'httpthumbnailer-client'
|
7
|
+
|
8
|
+
def parse_spec(spec)
|
9
|
+
method, width, height, format, *options = *spec.split(',')
|
10
|
+
fail "missing thumbnail specification argument in: #{spec}" unless method and width and height and format
|
11
|
+
|
12
|
+
opts = {}
|
13
|
+
options.each do |option|
|
14
|
+
key, value = option.split(':')
|
15
|
+
fail "missing option key or value in specifiaction: #{spec}" unless key and value
|
16
|
+
opts[key] = value
|
17
|
+
end
|
18
|
+
|
19
|
+
[method, width, height, format, opts]
|
20
|
+
end
|
21
|
+
|
22
|
+
settings = CLI.new do
|
23
|
+
description 'Client to httpthumbnailer image scaling and conversion HTTP API server'
|
24
|
+
stdin :image_data,
|
25
|
+
description: 'image to be thumbnailed'
|
26
|
+
option :host,
|
27
|
+
short: :H,
|
28
|
+
description: 'host on which the httpthumbnailer is running',
|
29
|
+
default: 'localhost'
|
30
|
+
option :port,
|
31
|
+
short: :p,
|
32
|
+
description: 'TCP port on which the httpthumbnailer is listening',
|
33
|
+
cast: Integer,
|
34
|
+
default: 3100
|
35
|
+
options :thumbnail,
|
36
|
+
short: :t,
|
37
|
+
description: 'thumbnail specification in format <operation type>,<width>,<height>,<format>[,<option key>:<option value>]* - see httpthumbnailer documenation for more information',
|
38
|
+
required: true
|
39
|
+
arguments :output_file_name,
|
40
|
+
short: :f,
|
41
|
+
description: 'file name to store resulting thumbnail; when multiple --thumbail options are used the same number of output file names should be specified; use "-" to write to STDOUT',
|
42
|
+
default: '-'
|
43
|
+
version (Pathname.new(__FILE__).dirname + '..' + 'VERSION').read
|
44
|
+
end.parse! do |settings|
|
45
|
+
STDERR.puts "Warning: there is more thumbnail specifications given than output file names; some thumbnails will not be saved" if settings.thumbnail.length > settings.output_file_name.length
|
46
|
+
fail 'only one thumbnail can be written to STDOUT' if settings.output_file_name.count('-') > 1
|
47
|
+
settings.thumbnail = settings.thumbnail.map{|spec| parse_spec(spec)}
|
48
|
+
end
|
49
|
+
|
50
|
+
thumbnailer = HTTPThumbnailerClient.new("http://#{settings.host}:#{settings.port}")
|
51
|
+
|
52
|
+
thumbnails = if settings.thumbnail.length == 1
|
53
|
+
[thumbnailer.thumbnail(settings.stdin.read, *settings.thumbnail.first)]
|
54
|
+
else
|
55
|
+
thumbnailer.thumbnail(settings.stdin.read) do
|
56
|
+
settings.thumbnail.each do |spec|
|
57
|
+
thumbnail *spec
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
thumbnails.zip(settings.output_file_name).select do |thumbnail, out_file|
|
63
|
+
out_file
|
64
|
+
end.each do |thumbnail, out_file|
|
65
|
+
if out_file == '-'
|
66
|
+
STDOUT.write thumbnail.data
|
67
|
+
else
|
68
|
+
File.open(out_file, 'w') do |file|
|
69
|
+
file.write thumbnail.data
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -5,13 +5,14 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "httpthumbnailer-client"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "1.0.0"
|
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 = "
|
12
|
+
s.date = "2013-07-16"
|
13
13
|
s.description = "Thumbnails images using httpthumbniler server"
|
14
14
|
s.email = "jpastuszek@gmail.com"
|
15
|
+
s.executables = ["httpthumbnailer-client"]
|
15
16
|
s.extra_rdoc_files = [
|
16
17
|
"LICENSE.txt",
|
17
18
|
"README.md"
|
@@ -25,58 +26,69 @@ Gem::Specification.new do |s|
|
|
25
26
|
"README.md",
|
26
27
|
"Rakefile",
|
27
28
|
"VERSION",
|
29
|
+
"bin/httpthumbnailer-client",
|
28
30
|
"features/step_definitions/httpthumbnailer-client_steps.rb",
|
29
31
|
"features/support/env.rb",
|
30
32
|
"httpthumbnailer-client.gemspec",
|
31
33
|
"lib/httpthumbnailer-client.rb",
|
32
|
-
"lib/httpthumbnailer-client/multipart_response.rb",
|
33
34
|
"spec/httpthumbnailer-client_spec.rb",
|
34
|
-
"spec/multipart_response_spec.rb",
|
35
35
|
"spec/spec_helper.rb",
|
36
|
-
"spec/test-large.jpg",
|
37
|
-
"spec/test.jpg",
|
38
|
-
"spec/test.txt"
|
36
|
+
"spec/support/test-large.jpg",
|
37
|
+
"spec/support/test.jpg",
|
38
|
+
"spec/support/test.txt",
|
39
|
+
"spec/thumbnail_spec.rb",
|
40
|
+
"spec/thumbnails_spec.rb",
|
41
|
+
"spec/uri_builder_spec.rb"
|
39
42
|
]
|
40
43
|
s.homepage = "http://github.com/jpastuszek/httpthumbnailer-client"
|
41
44
|
s.licenses = ["MIT"]
|
42
45
|
s.require_paths = ["lib"]
|
43
|
-
s.rubygems_version = "1.8.
|
46
|
+
s.rubygems_version = "1.8.25"
|
44
47
|
s.summary = "API client for httpthumbniler server"
|
45
48
|
|
46
49
|
if s.respond_to? :specification_version then
|
47
50
|
s.specification_version = 3
|
48
51
|
|
49
52
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
50
|
-
s.add_runtime_dependency(%q<httpclient>, [">= 2.
|
51
|
-
s.
|
53
|
+
s.add_runtime_dependency(%q<httpclient>, [">= 2.3"])
|
54
|
+
s.add_runtime_dependency(%q<cli>, ["~> 1.1.0"])
|
55
|
+
s.add_runtime_dependency(%q<multipart-parser>, ["~> 0.1.1"])
|
56
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.13"])
|
57
|
+
s.add_development_dependency(%q<rspec-mocks>, ["~> 2.13"])
|
52
58
|
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
53
|
-
s.add_development_dependency(%q<
|
54
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.6"])
|
55
|
-
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
56
60
|
s.add_development_dependency(%q<rdoc>, ["~> 3.9"])
|
57
61
|
s.add_development_dependency(%q<daemon>, ["~> 1"])
|
58
|
-
s.add_development_dependency(%q<
|
62
|
+
s.add_development_dependency(%q<rmagick>, ["~> 2"])
|
63
|
+
s.add_development_dependency(%q<httpthumbnailer>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<unicorn-cuba-base>, [">= 0"])
|
59
65
|
else
|
60
|
-
s.add_dependency(%q<httpclient>, [">= 2.
|
61
|
-
s.add_dependency(%q<
|
66
|
+
s.add_dependency(%q<httpclient>, [">= 2.3"])
|
67
|
+
s.add_dependency(%q<cli>, ["~> 1.1.0"])
|
68
|
+
s.add_dependency(%q<multipart-parser>, ["~> 0.1.1"])
|
69
|
+
s.add_dependency(%q<rspec>, ["~> 2.13"])
|
70
|
+
s.add_dependency(%q<rspec-mocks>, ["~> 2.13"])
|
62
71
|
s.add_dependency(%q<cucumber>, [">= 0"])
|
63
|
-
s.add_dependency(%q<
|
64
|
-
s.add_dependency(%q<jeweler>, ["~> 1.6"])
|
65
|
-
s.add_dependency(%q<simplecov>, [">= 0"])
|
72
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
66
73
|
s.add_dependency(%q<rdoc>, ["~> 3.9"])
|
67
74
|
s.add_dependency(%q<daemon>, ["~> 1"])
|
68
|
-
s.add_dependency(%q<
|
75
|
+
s.add_dependency(%q<rmagick>, ["~> 2"])
|
76
|
+
s.add_dependency(%q<httpthumbnailer>, [">= 0"])
|
77
|
+
s.add_dependency(%q<unicorn-cuba-base>, [">= 0"])
|
69
78
|
end
|
70
79
|
else
|
71
|
-
s.add_dependency(%q<httpclient>, [">= 2.
|
72
|
-
s.add_dependency(%q<
|
80
|
+
s.add_dependency(%q<httpclient>, [">= 2.3"])
|
81
|
+
s.add_dependency(%q<cli>, ["~> 1.1.0"])
|
82
|
+
s.add_dependency(%q<multipart-parser>, ["~> 0.1.1"])
|
83
|
+
s.add_dependency(%q<rspec>, ["~> 2.13"])
|
84
|
+
s.add_dependency(%q<rspec-mocks>, ["~> 2.13"])
|
73
85
|
s.add_dependency(%q<cucumber>, [">= 0"])
|
74
|
-
s.add_dependency(%q<
|
75
|
-
s.add_dependency(%q<jeweler>, ["~> 1.6"])
|
76
|
-
s.add_dependency(%q<simplecov>, [">= 0"])
|
86
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
77
87
|
s.add_dependency(%q<rdoc>, ["~> 3.9"])
|
78
88
|
s.add_dependency(%q<daemon>, ["~> 1"])
|
79
|
-
s.add_dependency(%q<
|
89
|
+
s.add_dependency(%q<rmagick>, ["~> 2"])
|
90
|
+
s.add_dependency(%q<httpthumbnailer>, [">= 0"])
|
91
|
+
s.add_dependency(%q<unicorn-cuba-base>, [">= 0"])
|
80
92
|
end
|
81
93
|
end
|
82
94
|
|
@@ -1,47 +1,98 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
1
|
require 'httpclient'
|
3
|
-
require '
|
2
|
+
require 'multipart_parser/reader'
|
4
3
|
|
5
4
|
class HTTPThumbnailerClient
|
6
|
-
|
7
|
-
|
5
|
+
HTTPThumbnailerClientError = Class.new(ArgumentError)
|
6
|
+
|
7
|
+
InvalidThumbnailSpecificationError = Class.new(HTTPThumbnailerClientError)
|
8
|
+
ServerResourceNotFoundError = Class.new(HTTPThumbnailerClientError)
|
9
|
+
UnsupportedMediaTypeError = Class.new(HTTPThumbnailerClientError)
|
10
|
+
ImageTooLargeError = Class.new(HTTPThumbnailerClientError)
|
11
|
+
RemoteServerError = Class.new(HTTPThumbnailerClientError)
|
12
|
+
|
13
|
+
@@errors = {}
|
14
|
+
|
15
|
+
module Status
|
16
|
+
def status=(st)
|
17
|
+
@status = st
|
18
|
+
end
|
8
19
|
|
9
|
-
|
20
|
+
def status
|
21
|
+
@status
|
22
|
+
end
|
23
|
+
|
24
|
+
module Instance
|
25
|
+
def status
|
26
|
+
defined?(self.class.status) ? self.class.status : 500
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.extended(klass)
|
31
|
+
klass.instance_eval do
|
32
|
+
include Instance
|
33
|
+
end
|
34
|
+
end
|
10
35
|
end
|
11
36
|
|
12
|
-
|
37
|
+
def self.assign_status_code(error, status)
|
38
|
+
error.extend Status
|
39
|
+
error.status = status
|
40
|
+
@@errors[status] = error
|
41
|
+
@@errors[error] = status
|
13
42
|
end
|
14
43
|
|
15
|
-
|
44
|
+
assign_status_code(InvalidThumbnailSpecificationError, 400)
|
45
|
+
assign_status_code(ServerResourceNotFoundError, 404)
|
46
|
+
assign_status_code(UnsupportedMediaTypeError, 415)
|
47
|
+
assign_status_code(ImageTooLargeError, 413)
|
48
|
+
assign_status_code(RemoteServerError, 500)
|
49
|
+
|
50
|
+
def error_for_status(status, body)
|
51
|
+
@@errors.fetch(status){|err| RemoteServerError}.new(body)
|
16
52
|
end
|
17
53
|
|
54
|
+
UnknownResponseType = Class.new HTTPThumbnailerClientError
|
55
|
+
InvalidMultipartResponseError = Class.new HTTPThumbnailerClientError
|
56
|
+
|
18
57
|
class URIBuilder
|
19
58
|
def initialize(service_uri, &block)
|
20
59
|
@specs = []
|
21
60
|
@service_uri = service_uri
|
22
|
-
instance_eval &block
|
61
|
+
instance_eval &block if block
|
23
62
|
end
|
24
63
|
|
25
64
|
def get
|
26
65
|
"#{@service_uri}/#{@specs.join('/')}"
|
27
66
|
end
|
28
67
|
|
29
|
-
def self.thumbnail(
|
30
|
-
self.new('/thumbnail'
|
68
|
+
def self.thumbnail(*spec)
|
69
|
+
self.new('/thumbnail').thumbnail(*spec).get
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.thumbnails(&block)
|
73
|
+
self.new('/thumbnails', &block).get
|
31
74
|
end
|
32
75
|
|
33
|
-
def thumbnail(method, width, height, format = '
|
76
|
+
def thumbnail(method, width, height, format = 'jpeg', options = {})
|
77
|
+
width = width.to_s
|
78
|
+
height = height.to_s
|
79
|
+
|
34
80
|
args = []
|
35
81
|
args << method.to_s
|
36
|
-
|
37
|
-
args <<
|
82
|
+
width !~ /^([0-9]+|input)$/ and raise InvalidThumbnailSpecificationError.new("bad dimension value: #{width}")
|
83
|
+
args << width
|
84
|
+
height !~ /^([0-9]+|input)$/ and raise InvalidThumbnailSpecificationError.new("bad dimension value: #{height}")
|
85
|
+
args << height
|
38
86
|
args << format.to_s
|
39
87
|
|
40
88
|
options.keys.sort{|a, b| a.to_s <=> b.to_s}.each do |key|
|
89
|
+
raise InvalidThumbnailSpecificationError.new("empty option key for value '#{options[key]}'") if key.nil? || key.to_s.empty?
|
90
|
+
raise InvalidThumbnailSpecificationError.new("missing option value for key '#{key}'") if options[key].nil? || options[key].to_s.empty?
|
41
91
|
args << "#{key}:#{options[key]}"
|
42
92
|
end
|
43
93
|
|
44
94
|
@specs << args.join(',')
|
95
|
+
self
|
45
96
|
end
|
46
97
|
end
|
47
98
|
|
@@ -58,64 +109,87 @@ class HTTPThumbnailerClient
|
|
58
109
|
attr_accessor :input_mime_type
|
59
110
|
end
|
60
111
|
|
61
|
-
class ThumbnailingError
|
62
|
-
def initialize(msg)
|
63
|
-
@message = msg
|
64
|
-
@type = msg.match(/Error: (.*?): /)[1] rescue NoMethodError
|
65
|
-
end
|
66
|
-
|
67
|
-
attr_reader :type, :message
|
68
|
-
end
|
69
|
-
|
70
112
|
def initialize(server_url, options = {})
|
71
113
|
@server_url = server_url
|
72
114
|
@client = HTTPClient.new
|
115
|
+
|
116
|
+
# long timeouts for big image data
|
117
|
+
@client.send_timeout = options[:send_timeout] || 300
|
118
|
+
@client.receive_timeout = options[:receive_timeout] || 300
|
119
|
+
|
120
|
+
# don't use keep alive by default since backend won't support it any way unless fronted with nginx or similar
|
73
121
|
@keep_alive = options[:keep_alive] || false
|
74
122
|
end
|
75
123
|
|
76
|
-
|
77
|
-
|
124
|
+
attr_reader :server_url
|
125
|
+
attr_reader :keep_alive
|
126
|
+
|
127
|
+
def thumbnail(data, *spec, &block)
|
128
|
+
uri = if block
|
129
|
+
URIBuilder.thumbnails(&block)
|
130
|
+
else
|
131
|
+
URIBuilder.thumbnail(*spec)
|
132
|
+
end
|
78
133
|
|
79
134
|
response = @client.request('PUT', "#{@server_url}#{uri}", nil, data, {'Content-Type' => 'image/autodetect'})
|
80
135
|
@client.reset_all unless @keep_alive
|
81
136
|
|
82
137
|
content_type = response.header['Content-Type'].last
|
83
138
|
|
84
|
-
case content_type
|
139
|
+
thumbnails = case content_type
|
85
140
|
when 'text/plain'
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
when 413
|
90
|
-
raise ImageTooLargeError, response.body.delete("\r")
|
91
|
-
else
|
92
|
-
raise RemoteServerError, response.body.delete("\r")
|
93
|
-
end
|
141
|
+
raise error_for_status(response.status, response.body)
|
142
|
+
when /^image\//
|
143
|
+
Thumbnail.new(content_type, response.body)
|
94
144
|
when /^multipart\/mixed/
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
raise UnknownResponseType, part_content_type
|
145
|
+
parts = []
|
146
|
+
parser = MultipartParser::Reader.new(MultipartParser::Reader.extract_boundary_value(content_type))
|
147
|
+
parser.on_part do |part|
|
148
|
+
part_content_type = part.headers['content-type'] or raise InvalidMultipartResponseError, 'missing Content-Type header in multipart part'
|
149
|
+
part_status = part.headers['status']
|
150
|
+
data = ''
|
151
|
+
|
152
|
+
part.on_data do |partial_data|
|
153
|
+
data << partial_data
|
105
154
|
end
|
106
|
-
end
|
107
155
|
|
108
|
-
|
109
|
-
|
110
|
-
|
156
|
+
part.on_end do
|
157
|
+
case part_content_type
|
158
|
+
when 'text/plain'
|
159
|
+
part_status or raise InvalidMultipartResponseError, 'missing Status header in error part (text/plain)'
|
160
|
+
|
161
|
+
begin
|
162
|
+
raise error_for_status(part_status.to_i, data.strip)
|
163
|
+
rescue HTTPThumbnailerClientError => error # raise for stack trace
|
164
|
+
parts << error
|
165
|
+
end
|
166
|
+
when /^image\//
|
167
|
+
parts << Thumbnail.new(part_content_type, data)
|
168
|
+
else
|
169
|
+
raise UnknownResponseType, part_content_type
|
170
|
+
end
|
171
|
+
end
|
111
172
|
end
|
112
173
|
|
113
|
-
|
174
|
+
parser.write response.body
|
175
|
+
parser.ended? or raise InvalidMultipartResponseError, 'truncated multipart message'
|
176
|
+
parts
|
114
177
|
else
|
115
178
|
raise UnknownResponseType, content_type
|
116
179
|
end
|
180
|
+
|
181
|
+
unless response.header['X-Input-Image-Content-Type'].empty?
|
182
|
+
thumbnails.extend(ThumbnailsInputMimeTypeMeta)
|
183
|
+
thumbnails.input_mime_type = response.header['X-Input-Image-Content-Type'].first
|
184
|
+
end
|
185
|
+
|
186
|
+
return thumbnails
|
117
187
|
rescue HTTPClient::KeepAliveDisconnected
|
118
188
|
raise RemoteServerError, 'empty response'
|
119
189
|
end
|
190
|
+
|
191
|
+
def inspect
|
192
|
+
"#<#{self.class.name} server_url=#{server_url.inspect} keep_alive=#{keep_alive.inspect}>"
|
193
|
+
end
|
120
194
|
end
|
121
195
|
|