httpthumbnailer 0.0.14 → 0.1.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 +1 -4
- data/Gemfile.lock +2 -4
- data/VERSION +1 -1
- data/bin/httpthumbnailer +8 -6
- data/features/httpthumbnailer.feature +13 -1
- data/features/step_definitions/httpthumbnailer_steps.rb +5 -0
- data/features/support/test.png +0 -0
- data/httpthumbnailer.gemspec +6 -8
- data/lib/httpthumbnailer/thumbnailer.rb +62 -39
- metadata +25 -40
data/Gemfile
CHANGED
@@ -5,10 +5,7 @@ gem "mongrel", ">= 1.1.5"
|
|
5
5
|
gem "rmagick", "~> 2"
|
6
6
|
gem "haml", "~> 3"
|
7
7
|
gem "ruby-ip", "~> 0.9"
|
8
|
-
gem "cli", "~>
|
9
|
-
|
10
|
-
# testing
|
11
|
-
gem "fcgi", "~> 0.8.8"
|
8
|
+
gem "cli", "~> 1.1.0"
|
12
9
|
|
13
10
|
# Add dependencies to develop your gem here.
|
14
11
|
# Include everything needed to run rake, tests, features, etc.
|
data/Gemfile.lock
CHANGED
@@ -3,7 +3,7 @@ GEM
|
|
3
3
|
specs:
|
4
4
|
builder (3.0.0)
|
5
5
|
cgi_multipart_eof_fix (2.5.0)
|
6
|
-
cli (
|
6
|
+
cli (1.1.0)
|
7
7
|
cucumber (1.1.2)
|
8
8
|
builder (>= 2.1.2)
|
9
9
|
diff-lcs (>= 1.1.2)
|
@@ -14,7 +14,6 @@ GEM
|
|
14
14
|
daemons (1.1.4)
|
15
15
|
diff-lcs (1.1.3)
|
16
16
|
fastthread (1.0.7)
|
17
|
-
fcgi (0.8.8)
|
18
17
|
gem_plugin (0.2.3)
|
19
18
|
gherkin (2.6.5)
|
20
19
|
json (>= 1.4.6)
|
@@ -59,10 +58,9 @@ PLATFORMS
|
|
59
58
|
|
60
59
|
DEPENDENCIES
|
61
60
|
bundler (~> 1.0.0)
|
62
|
-
cli (~>
|
61
|
+
cli (~> 1.1.0)
|
63
62
|
cucumber
|
64
63
|
daemon (~> 1)
|
65
|
-
fcgi (~> 0.8.8)
|
66
64
|
haml (~> 3)
|
67
65
|
httpclient (~> 2.2)
|
68
66
|
jeweler (~> 1.6.4)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/bin/httpthumbnailer
CHANGED
@@ -99,21 +99,23 @@ sinatra.put %r{^/thumbnail/(.*)} do |specs|
|
|
99
99
|
opts.merge!({'max-width' => biggest_spec.width, 'max-height' => biggest_spec.height})
|
100
100
|
end
|
101
101
|
|
102
|
-
$thumbnailer.load(request.body, opts)
|
102
|
+
input_image_handler = $thumbnailer.load(request.body, opts)
|
103
|
+
input_image_handler.get do |input_image|
|
103
104
|
status 200
|
104
105
|
headers "Content-Type" => "multipart/mixed; boundary=\"#{settings.boundary}\""
|
106
|
+
headers "X-Input-Image-Content-Type" => input_image.mime_type
|
105
107
|
|
106
108
|
stream do |out| # this is non blocking
|
107
|
-
|
109
|
+
input_image_handler.use do |input_image|
|
108
110
|
thumbnail_specs.each do |spec|
|
109
111
|
logger.info "Thumbnailing: #{spec}"
|
110
112
|
out << "--#{settings.boundary}\r\n"
|
111
113
|
|
112
114
|
begin
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
115
|
+
input_image.thumbnail(spec) do |thumbnail|
|
116
|
+
out << "Content-Type: #{thumbnail.mime_type}\r\n\r\n"
|
117
|
+
out << thumbnail.data
|
118
|
+
end
|
117
119
|
rescue => e
|
118
120
|
logger.error "Thumbnailing error: #{e.class.name}: #{e}: \n#{e.backtrace.join("\n")}"
|
119
121
|
out << "Content-Type: text/plain\r\n\r\n"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Feature: Generating set of thumbnails with single PUT request
|
2
2
|
In order to generate a set of image thumbnails
|
3
|
-
A user must PUT
|
3
|
+
A user must PUT an image to URL in format
|
4
4
|
/thumbnail[/<thumbnail type>,<width>,<height>,<format>[,<option key>:<option value>]+]+
|
5
5
|
|
6
6
|
Background:
|
@@ -213,3 +213,15 @@ Feature: Generating set of thumbnails with single PUT request
|
|
213
213
|
Then second part will contain body smaller than third part
|
214
214
|
And there will be no leaked images
|
215
215
|
|
216
|
+
Scenario: Hint on input image mime type - JPEG
|
217
|
+
Given test.jpg file content as request body
|
218
|
+
When I do PUT request http://localhost:3100/thumbnail/crop,16,16,PNG
|
219
|
+
Then response status will be 200
|
220
|
+
And X-Input-Image-Content-Type header will be image/jpeg
|
221
|
+
|
222
|
+
Scenario: Hint on input image mime type - PNG
|
223
|
+
Given test.png file content as request body
|
224
|
+
When I do PUT request http://localhost:3100/thumbnail/crop,16,16,PNG
|
225
|
+
Then response status will be 200
|
226
|
+
And X-Input-Image-Content-Type header will be image/png
|
227
|
+
|
@@ -19,6 +19,11 @@ When /I do (.*) request (.*)/ do |method, url|
|
|
19
19
|
@response = HTTPClient.new.request(method, url, nil, @request_body)
|
20
20
|
end
|
21
21
|
|
22
|
+
Then /(.*) header will be (.*)/ do |header, value|
|
23
|
+
@response.header[header].should_not be_empty
|
24
|
+
@response.header[header].first.should == value
|
25
|
+
end
|
26
|
+
|
22
27
|
Then /I will get multipart response/ do
|
23
28
|
@response.header['Content-Type'].first.should match /^multipart/
|
24
29
|
@response_multipart = MultipartResponse.new(@response.header['Content-Type'].last, @response.body)
|
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.1.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 = "2012-01-04"
|
13
13
|
s.description = "Provides HTTP API for thumbnailing images"
|
14
14
|
s.email = "jpastuszek@gmail.com"
|
15
15
|
s.executables = ["httpthumbnailer"]
|
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
|
|
33
33
|
"features/support/test-large.jpg",
|
34
34
|
"features/support/test-transparent.png",
|
35
35
|
"features/support/test.jpg",
|
36
|
+
"features/support/test.png",
|
36
37
|
"features/support/test.txt",
|
37
38
|
"httpthumbnailer.gemspec",
|
38
39
|
"lib/httpthumbnailer/multipart_response.rb",
|
@@ -69,8 +70,7 @@ Gem::Specification.new do |s|
|
|
69
70
|
s.add_runtime_dependency(%q<rmagick>, ["~> 2"])
|
70
71
|
s.add_runtime_dependency(%q<haml>, ["~> 3"])
|
71
72
|
s.add_runtime_dependency(%q<ruby-ip>, ["~> 0.9"])
|
72
|
-
s.add_runtime_dependency(%q<cli>, ["~>
|
73
|
-
s.add_runtime_dependency(%q<fcgi>, ["~> 0.8.8"])
|
73
|
+
s.add_runtime_dependency(%q<cli>, ["~> 1.1.0"])
|
74
74
|
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
75
75
|
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
76
76
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
@@ -85,8 +85,7 @@ Gem::Specification.new do |s|
|
|
85
85
|
s.add_dependency(%q<rmagick>, ["~> 2"])
|
86
86
|
s.add_dependency(%q<haml>, ["~> 3"])
|
87
87
|
s.add_dependency(%q<ruby-ip>, ["~> 0.9"])
|
88
|
-
s.add_dependency(%q<cli>, ["~>
|
89
|
-
s.add_dependency(%q<fcgi>, ["~> 0.8.8"])
|
88
|
+
s.add_dependency(%q<cli>, ["~> 1.1.0"])
|
90
89
|
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
91
90
|
s.add_dependency(%q<cucumber>, [">= 0"])
|
92
91
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
@@ -102,8 +101,7 @@ Gem::Specification.new do |s|
|
|
102
101
|
s.add_dependency(%q<rmagick>, ["~> 2"])
|
103
102
|
s.add_dependency(%q<haml>, ["~> 3"])
|
104
103
|
s.add_dependency(%q<ruby-ip>, ["~> 0.9"])
|
105
|
-
s.add_dependency(%q<cli>, ["~>
|
106
|
-
s.add_dependency(%q<fcgi>, ["~> 0.8.8"])
|
104
|
+
s.add_dependency(%q<cli>, ["~> 1.1.0"])
|
107
105
|
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
108
106
|
s.add_dependency(%q<cucumber>, [">= 0"])
|
109
107
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
@@ -22,18 +22,10 @@ class ThumbnailSpec
|
|
22
22
|
@options = options
|
23
23
|
end
|
24
24
|
|
25
|
-
def mime
|
26
|
-
mime = case format
|
27
|
-
when 'JPG' then 'jpeg'
|
28
|
-
else format.downcase
|
29
|
-
end
|
30
|
-
"image/#{mime}"
|
31
|
-
end
|
32
|
-
|
33
25
|
attr_reader :method, :width, :height, :format, :options
|
34
26
|
|
35
27
|
def to_s
|
36
|
-
"#{method} #{width}x#{height}
|
28
|
+
"#{method} #{width}x#{height} (#{format}) #{options.inspect}"
|
37
29
|
end
|
38
30
|
end
|
39
31
|
|
@@ -67,6 +59,17 @@ class Thumbnailer
|
|
67
59
|
@image = yield
|
68
60
|
end
|
69
61
|
|
62
|
+
def get
|
63
|
+
raise ImageDestroyedError unless @image
|
64
|
+
begin
|
65
|
+
yield @image
|
66
|
+
rescue
|
67
|
+
@image.destroy!
|
68
|
+
@image = nil
|
69
|
+
raise
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
70
73
|
def use
|
71
74
|
raise ImageDestroyedError unless @image
|
72
75
|
begin
|
@@ -84,7 +87,7 @@ class Thumbnailer
|
|
84
87
|
end
|
85
88
|
end
|
86
89
|
|
87
|
-
class
|
90
|
+
class InputImage
|
88
91
|
def initialize(io, methods, options = {})
|
89
92
|
@options = options
|
90
93
|
@logger = (options[:logger] or Logger.new('/dev/null'))
|
@@ -128,16 +131,10 @@ class Thumbnailer
|
|
128
131
|
|
129
132
|
def thumbnail(spec)
|
130
133
|
begin
|
131
|
-
quality = (spec.options['quality'] or default_quality(spec.format))
|
132
|
-
quality = quality.to_i if quality
|
133
|
-
|
134
134
|
ImageHandler.new do
|
135
135
|
process_image(@image, spec).render_on_background!((spec.options['background-color'] or 'white').sub(/^0x/, '#'))
|
136
|
-
end.use do |
|
137
|
-
|
138
|
-
self.format = spec.format
|
139
|
-
self.quality = quality if quality
|
140
|
-
end
|
136
|
+
end.use do |image|
|
137
|
+
yield Thumbnail.new(image, spec)
|
141
138
|
end
|
142
139
|
rescue Magick::ImageMagickError => e
|
143
140
|
raise ImageTooLargeError, e if e.message =~ /cache resources exhausted/
|
@@ -145,23 +142,16 @@ class Thumbnailer
|
|
145
142
|
end
|
146
143
|
end
|
147
144
|
|
145
|
+
def mime_type
|
146
|
+
@image.mime_type
|
147
|
+
end
|
148
|
+
|
148
149
|
def destroy!
|
149
150
|
@image.destroy!
|
150
151
|
end
|
151
152
|
|
152
153
|
private
|
153
154
|
|
154
|
-
def default_quality(format)
|
155
|
-
case format
|
156
|
-
when /png/i
|
157
|
-
95 # max zlib compression, adaptive filtering (photo)
|
158
|
-
when /jpeg|jpg/i
|
159
|
-
85
|
160
|
-
else
|
161
|
-
nil
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
155
|
def find_prescale_factor(max_width, max_height, factor = 1)
|
166
156
|
new_factor = factor * 2
|
167
157
|
if @image.columns / new_factor > max_width * 2 and @image.rows / new_factor > max_height * 2
|
@@ -183,6 +173,47 @@ class Thumbnailer
|
|
183
173
|
end
|
184
174
|
end
|
185
175
|
|
176
|
+
class Thumbnail
|
177
|
+
def initialize(image, spec)
|
178
|
+
@image = image
|
179
|
+
@spec = spec
|
180
|
+
end
|
181
|
+
|
182
|
+
def data
|
183
|
+
quality = (@spec.options['quality'] or default_quality(@spec.format))
|
184
|
+
quality = quality.to_i if quality
|
185
|
+
|
186
|
+
spec = @spec
|
187
|
+
@image.to_blob do
|
188
|
+
self.format = spec.format
|
189
|
+
self.quality = quality if quality
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def mime_type
|
194
|
+
#@image.mime_type cannot be used since it is raw loaded image
|
195
|
+
#TODO: how do I do it better?
|
196
|
+
mime = case @spec.format
|
197
|
+
when 'JPG' then 'jpeg'
|
198
|
+
else @spec.format.downcase
|
199
|
+
end
|
200
|
+
"image/#{mime}"
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def default_quality(format)
|
206
|
+
case format
|
207
|
+
when /png/i
|
208
|
+
95 # max zlib compression, adaptive filtering (photo)
|
209
|
+
when /jpeg|jpg/i
|
210
|
+
85
|
211
|
+
else
|
212
|
+
nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
186
217
|
def initialize(options = {})
|
187
218
|
@methods = {}
|
188
219
|
@options = options
|
@@ -208,16 +239,8 @@ class Thumbnailer
|
|
208
239
|
end
|
209
240
|
|
210
241
|
def load(io, options = {})
|
211
|
-
|
212
|
-
|
213
|
-
end
|
214
|
-
|
215
|
-
begin
|
216
|
-
yield h
|
217
|
-
rescue
|
218
|
-
# make sure that we destroy original image if there was an error before it could be used
|
219
|
-
h.destroy!
|
220
|
-
raise
|
242
|
+
ImageHandler.new do
|
243
|
+
InputImage.new(io, @methods, @options.merge(options))
|
221
244
|
end
|
222
245
|
end
|
223
246
|
|
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: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.14
|
10
|
+
version: 0.1.0
|
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:
|
18
|
+
date: 2012-01-04 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
type: :runtime
|
@@ -99,34 +99,18 @@ dependencies:
|
|
99
99
|
requirements:
|
100
100
|
- - ~>
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
hash:
|
102
|
+
hash: 19
|
103
103
|
segments:
|
104
|
+
- 1
|
105
|
+
- 1
|
104
106
|
- 0
|
105
|
-
|
106
|
-
- 3
|
107
|
-
version: 0.0.3
|
107
|
+
version: 1.1.0
|
108
108
|
prerelease: false
|
109
109
|
name: cli
|
110
110
|
version_requirements: *id006
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
type: :runtime
|
113
|
-
requirement: &id007 !ruby/object:Gem::Requirement
|
114
|
-
none: false
|
115
|
-
requirements:
|
116
|
-
- - ~>
|
117
|
-
- !ruby/object:Gem::Version
|
118
|
-
hash: 47
|
119
|
-
segments:
|
120
|
-
- 0
|
121
|
-
- 8
|
122
|
-
- 8
|
123
|
-
version: 0.8.8
|
124
|
-
prerelease: false
|
125
|
-
name: fcgi
|
126
|
-
version_requirements: *id007
|
127
111
|
- !ruby/object:Gem::Dependency
|
128
112
|
type: :development
|
129
|
-
requirement: &
|
113
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
130
114
|
none: false
|
131
115
|
requirements:
|
132
116
|
- - ~>
|
@@ -139,10 +123,10 @@ dependencies:
|
|
139
123
|
version: 2.3.0
|
140
124
|
prerelease: false
|
141
125
|
name: rspec
|
142
|
-
version_requirements: *
|
126
|
+
version_requirements: *id007
|
143
127
|
- !ruby/object:Gem::Dependency
|
144
128
|
type: :development
|
145
|
-
requirement: &
|
129
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
146
130
|
none: false
|
147
131
|
requirements:
|
148
132
|
- - ">="
|
@@ -153,10 +137,10 @@ dependencies:
|
|
153
137
|
version: "0"
|
154
138
|
prerelease: false
|
155
139
|
name: cucumber
|
156
|
-
version_requirements: *
|
140
|
+
version_requirements: *id008
|
157
141
|
- !ruby/object:Gem::Dependency
|
158
142
|
type: :development
|
159
|
-
requirement: &
|
143
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
160
144
|
none: false
|
161
145
|
requirements:
|
162
146
|
- - ~>
|
@@ -169,10 +153,10 @@ dependencies:
|
|
169
153
|
version: 1.0.0
|
170
154
|
prerelease: false
|
171
155
|
name: bundler
|
172
|
-
version_requirements: *
|
156
|
+
version_requirements: *id009
|
173
157
|
- !ruby/object:Gem::Dependency
|
174
158
|
type: :development
|
175
|
-
requirement: &
|
159
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
176
160
|
none: false
|
177
161
|
requirements:
|
178
162
|
- - ~>
|
@@ -185,10 +169,10 @@ dependencies:
|
|
185
169
|
version: 1.6.4
|
186
170
|
prerelease: false
|
187
171
|
name: jeweler
|
188
|
-
version_requirements: *
|
172
|
+
version_requirements: *id010
|
189
173
|
- !ruby/object:Gem::Dependency
|
190
174
|
type: :development
|
191
|
-
requirement: &
|
175
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
192
176
|
none: false
|
193
177
|
requirements:
|
194
178
|
- - ">="
|
@@ -199,10 +183,10 @@ dependencies:
|
|
199
183
|
version: "0"
|
200
184
|
prerelease: false
|
201
185
|
name: rcov
|
202
|
-
version_requirements: *
|
186
|
+
version_requirements: *id011
|
203
187
|
- !ruby/object:Gem::Dependency
|
204
188
|
type: :development
|
205
|
-
requirement: &
|
189
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
206
190
|
none: false
|
207
191
|
requirements:
|
208
192
|
- - ~>
|
@@ -213,10 +197,10 @@ dependencies:
|
|
213
197
|
version: "1"
|
214
198
|
prerelease: false
|
215
199
|
name: daemon
|
216
|
-
version_requirements: *
|
200
|
+
version_requirements: *id012
|
217
201
|
- !ruby/object:Gem::Dependency
|
218
202
|
type: :development
|
219
|
-
requirement: &
|
203
|
+
requirement: &id013 !ruby/object:Gem::Requirement
|
220
204
|
none: false
|
221
205
|
requirements:
|
222
206
|
- - ~>
|
@@ -228,10 +212,10 @@ dependencies:
|
|
228
212
|
version: "2.2"
|
229
213
|
prerelease: false
|
230
214
|
name: httpclient
|
231
|
-
version_requirements: *
|
215
|
+
version_requirements: *id013
|
232
216
|
- !ruby/object:Gem::Dependency
|
233
217
|
type: :development
|
234
|
-
requirement: &
|
218
|
+
requirement: &id014 !ruby/object:Gem::Requirement
|
235
219
|
none: false
|
236
220
|
requirements:
|
237
221
|
- - ~>
|
@@ -243,7 +227,7 @@ dependencies:
|
|
243
227
|
version: "3.9"
|
244
228
|
prerelease: false
|
245
229
|
name: rdoc
|
246
|
-
version_requirements: *
|
230
|
+
version_requirements: *id014
|
247
231
|
description: Provides HTTP API for thumbnailing images
|
248
232
|
email: jpastuszek@gmail.com
|
249
233
|
executables:
|
@@ -269,6 +253,7 @@ files:
|
|
269
253
|
- features/support/test-large.jpg
|
270
254
|
- features/support/test-transparent.png
|
271
255
|
- features/support/test.jpg
|
256
|
+
- features/support/test.png
|
272
257
|
- features/support/test.txt
|
273
258
|
- httpthumbnailer.gemspec
|
274
259
|
- lib/httpthumbnailer/multipart_response.rb
|