httpthumbnailer 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -1
- data/Gemfile.lock +6 -6
- data/README.md +24 -6
- data/VERSION +1 -1
- data/bin/httpthumbnailer +11 -8
- data/httpthumbnailer.gemspec +10 -12
- data/lib/httpthumbnailer/error_reporter.rb +1 -1
- data/lib/httpthumbnailer/plugin/thumbnailer.rb +76 -34
- data/lib/httpthumbnailer/thumbnailer.rb +3 -3
- data/spec/image_processing_spec.rb +14 -14
- data/spec/plugin_thumbnailer_spec.rb +318 -0
- data/spec/support/square_even.png +0 -0
- data/spec/support/square_odd.png +0 -0
- data/spec/support/test_image.rb +16 -0
- data/spec/thumbnail_specs_spec.rb +1 -1
- metadata +10 -12
- data/.idea/encodings.xml +0 -5
- data/.idea/httpthumbnailer.iml +0 -56
- data/.idea/misc.xml +0 -5
- data/.idea/modules.xml +0 -9
- data/.idea/scopes/scope_settings.xml +0 -5
- data/.idea/vcs.xml +0 -7
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -13,7 +13,7 @@ GEM
|
|
13
13
|
childprocess (0.3.9)
|
14
14
|
ffi (~> 1.0, >= 1.0.11)
|
15
15
|
cli (1.3.1)
|
16
|
-
cuba (3.1.
|
16
|
+
cuba (3.1.1)
|
17
17
|
rack
|
18
18
|
cucumber (1.3.1)
|
19
19
|
builder (>= 2.1.2)
|
@@ -52,7 +52,7 @@ GEM
|
|
52
52
|
json (1.7.7)
|
53
53
|
jwt (0.1.8)
|
54
54
|
multi_json (>= 1.5)
|
55
|
-
kgio (2.
|
55
|
+
kgio (2.9.2)
|
56
56
|
mime-types (1.23)
|
57
57
|
multi_json (1.7.3)
|
58
58
|
multi_xml (0.5.5)
|
@@ -69,7 +69,7 @@ GEM
|
|
69
69
|
rack (1.5.2)
|
70
70
|
rack-test (0.6.2)
|
71
71
|
rack (>= 1.0)
|
72
|
-
raindrops (0.
|
72
|
+
raindrops (0.13.0)
|
73
73
|
rake (10.1.0)
|
74
74
|
rdoc (3.12.2)
|
75
75
|
json (~> 1.4)
|
@@ -89,11 +89,11 @@ GEM
|
|
89
89
|
multi_json (~> 1.0)
|
90
90
|
rubyzip
|
91
91
|
websocket (~> 1.0.4)
|
92
|
-
unicorn (4.
|
92
|
+
unicorn (4.8.2)
|
93
93
|
kgio (~> 2.6)
|
94
94
|
rack
|
95
95
|
raindrops (~> 0.7)
|
96
|
-
unicorn-cuba-base (1.
|
96
|
+
unicorn-cuba-base (1.2.0)
|
97
97
|
cli (~> 1.3)
|
98
98
|
cuba (~> 3.0)
|
99
99
|
facter (~> 1.6.11)
|
@@ -118,4 +118,4 @@ DEPENDENCIES
|
|
118
118
|
rmagick (~> 2)
|
119
119
|
rspec (~> 2.13)
|
120
120
|
rspec-mocks (~> 2.13)
|
121
|
-
unicorn-cuba-base (~> 1.
|
121
|
+
unicorn-cuba-base (~> 1.2.0)
|
data/README.md
CHANGED
@@ -17,6 +17,12 @@ It is using [ImageMagick](http://www.imagemagick.org) or [GraphicsMagick](http:/
|
|
17
17
|
|
18
18
|
## Changelog
|
19
19
|
|
20
|
+
### 1.2.0
|
21
|
+
* added `float-x` and `float-y` option support
|
22
|
+
* added `interlace` option support
|
23
|
+
* syslog logging
|
24
|
+
* transaction ID tracking
|
25
|
+
|
20
26
|
### 1.1.0
|
21
27
|
|
22
28
|
* added identification API for image mime type and size identification
|
@@ -92,13 +98,18 @@ If running as root you can use `--user` option to specify user with whose privil
|
|
92
98
|
|
93
99
|
Additionally `httpthumbnailer` will log requests in [common NCSA format](http://en.wikipedia.org/wiki/Common_Log_Format) to `httpthumbnailer_access.log` file. Use `--access-log-file` option to change location of access log.
|
94
100
|
|
101
|
+
Syslog logging can be enabled with `--syslog-facility` option followed by name of syslog facility to use. When enabled log files are not created and both application logs and access logs are sent to syslog.
|
102
|
+
Access logs will gain meta information that will include `type="http-access"` that can be used to filter access log entries out from application log entries.
|
103
|
+
|
104
|
+
With `--xid-header` option name of HTTP request header can be specified. Value of this header will be logged in meta information tag `xid` along side all request related log entries.
|
105
|
+
|
95
106
|
### Supported operations
|
96
107
|
|
97
108
|
As operation type you can select one of the following options:
|
98
|
-
* fit - fit image within given dimensions keeping aspect ratio
|
99
|
-
* crop - cut image to fit within given dimensions keeping aspect ratio
|
100
|
-
* pad - fit scale image and pad image with background colour to given dimensions keeping aspect ratio
|
101
|
-
* limit - fit scale image to given dimensions if it is larger than that dimensions
|
109
|
+
* `fit` - fit image within given dimensions keeping aspect ratio
|
110
|
+
* `crop` - cut image to fit within given dimensions keeping aspect ratio
|
111
|
+
* `pad` - fit scale image and pad image with background colour to given dimensions keeping aspect ratio
|
112
|
+
* `limit` - fit scale image to given dimensions if it is larger than that dimensions
|
102
113
|
|
103
114
|
### Supported formats
|
104
115
|
|
@@ -113,8 +124,10 @@ Width and height values are interpreted depending on operation.
|
|
113
124
|
### Thumbnail options
|
114
125
|
|
115
126
|
Following options can be used with thumbnail specification:
|
116
|
-
* quality - set output image quality; this is format specific: for JPEG 0 is maximum compression and 100 is maximum quality, for PNG first digit is zlib compression level and second one is filter level
|
117
|
-
* background-color - color in HTML notation or textual description ('red', 'green' etc.) used for background when processing transparent images or padding; by default white background is used
|
127
|
+
* `quality` - set output image quality; this is format specific: for JPEG 0 is maximum compression and 100 is maximum quality, for PNG first digit is zlib compression level and second one is filter level
|
128
|
+
* `background-color` - color in HTML notation or textual description ('red', 'green' etc.) used for background when processing transparent images or padding; by default white background is used
|
129
|
+
* `float-x` and `float-y` - value between 0.0 and 1.0; can be used with `crop` and `pad` operations to move cropping view or image over background left to right or top to bottom (0.0 to 1.0); both default to 0.5 centering the view or image
|
130
|
+
* `interlace` - one of `UndefinedInterlace`, `NoInterlace`, `LineInterlace`, `PlaneInterlace`, `PartitionInterlace`, `GIFInterlace`, `JPEGInterlace`, `PNGInterlace`; some formats support interlaced output format; use `JPEGInterlace` or `LineInterlace` or `PlaneInterlace` with `jpeg` format to produce progressive JPEG; defaults to `NoInterlace`
|
118
131
|
|
119
132
|
### API
|
120
133
|
|
@@ -252,6 +265,11 @@ $ curl 127.0.0.1:3100/stats/total_write_multipart
|
|
252
265
|
* When 413 error is reported due to memory limit exhaustion the disk offloading won't work any more and only requests that can fit in the memory can be processed without getting 413 - this is due to a bug in ImageMagick v6.8.6-8 (2013-08-06 6.8.6-8) or less
|
253
266
|
* Mime type generated for images may not be the official mime type assigned for given format; please let me know of any inconsistencies or send a patch to get better output in efficient way
|
254
267
|
|
268
|
+
## TODO
|
269
|
+
|
270
|
+
* Save input image data for images that has failed processing to allow further investigation
|
271
|
+
* Allow for specifying different quality values for different image types, e.g.: quality[jpeg]:97,quality[png]:85
|
272
|
+
|
255
273
|
## Contributing to HTTP Thumbnailer
|
256
274
|
|
257
275
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
data/bin/httpthumbnailer
CHANGED
@@ -30,25 +30,28 @@ Application.new('httpthumbnailer', port: 3100) do
|
|
30
30
|
puts
|
31
31
|
puts "Output formats:"
|
32
32
|
puts "\t#{Plugin::Thumbnailer::Service.output_formats.join(' ')}"
|
33
|
+
puts
|
34
|
+
puts "Supported Interlace:"
|
35
|
+
puts "\t#{Magick::InterlaceType.values.map(&:to_s).join(' ')}"
|
33
36
|
exit 0
|
34
37
|
end
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
Controller.settings[:optimization] = (not settings.no_optimization)
|
40
|
+
Controller.settings[:limit_memory] = settings.limit_memory * 1024**2
|
41
|
+
Controller.settings[:limit_map] = settings.limit_disk * 1024**2
|
42
|
+
Controller.settings[:limit_disk] = settings.limit_disk * 1024**2
|
40
43
|
end
|
41
44
|
|
42
45
|
main do |settings|
|
43
46
|
require 'httpthumbnailer/error_reporter'
|
44
47
|
require 'httpthumbnailer/thumbnailer'
|
45
48
|
|
46
|
-
class HTTPThumbniler <
|
49
|
+
class HTTPThumbniler < Controller
|
47
50
|
extend Stats
|
48
51
|
def_stats(
|
49
52
|
:workers,
|
50
|
-
:total_requests,
|
51
|
-
:total_errors
|
53
|
+
:total_requests,
|
54
|
+
:total_errors
|
52
55
|
)
|
53
56
|
|
54
57
|
raindrops_stats = Raindrops::Middleware::Stats.new
|
@@ -65,7 +68,7 @@ Application.new('httpthumbnailer', port: 3100) do
|
|
65
68
|
HTTPThumbniler.stats.incr_total_errors
|
66
69
|
run ErrorReporter
|
67
70
|
end
|
68
|
-
|
71
|
+
|
69
72
|
on 'stats' do
|
70
73
|
run StatsReporter
|
71
74
|
end
|
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 = "1.
|
8
|
+
s.version = "1.2.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 = "2014-07-28"
|
13
13
|
s.description = "Provides HTTP API for thumbnailing images"
|
14
14
|
s.email = "jpastuszek@gmail.com"
|
15
15
|
s.executables = ["httpthumbnailer"]
|
@@ -19,12 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
".document",
|
22
|
-
".idea/encodings.xml",
|
23
|
-
".idea/httpthumbnailer.iml",
|
24
|
-
".idea/misc.xml",
|
25
|
-
".idea/modules.xml",
|
26
|
-
".idea/scopes/scope_settings.xml",
|
27
|
-
".idea/vcs.xml",
|
28
22
|
".rspec",
|
29
23
|
"Gemfile",
|
30
24
|
"Gemfile.lock",
|
@@ -69,20 +63,24 @@ Gem::Specification.new do |s|
|
|
69
63
|
"load_test/v0.0.14-no-optimization.csv",
|
70
64
|
"load_test/v0.0.14.csv",
|
71
65
|
"spec/image_processing_spec.rb",
|
66
|
+
"spec/plugin_thumbnailer_spec.rb",
|
72
67
|
"spec/spec_helper.rb",
|
68
|
+
"spec/support/square_even.png",
|
69
|
+
"spec/support/square_odd.png",
|
70
|
+
"spec/support/test_image.rb",
|
73
71
|
"spec/thumbnail_specs_spec.rb"
|
74
72
|
]
|
75
73
|
s.homepage = "http://github.com/jpastuszek/httpthumbnailer"
|
76
74
|
s.licenses = ["MIT"]
|
77
75
|
s.require_paths = ["lib"]
|
78
|
-
s.rubygems_version = "1.8.
|
76
|
+
s.rubygems_version = "1.8.23"
|
79
77
|
s.summary = "HTTP thumbnailing server"
|
80
78
|
|
81
79
|
if s.respond_to? :specification_version then
|
82
80
|
s.specification_version = 3
|
83
81
|
|
84
82
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
85
|
-
s.add_runtime_dependency(%q<unicorn-cuba-base>, ["~> 1.
|
83
|
+
s.add_runtime_dependency(%q<unicorn-cuba-base>, ["~> 1.2.0"])
|
86
84
|
s.add_runtime_dependency(%q<rmagick>, ["~> 2"])
|
87
85
|
s.add_development_dependency(%q<rspec>, ["~> 2.13"])
|
88
86
|
s.add_development_dependency(%q<rspec-mocks>, ["~> 2.13"])
|
@@ -94,7 +92,7 @@ Gem::Specification.new do |s|
|
|
94
92
|
s.add_development_dependency(%q<multipart-parser>, ["~> 0.1.1"])
|
95
93
|
s.add_development_dependency(%q<daemon>, ["~> 1.1"])
|
96
94
|
else
|
97
|
-
s.add_dependency(%q<unicorn-cuba-base>, ["~> 1.
|
95
|
+
s.add_dependency(%q<unicorn-cuba-base>, ["~> 1.2.0"])
|
98
96
|
s.add_dependency(%q<rmagick>, ["~> 2"])
|
99
97
|
s.add_dependency(%q<rspec>, ["~> 2.13"])
|
100
98
|
s.add_dependency(%q<rspec-mocks>, ["~> 2.13"])
|
@@ -107,7 +105,7 @@ Gem::Specification.new do |s|
|
|
107
105
|
s.add_dependency(%q<daemon>, ["~> 1.1"])
|
108
106
|
end
|
109
107
|
else
|
110
|
-
s.add_dependency(%q<unicorn-cuba-base>, ["~> 1.
|
108
|
+
s.add_dependency(%q<unicorn-cuba-base>, ["~> 1.2.0"])
|
111
109
|
s.add_dependency(%q<rmagick>, ["~> 2"])
|
112
110
|
s.add_dependency(%q<rspec>, ["~> 2.13"])
|
113
111
|
s.add_dependency(%q<rspec-mocks>, ["~> 2.13"])
|
@@ -1,9 +1,6 @@
|
|
1
1
|
require 'RMagick'
|
2
2
|
require 'forwardable'
|
3
3
|
|
4
|
-
# ImageMagick Image.mime_type is absolutely bunkers! It goes over file system to look for some strange files WTF?!
|
5
|
-
# Also it cannot be used for thumbnails since they are not yet rendered to desired format
|
6
|
-
# Here is stupid implementaiton
|
7
4
|
module MetaData
|
8
5
|
def width
|
9
6
|
@image.columns
|
@@ -13,6 +10,9 @@ module MetaData
|
|
13
10
|
@image.rows
|
14
11
|
end
|
15
12
|
|
13
|
+
# ImageMagick Image.mime_type is absolutely bunkers! It goes over file system to look for some strange files WTF?!
|
14
|
+
# Also it cannot be used for thumbnails since they are not yet rendered to desired format
|
15
|
+
# Here is stupid implementation
|
16
16
|
def mime_type
|
17
17
|
#TODO: how do I do it better?
|
18
18
|
format = @format || @image.format
|
@@ -94,7 +94,7 @@ module Plugin
|
|
94
94
|
|
95
95
|
def thumbnail(spec)
|
96
96
|
spec = spec.dup
|
97
|
-
# default
|
97
|
+
# default background is white
|
98
98
|
spec.options['background-color'] = spec.options.fetch('background-color', 'white').sub(/^0x/, '#')
|
99
99
|
|
100
100
|
width = spec.width == :input ? @image.columns : spec.width
|
@@ -134,7 +134,7 @@ module Plugin
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
-
def_delegators :@image, :destroy!, :destroyed
|
137
|
+
def_delegators :@image, :destroy!, :destroyed?, :format
|
138
138
|
|
139
139
|
include MetaData
|
140
140
|
|
@@ -155,20 +155,32 @@ module Plugin
|
|
155
155
|
|
156
156
|
class Thumbnail
|
157
157
|
include ClassLogging
|
158
|
+
extend Forwardable
|
158
159
|
|
159
160
|
def initialize(image, format, options = {})
|
160
161
|
@image = image
|
161
162
|
@format = format
|
163
|
+
|
162
164
|
@quality = (options['quality'] or default_quality(format))
|
163
165
|
@quality &&= @quality.to_i
|
166
|
+
|
167
|
+
@interlace = (options['interlace'] or 'NoInterlace')
|
168
|
+
fail "unsupported interlace: #{@interlace}" unless Magick::InterlaceType.values.map(&:to_s).include? @interlace
|
169
|
+
@interlace = Magick.const_get @interlace.to_sym
|
164
170
|
end
|
165
171
|
|
172
|
+
def_delegators :@image, :format
|
173
|
+
|
166
174
|
def data
|
175
|
+
# export class variables to local scope
|
167
176
|
format = @format
|
168
177
|
quality = @quality
|
178
|
+
interlace = @interlace
|
179
|
+
|
169
180
|
@image.to_blob do
|
170
181
|
self.format = format
|
171
182
|
self.quality = quality if quality
|
183
|
+
self.interlace = interlace
|
172
184
|
end
|
173
185
|
end
|
174
186
|
|
@@ -359,6 +371,26 @@ module Plugin
|
|
359
371
|
log.info "changed #{limit} limit from #{old} to #{value} bytes"
|
360
372
|
old
|
361
373
|
end
|
374
|
+
|
375
|
+
def setup_default_methods
|
376
|
+
processing_method('crop') do |image, width, height, options|
|
377
|
+
image.resize_to_fill(width, height, (Float(options['float-x']) rescue 0.5), (Float(options['float-y']) rescue 0.5)) if image.columns != width or image.rows != height
|
378
|
+
end
|
379
|
+
|
380
|
+
processing_method('fit') do |image, width, height, options|
|
381
|
+
image.resize_to_fit(width, height) if image.columns != width or image.rows != height
|
382
|
+
end
|
383
|
+
|
384
|
+
processing_method('pad') do |image, width, height, options|
|
385
|
+
image.resize_to_fit(width, height).replace do |resize|
|
386
|
+
resize.render_on_background(options['background-color'], width, height, (Float(options['float-x']) rescue 0.5), (Float(options['float-y']) rescue 0.5))
|
387
|
+
end if image.columns != width or image.rows != height
|
388
|
+
end
|
389
|
+
|
390
|
+
processing_method('limit') do |image, width, height, options|
|
391
|
+
image.resize_to_fit(width, height) if image.columns > width or image.rows > height
|
392
|
+
end
|
393
|
+
end
|
362
394
|
end
|
363
395
|
|
364
396
|
def self.setup(app)
|
@@ -372,23 +404,7 @@ module Plugin
|
|
372
404
|
limit_disk: app.settings[:limit_disk]
|
373
405
|
)
|
374
406
|
|
375
|
-
@@service.
|
376
|
-
image.resize_to_fill(width, height) if image.columns != width or image.rows != height
|
377
|
-
end
|
378
|
-
|
379
|
-
@@service.processing_method('fit') do |image, width, height, options|
|
380
|
-
image.resize_to_fit(width, height) if image.columns != width or image.rows != height
|
381
|
-
end
|
382
|
-
|
383
|
-
@@service.processing_method('pad') do |image, width, height, options|
|
384
|
-
image.resize_to_fit(width, height).replace do |resize|
|
385
|
-
resize.render_on_background(options['background-color'], width, height)
|
386
|
-
end if image.columns != width or image.rows != height
|
387
|
-
end
|
388
|
-
|
389
|
-
@@service.processing_method('limit') do |image, width, height, options|
|
390
|
-
image.resize_to_fit(width, height) if image.columns > width or image.rows > height
|
391
|
-
end
|
407
|
+
@@service.setup_default_methods
|
392
408
|
end
|
393
409
|
|
394
410
|
def thumbnailer
|
@@ -400,8 +416,16 @@ end
|
|
400
416
|
class Magick::Image
|
401
417
|
include Plugin::Thumbnailer::ImageProcessing
|
402
418
|
|
403
|
-
def render_on_background(background_color, width = nil, height = nil)
|
404
|
-
|
419
|
+
def render_on_background(background_color, width = nil, height = nil, float_x = 0.5, float_y = 0.5)
|
420
|
+
# default to image size
|
421
|
+
width ||= self.columns
|
422
|
+
height ||= self.rows
|
423
|
+
|
424
|
+
# make sure we have enough background to fit image on top of it
|
425
|
+
width = self.columns if width < self.columns
|
426
|
+
height = self.rows if height < self.rows
|
427
|
+
|
428
|
+
Magick::Image.new(width, height) {
|
405
429
|
begin
|
406
430
|
self.background_color = background_color
|
407
431
|
rescue ArgumentError
|
@@ -409,20 +433,22 @@ class Magick::Image
|
|
409
433
|
end
|
410
434
|
self.depth = 8
|
411
435
|
}.replace do |background|
|
412
|
-
background.composite!(self,
|
436
|
+
background.composite!(self, *background.float_to_offset(self.columns, self.rows, float_x, float_y), Magick::OverCompositeOp)
|
413
437
|
end
|
414
438
|
end
|
415
439
|
|
416
440
|
# non coping version
|
417
|
-
def resize_to_fill(
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
441
|
+
def resize_to_fill(width, height = nil, float_x = 0.5, float_y = 0.5)
|
442
|
+
# default to square
|
443
|
+
height ||= width
|
444
|
+
|
445
|
+
return if width == columns and height == rows
|
446
|
+
|
447
|
+
scale = [width / columns.to_f, height / rows.to_f].max
|
448
|
+
|
449
|
+
resize((scale * columns).ceil, (scale * rows).ceil).replace do |image|
|
450
|
+
next if width == image.columns and height == image.rows
|
451
|
+
image.crop(*image.float_to_offset(width, height, float_x, float_y), width, height, true)
|
426
452
|
end
|
427
453
|
end
|
428
454
|
|
@@ -438,5 +464,21 @@ class Magick::Image
|
|
438
464
|
factor
|
439
465
|
end
|
440
466
|
end
|
467
|
+
|
468
|
+
def float_to_offset(float_width, float_height, float_x = 0.5, float_y = 0.5)
|
469
|
+
base_width = self.columns
|
470
|
+
base_height = self.rows
|
471
|
+
|
472
|
+
x = ((base_width - float_width) * float_x).ceil
|
473
|
+
y = ((base_height - float_height) * float_y).ceil
|
474
|
+
|
475
|
+
x = 0 if x < 0
|
476
|
+
x = (base_width - float_width) if x > (base_width - float_width)
|
477
|
+
|
478
|
+
y = 0 if y < 0
|
479
|
+
y = (base_height - float_height) if y > (base_height - float_height)
|
480
|
+
|
481
|
+
[x, y]
|
482
|
+
end
|
441
483
|
end
|
442
484
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'httpthumbnailer/plugin/thumbnailer'
|
2
2
|
require 'httpthumbnailer/thumbnail_specs'
|
3
3
|
|
4
|
-
class Thumbnailer <
|
4
|
+
class Thumbnailer < Controller
|
5
5
|
self.plugin Plugin::Thumbnailer
|
6
6
|
|
7
7
|
self.define do
|
@@ -22,7 +22,7 @@ class Thumbnailer < Controler
|
|
22
22
|
|
23
23
|
log.info "generating thumbnail: #{spec}"
|
24
24
|
input_image.thumbnail(spec) do |image|
|
25
|
-
write 200, image.mime_type, image.data,
|
25
|
+
write 200, image.mime_type, image.data,
|
26
26
|
"X-Image-Width" => image.width,
|
27
27
|
"X-Image-Height" => image.height,
|
28
28
|
"X-Input-Image-Mime-Type" => input_image.mime_type,
|
@@ -43,7 +43,7 @@ class Thumbnailer < Controler
|
|
43
43
|
|
44
44
|
thumbnailer.load(req.body, opts).use do |input_image|
|
45
45
|
log.info "original image loaded: #{input_image.mime_type}"
|
46
|
-
write_preamble 200,
|
46
|
+
write_preamble 200,
|
47
47
|
"X-Input-Image-Mime-Type" => input_image.mime_type,
|
48
48
|
"X-Input-Image-Width" => input_image.width,
|
49
49
|
"X-Input-Image-Height" => input_image.height
|
@@ -2,7 +2,7 @@ require_relative 'spec_helper'
|
|
2
2
|
require 'unicorn-cuba-base'
|
3
3
|
require 'httpthumbnailer/plugin/thumbnailer'
|
4
4
|
|
5
|
-
class
|
5
|
+
class StubImage
|
6
6
|
include Plugin::Thumbnailer::ImageProcessing
|
7
7
|
|
8
8
|
@@created = 0
|
@@ -26,7 +26,7 @@ class TestImage
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def copy
|
29
|
-
|
29
|
+
StubImage.new
|
30
30
|
end
|
31
31
|
|
32
32
|
def destroyed?
|
@@ -45,7 +45,7 @@ end
|
|
45
45
|
|
46
46
|
describe 'image processing module' do
|
47
47
|
it '#replace will return new or original image makng sure that all other images are destroyed' do
|
48
|
-
image =
|
48
|
+
image = StubImage.new.replace do |image|
|
49
49
|
image.copy.replace do |image|
|
50
50
|
image.final!
|
51
51
|
end
|
@@ -53,10 +53,10 @@ describe 'image processing module' do
|
|
53
53
|
image.should be_final
|
54
54
|
image.should_not be_destroyed
|
55
55
|
|
56
|
-
|
56
|
+
StubImage.alive.should == 1
|
57
57
|
image.destroy!
|
58
58
|
|
59
|
-
image =
|
59
|
+
image = StubImage.new.replace do |image|
|
60
60
|
image.copy.replace do |image|
|
61
61
|
image.copy.replace do |image|
|
62
62
|
image.copy
|
@@ -68,10 +68,10 @@ describe 'image processing module' do
|
|
68
68
|
image.should be_final
|
69
69
|
image.should_not be_destroyed
|
70
70
|
|
71
|
-
|
71
|
+
StubImage.alive.should == 1
|
72
72
|
image.destroy!
|
73
73
|
|
74
|
-
image =
|
74
|
+
image = StubImage.new.replace do |image|
|
75
75
|
image.copy.replace do |image|
|
76
76
|
image = image.copy.replace do |image|
|
77
77
|
image.copy
|
@@ -86,13 +86,13 @@ describe 'image processing module' do
|
|
86
86
|
image.should be_final
|
87
87
|
image.should_not be_destroyed
|
88
88
|
|
89
|
-
|
89
|
+
StubImage.alive.should == 1
|
90
90
|
image.destroy!
|
91
91
|
end
|
92
92
|
|
93
93
|
it '#replace will destroy created images on exception' do
|
94
94
|
lambda {
|
95
|
-
image =
|
95
|
+
image = StubImage.new.replace do |image|
|
96
96
|
image.copy.replace do |image|
|
97
97
|
image.copy.replace do |image|
|
98
98
|
image.copy.replace do |image|
|
@@ -103,10 +103,10 @@ describe 'image processing module' do
|
|
103
103
|
end
|
104
104
|
}.should raise_error(RuntimeError, 'test')
|
105
105
|
|
106
|
-
|
106
|
+
StubImage.alive.should == 0
|
107
107
|
|
108
108
|
lambda {
|
109
|
-
image =
|
109
|
+
image = StubImage.new.replace do |image|
|
110
110
|
image.copy.replace do |image|
|
111
111
|
image.copy.replace do |image|
|
112
112
|
image.copy
|
@@ -118,11 +118,11 @@ describe 'image processing module' do
|
|
118
118
|
end
|
119
119
|
}.should raise_error(RuntimeError, 'test')
|
120
120
|
|
121
|
-
|
121
|
+
StubImage.alive.should == 0
|
122
122
|
end
|
123
123
|
|
124
124
|
it '#use should return image to be used for multiple processing and destroy it at the end' do
|
125
|
-
image =
|
125
|
+
image = StubImage.new.use do |image|
|
126
126
|
i1 = image.replace do |image|
|
127
127
|
image.copy.final!
|
128
128
|
end
|
@@ -142,7 +142,7 @@ describe 'image processing module' do
|
|
142
142
|
end
|
143
143
|
image.should be_destroyed
|
144
144
|
|
145
|
-
|
145
|
+
StubImage.alive.should == 0
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
@@ -0,0 +1,318 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'unicorn-cuba-base'
|
3
|
+
require 'httpthumbnailer/plugin/thumbnailer'
|
4
|
+
require 'httpthumbnailer/thumbnail_specs'
|
5
|
+
|
6
|
+
describe Plugin::Thumbnailer::Service do
|
7
|
+
subject do
|
8
|
+
service = Plugin::Thumbnailer::Service.new
|
9
|
+
service.setup_default_methods
|
10
|
+
service
|
11
|
+
end
|
12
|
+
|
13
|
+
def square_odd
|
14
|
+
subject.load(TestImage.io('square_odd.png'))
|
15
|
+
end
|
16
|
+
|
17
|
+
def square_even
|
18
|
+
subject.load(TestImage.io('square_even.png'))
|
19
|
+
end
|
20
|
+
|
21
|
+
before :each do
|
22
|
+
@before_stats = Plugin::Thumbnailer::Service.stats.to_hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def diff_stat(name)
|
26
|
+
Plugin::Thumbnailer::Service.stats.to_hash[name] - @before_stats[name]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should load images from provided blob" do
|
30
|
+
image = subject.load(TestImage.io('square_odd.png'))
|
31
|
+
image.format.should == 'PNG'
|
32
|
+
image.width.should == 99
|
33
|
+
image.height.should == 99
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'encoding' do
|
37
|
+
describe 'interlace' do
|
38
|
+
it 'should fail if unknown interlace is specified' do
|
39
|
+
expect {
|
40
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg,interlace:Blah')) do |thumbnail|
|
41
|
+
end
|
42
|
+
}.to raise_error RuntimeError, 'unsupported interlace: Blah'
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should allow to construct progressive JPEG with interlace JPEGInterlace or LineInterlace or PlaneInerlace' do
|
46
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg')) do |thumbnail|
|
47
|
+
image = Magick::Image.from_blob(thumbnail.data).first
|
48
|
+
image.columns.should == 99
|
49
|
+
image.rows.should == 99
|
50
|
+
image.interlace.to_s.should == 'NoInterlace'
|
51
|
+
image.destroy!
|
52
|
+
end
|
53
|
+
|
54
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg,interlace:JPEGInterlace')) do |thumbnail|
|
55
|
+
image = Magick::Image.from_blob(thumbnail.data).first
|
56
|
+
image.columns.should == 99
|
57
|
+
image.rows.should == 99
|
58
|
+
image.interlace.to_s.should == 'JPEGInterlace'
|
59
|
+
image.destroy!
|
60
|
+
end
|
61
|
+
|
62
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg,interlace:LineInterlace')) do |thumbnail|
|
63
|
+
image = Magick::Image.from_blob(thumbnail.data).first
|
64
|
+
image.columns.should == 99
|
65
|
+
image.rows.should == 99
|
66
|
+
image.interlace.to_s.should == 'JPEGInterlace'
|
67
|
+
image.destroy!
|
68
|
+
end
|
69
|
+
|
70
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg,interlace:PlaneInterlace')) do |thumbnail|
|
71
|
+
image = Magick::Image.from_blob(thumbnail.data).first
|
72
|
+
image.columns.should == 99
|
73
|
+
image.rows.should == 99
|
74
|
+
image.interlace.to_s.should == 'JPEGInterlace'
|
75
|
+
image.destroy!
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'thumbnailing' do
|
82
|
+
describe 'cropping' do
|
83
|
+
it 'should be a noop if same width and height are used as original image' do
|
84
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,png')) do |thumbnail|
|
85
|
+
thumbnail.width.should == 99
|
86
|
+
thumbnail.height.should == 99
|
87
|
+
end
|
88
|
+
diff_stat(:total_images_created_resize).should == 0
|
89
|
+
diff_stat(:total_images_created_crop).should == 0
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should be a resize operation if same proportions are used as original image' do
|
93
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,50,50,png')) do |thumbnail|
|
94
|
+
thumbnail.width.should == 50
|
95
|
+
thumbnail.height.should == 50
|
96
|
+
end
|
97
|
+
diff_stat(:total_images_created_resize).should == 1
|
98
|
+
diff_stat(:total_images_created_crop).should == 0
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should be a resize and crop operation if different proportions are used as original image' do
|
102
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,50,60,png')) do |thumbnail|
|
103
|
+
thumbnail.width.should == 50
|
104
|
+
thumbnail.height.should == 60
|
105
|
+
end
|
106
|
+
diff_stat(:total_images_created_resize).should == 1
|
107
|
+
diff_stat(:total_images_created_crop).should == 1
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'should crop to even and odd proportions' do
|
111
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,33,33,png')) do |thumbnail|
|
112
|
+
thumbnail.width.should == 33
|
113
|
+
thumbnail.height.should == 33
|
114
|
+
end
|
115
|
+
|
116
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,91,91,png')) do |thumbnail|
|
117
|
+
thumbnail.width.should == 91
|
118
|
+
thumbnail.height.should == 91
|
119
|
+
end
|
120
|
+
|
121
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,33,50,png')) do |thumbnail|
|
122
|
+
thumbnail.width.should == 33
|
123
|
+
thumbnail.height.should == 50
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should crop with upwards scaling' do
|
128
|
+
square_odd.thumbnail(ThumbnailSpec.from_uri('crop,198,99,png')) do |thumbnail|
|
129
|
+
thumbnail.width.should == 198
|
130
|
+
thumbnail.height.should == 99
|
131
|
+
#show_blob thumbnail.data
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'floating' do
|
136
|
+
it 'should crop with floating horizontally' do
|
137
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png')) do |thumbnail|
|
138
|
+
thumbnail.width.should == 100
|
139
|
+
thumbnail.height.should == 200
|
140
|
+
#show_blob thumbnail.data
|
141
|
+
end
|
142
|
+
|
143
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:1.0')) do |thumbnail|
|
144
|
+
thumbnail.width.should == 100
|
145
|
+
thumbnail.height.should == 200
|
146
|
+
#show_blob thumbnail.data
|
147
|
+
end
|
148
|
+
|
149
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:0.0')) do |thumbnail|
|
150
|
+
thumbnail.width.should == 100
|
151
|
+
thumbnail.height.should == 200
|
152
|
+
#show_blob thumbnail.data
|
153
|
+
end
|
154
|
+
|
155
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:0.8')) do |thumbnail|
|
156
|
+
thumbnail.width.should == 100
|
157
|
+
thumbnail.height.should == 200
|
158
|
+
#show_blob thumbnail.data
|
159
|
+
end
|
160
|
+
|
161
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:-9.8')) do |thumbnail|
|
162
|
+
thumbnail.width.should == 100
|
163
|
+
thumbnail.height.should == 200
|
164
|
+
#show_blob thumbnail.data
|
165
|
+
end
|
166
|
+
|
167
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:3')) do |thumbnail|
|
168
|
+
thumbnail.width.should == 100
|
169
|
+
thumbnail.height.should == 200
|
170
|
+
#show_blob thumbnail.data
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'should crop with floating vertically' do
|
175
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png')) do |thumbnail|
|
176
|
+
thumbnail.width.should == 200
|
177
|
+
thumbnail.height.should == 100
|
178
|
+
#show_blob thumbnail.data
|
179
|
+
end
|
180
|
+
|
181
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:1.0')) do |thumbnail|
|
182
|
+
thumbnail.width.should == 200
|
183
|
+
thumbnail.height.should == 100
|
184
|
+
#show_blob thumbnail.data
|
185
|
+
end
|
186
|
+
|
187
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:0.0')) do |thumbnail|
|
188
|
+
thumbnail.width.should == 200
|
189
|
+
thumbnail.height.should == 100
|
190
|
+
#show_blob thumbnail.data
|
191
|
+
end
|
192
|
+
|
193
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:0.8')) do |thumbnail|
|
194
|
+
thumbnail.width.should == 200
|
195
|
+
thumbnail.height.should == 100
|
196
|
+
#show_blob thumbnail.data
|
197
|
+
end
|
198
|
+
|
199
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:-9.8')) do |thumbnail|
|
200
|
+
thumbnail.width.should == 200
|
201
|
+
thumbnail.height.should == 100
|
202
|
+
#show_blob thumbnail.data
|
203
|
+
end
|
204
|
+
|
205
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:3')) do |thumbnail|
|
206
|
+
thumbnail.width.should == 200
|
207
|
+
thumbnail.height.should == 100
|
208
|
+
#show_blob thumbnail.data
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should crop with floating horizontally and vertically' do
|
213
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:0.25,float-y:0.25')) do |thumbnail|
|
214
|
+
thumbnail.width.should == 100
|
215
|
+
thumbnail.height.should == 200
|
216
|
+
#show_blob thumbnail.data
|
217
|
+
end
|
218
|
+
|
219
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-x:0.25,float-y:0.25')) do |thumbnail|
|
220
|
+
thumbnail.width.should == 200
|
221
|
+
thumbnail.height.should == 100
|
222
|
+
#show_blob thumbnail.data
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'should pad with floating horizontaly' do
|
227
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png')) do |thumbnail|
|
228
|
+
thumbnail.width.should == 200
|
229
|
+
thumbnail.height.should == 100
|
230
|
+
#show_blob thumbnail.data
|
231
|
+
end
|
232
|
+
|
233
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:1.0')) do |thumbnail|
|
234
|
+
thumbnail.width.should == 200
|
235
|
+
thumbnail.height.should == 100
|
236
|
+
#show_blob thumbnail.data
|
237
|
+
end
|
238
|
+
|
239
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:0.0')) do |thumbnail|
|
240
|
+
thumbnail.width.should == 200
|
241
|
+
thumbnail.height.should == 100
|
242
|
+
#show_blob thumbnail.data
|
243
|
+
end
|
244
|
+
|
245
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:0.8')) do |thumbnail|
|
246
|
+
thumbnail.width.should == 200
|
247
|
+
thumbnail.height.should == 100
|
248
|
+
#show_blob thumbnail.data
|
249
|
+
end
|
250
|
+
|
251
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:-9.8')) do |thumbnail|
|
252
|
+
thumbnail.width.should == 200
|
253
|
+
thumbnail.height.should == 100
|
254
|
+
#show_blob thumbnail.data
|
255
|
+
end
|
256
|
+
|
257
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:3')) do |thumbnail|
|
258
|
+
thumbnail.width.should == 200
|
259
|
+
thumbnail.height.should == 100
|
260
|
+
#show_blob thumbnail.data
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'should pad with floating verticaly' do
|
265
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png')) do |thumbnail|
|
266
|
+
thumbnail.width.should == 100
|
267
|
+
thumbnail.height.should == 200
|
268
|
+
#show_blob thumbnail.data
|
269
|
+
end
|
270
|
+
|
271
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:1.0')) do |thumbnail|
|
272
|
+
thumbnail.width.should == 100
|
273
|
+
thumbnail.height.should == 200
|
274
|
+
#show_blob thumbnail.data
|
275
|
+
end
|
276
|
+
|
277
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:0.0')) do |thumbnail|
|
278
|
+
thumbnail.width.should == 100
|
279
|
+
thumbnail.height.should == 200
|
280
|
+
#show_blob thumbnail.data
|
281
|
+
end
|
282
|
+
|
283
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:0.8')) do |thumbnail|
|
284
|
+
thumbnail.width.should == 100
|
285
|
+
thumbnail.height.should == 200
|
286
|
+
#show_blob thumbnail.data
|
287
|
+
end
|
288
|
+
|
289
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:-9.8')) do |thumbnail|
|
290
|
+
thumbnail.width.should == 100
|
291
|
+
thumbnail.height.should == 200
|
292
|
+
#show_blob thumbnail.data
|
293
|
+
end
|
294
|
+
|
295
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:3')) do |thumbnail|
|
296
|
+
thumbnail.width.should == 100
|
297
|
+
thumbnail.height.should == 200
|
298
|
+
#show_blob thumbnail.data
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'should pad with floating horizontally and vertically' do
|
303
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-x:0.25,float-y:0.25')) do |thumbnail|
|
304
|
+
thumbnail.width.should == 100
|
305
|
+
thumbnail.height.should == 200
|
306
|
+
#show_blob thumbnail.data
|
307
|
+
end
|
308
|
+
|
309
|
+
square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:0.25,float-y:0.25')) do |thumbnail|
|
310
|
+
thumbnail.width.should == 200
|
311
|
+
thumbnail.height.should == 100
|
312
|
+
#show_blob thumbnail.data
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module TestImage
|
4
|
+
def self.io(name)
|
5
|
+
File.open(Pathname.new(__FILE__).dirname + name)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def show_blob(blob)
|
10
|
+
t = Tempfile.new('blob')
|
11
|
+
t.write(blob)
|
12
|
+
t.close
|
13
|
+
`display '#{t.path}'`
|
14
|
+
#pid = Process.spawn "display '#{t.path}'"
|
15
|
+
#Process.detach pid
|
16
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpthumbnailer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-07-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: unicorn-cuba-base
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 1.
|
21
|
+
version: 1.2.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 1.
|
29
|
+
version: 1.2.0
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: rmagick
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -197,12 +197,6 @@ extra_rdoc_files:
|
|
197
197
|
- README.md
|
198
198
|
files:
|
199
199
|
- .document
|
200
|
-
- .idea/encodings.xml
|
201
|
-
- .idea/httpthumbnailer.iml
|
202
|
-
- .idea/misc.xml
|
203
|
-
- .idea/modules.xml
|
204
|
-
- .idea/scopes/scope_settings.xml
|
205
|
-
- .idea/vcs.xml
|
206
200
|
- .rspec
|
207
201
|
- Gemfile
|
208
202
|
- Gemfile.lock
|
@@ -247,7 +241,11 @@ files:
|
|
247
241
|
- load_test/v0.0.14-no-optimization.csv
|
248
242
|
- load_test/v0.0.14.csv
|
249
243
|
- spec/image_processing_spec.rb
|
244
|
+
- spec/plugin_thumbnailer_spec.rb
|
250
245
|
- spec/spec_helper.rb
|
246
|
+
- spec/support/square_even.png
|
247
|
+
- spec/support/square_odd.png
|
248
|
+
- spec/support/test_image.rb
|
251
249
|
- spec/thumbnail_specs_spec.rb
|
252
250
|
homepage: http://github.com/jpastuszek/httpthumbnailer
|
253
251
|
licenses:
|
@@ -264,7 +262,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
264
262
|
version: '0'
|
265
263
|
segments:
|
266
264
|
- 0
|
267
|
-
hash:
|
265
|
+
hash: -2315585188832359526
|
268
266
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
269
267
|
none: false
|
270
268
|
requirements:
|
@@ -273,7 +271,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
273
271
|
version: '0'
|
274
272
|
requirements: []
|
275
273
|
rubyforge_project:
|
276
|
-
rubygems_version: 1.8.
|
274
|
+
rubygems_version: 1.8.23
|
277
275
|
signing_key:
|
278
276
|
specification_version: 3
|
279
277
|
summary: HTTP thumbnailing server
|
data/.idea/encodings.xml
DELETED
data/.idea/httpthumbnailer.iml
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<module type="RUBY_MODULE" version="4">
|
3
|
-
<component name="FacetManager">
|
4
|
-
<facet type="gem" name="Gem">
|
5
|
-
<configuration>
|
6
|
-
<option name="GEM_APP_ROOT_PATH" value="$MODULE_DIR$" />
|
7
|
-
<option name="GEM_APP_TEST_PATH" value="" />
|
8
|
-
<option name="GEM_APP_LIB_PATH" value="$MODULE_DIR$/lib" />
|
9
|
-
</configuration>
|
10
|
-
</facet>
|
11
|
-
</component>
|
12
|
-
<component name="NewModuleRootManager">
|
13
|
-
<content url="file://$MODULE_DIR$" />
|
14
|
-
<orderEntry type="inheritedJdk" />
|
15
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
16
|
-
<orderEntry type="library" scope="PROVIDED" name="builder (v3.2.0, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
17
|
-
<orderEntry type="library" scope="PROVIDED" name="bundler (v1.3.5, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
18
|
-
<orderEntry type="library" scope="PROVIDED" name="capybara (v1.1.4, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
19
|
-
<orderEntry type="library" scope="PROVIDED" name="childprocess (v0.3.9, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
20
|
-
<orderEntry type="library" scope="PROVIDED" name="cli (v1.3.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
21
|
-
<orderEntry type="library" scope="PROVIDED" name="cuba (v3.1.0, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
22
|
-
<orderEntry type="library" scope="PROVIDED" name="cucumber (v1.3.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
23
|
-
<orderEntry type="library" scope="PROVIDED" name="daemon (v1.1.0, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
24
|
-
<orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.2.4, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
25
|
-
<orderEntry type="library" scope="PROVIDED" name="facter (v1.6.18, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
26
|
-
<orderEntry type="library" scope="PROVIDED" name="ffi (v1.8.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
27
|
-
<orderEntry type="library" scope="PROVIDED" name="gherkin (v2.12.0, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
28
|
-
<orderEntry type="library" scope="PROVIDED" name="git (v1.2.5, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
29
|
-
<orderEntry type="library" scope="PROVIDED" name="httpclient (v2.3.3, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
30
|
-
<orderEntry type="library" scope="PROVIDED" name="jeweler (v1.8.4, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
31
|
-
<orderEntry type="library" scope="PROVIDED" name="json (v1.7.7, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
32
|
-
<orderEntry type="library" scope="PROVIDED" name="kgio (v2.8.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
33
|
-
<orderEntry type="library" scope="PROVIDED" name="mime-types (v1.23, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
34
|
-
<orderEntry type="library" scope="PROVIDED" name="multi_json (v1.7.3, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
35
|
-
<orderEntry type="library" scope="PROVIDED" name="multipart-parser (v0.1.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
36
|
-
<orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.5.9, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
37
|
-
<orderEntry type="library" scope="PROVIDED" name="rack (v1.5.2, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
38
|
-
<orderEntry type="library" scope="PROVIDED" name="rack-test (v0.6.2, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
39
|
-
<orderEntry type="library" scope="PROVIDED" name="raindrops (v0.12.0, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
40
|
-
<orderEntry type="library" scope="PROVIDED" name="rake (v10.0.4, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
41
|
-
<orderEntry type="library" scope="PROVIDED" name="rdoc (v3.12.2, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
42
|
-
<orderEntry type="library" scope="PROVIDED" name="rmagick (v2.13.2, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
43
|
-
<orderEntry type="library" scope="PROVIDED" name="rspec (v2.13.0, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
44
|
-
<orderEntry type="library" scope="PROVIDED" name="rspec-core (v2.13.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
45
|
-
<orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v2.13.0, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
46
|
-
<orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v2.13.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
47
|
-
<orderEntry type="library" scope="PROVIDED" name="ruby-ip (v0.9.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
48
|
-
<orderEntry type="library" scope="PROVIDED" name="rubyzip (v0.9.9, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
49
|
-
<orderEntry type="library" scope="PROVIDED" name="selenium-webdriver (v2.32.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
50
|
-
<orderEntry type="library" scope="PROVIDED" name="unicorn (v4.6.3, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
51
|
-
<orderEntry type="library" scope="PROVIDED" name="unicorn-cuba-base (v1.1.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
52
|
-
<orderEntry type="library" scope="PROVIDED" name="websocket (v1.0.7, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
53
|
-
<orderEntry type="library" scope="PROVIDED" name="xpath (v0.1.4, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
54
|
-
</component>
|
55
|
-
</module>
|
56
|
-
|
data/.idea/misc.xml
DELETED
data/.idea/modules.xml
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<project version="4">
|
3
|
-
<component name="ProjectModuleManager">
|
4
|
-
<modules>
|
5
|
-
<module fileurl="file://$PROJECT_DIR$/.idea/httpthumbnailer.iml" filepath="$PROJECT_DIR$/.idea/httpthumbnailer.iml" />
|
6
|
-
</modules>
|
7
|
-
</component>
|
8
|
-
</project>
|
9
|
-
|