httpthumbnailer 0.0.14 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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", "~> 0.0.3"
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 (0.0.3)
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 (~> 0.0.3)
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.14
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) do |original_image_handler|
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
- original_image_handler.use do |original_image|
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
- thumbnail_data = original_image.thumbnail(spec)
114
-
115
- out << "Content-Type: #{spec.mime}\r\n\r\n"
116
- out << thumbnail_data
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 original image to URL in format
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
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "httpthumbnailer"
8
- s.version = "0.0.14"
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 = "2011-12-02"
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>, ["~> 0.0.3"])
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>, ["~> 0.0.3"])
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>, ["~> 0.0.3"])
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} #{mime} (#{format}) #{options.inspect}"
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 OriginalImage
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 |thumb|
137
- thumb.to_blob do
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
- h = ImageHandler.new do
212
- OriginalImage.new(io, @methods, @options.merge(options))
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: 3
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 14
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: 2011-12-02 00:00:00 Z
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: 25
102
+ hash: 19
103
103
  segments:
104
+ - 1
105
+ - 1
104
106
  - 0
105
- - 0
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: &id008 !ruby/object:Gem::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: *id008
126
+ version_requirements: *id007
143
127
  - !ruby/object:Gem::Dependency
144
128
  type: :development
145
- requirement: &id009 !ruby/object:Gem::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: *id009
140
+ version_requirements: *id008
157
141
  - !ruby/object:Gem::Dependency
158
142
  type: :development
159
- requirement: &id010 !ruby/object:Gem::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: *id010
156
+ version_requirements: *id009
173
157
  - !ruby/object:Gem::Dependency
174
158
  type: :development
175
- requirement: &id011 !ruby/object:Gem::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: *id011
172
+ version_requirements: *id010
189
173
  - !ruby/object:Gem::Dependency
190
174
  type: :development
191
- requirement: &id012 !ruby/object:Gem::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: *id012
186
+ version_requirements: *id011
203
187
  - !ruby/object:Gem::Dependency
204
188
  type: :development
205
- requirement: &id013 !ruby/object:Gem::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: *id013
200
+ version_requirements: *id012
217
201
  - !ruby/object:Gem::Dependency
218
202
  type: :development
219
- requirement: &id014 !ruby/object:Gem::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: *id014
215
+ version_requirements: *id013
232
216
  - !ruby/object:Gem::Dependency
233
217
  type: :development
234
- requirement: &id015 !ruby/object:Gem::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: *id015
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