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 CHANGED
@@ -1,7 +1,8 @@
1
1
  source "http://rubygems.org"
2
2
  ruby "1.9.3"
3
3
 
4
- gem "unicorn-cuba-base", "~> 1.1.1"
4
+ gem "unicorn-cuba-base", "~> 1.2.0"
5
+ #gem "unicorn-cuba-base", path: "../unicorn-cuba-base"
5
6
  gem "rmagick", "~> 2"
6
7
 
7
8
  # Add dependencies to develop your gem here.
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.0)
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.8.1)
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.12.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.6.3)
92
+ unicorn (4.8.2)
93
93
  kgio (~> 2.6)
94
94
  rack
95
95
  raindrops (~> 0.7)
96
- unicorn-cuba-base (1.1.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.1.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.2
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
- Controler.settings[:optimization] = (not settings.no_optimization)
37
- Controler.settings[:limit_memory] = settings.limit_memory * 1024**2
38
- Controler.settings[:limit_map] = settings.limit_disk * 1024**2
39
- Controler.settings[:limit_disk] = settings.limit_disk * 1024**2
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 < Controler
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
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "httpthumbnailer"
8
- s.version = "1.1.2"
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 = "2013-11-04"
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.25"
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.1.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.1.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.1.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,4 +1,4 @@
1
- class ErrorReporter < Controler
1
+ class ErrorReporter < Controller
2
2
  self.define do
3
3
  on error Plugin::Thumbnailer::UnsupportedMediaTypeError do |error|
4
4
  write_error 415, error
@@ -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 backgraud is white
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.processing_method('crop') do |image, width, height, options|
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
- Magick::Image.new(width || self.columns, height || self.rows) {
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, Magick::CenterGravity, Magick::OverCompositeOp)
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(ncols, nrows = nil, gravity = Magick::CenterGravity)
418
- nrows ||= ncols
419
- if ncols != columns or nrows != rows
420
- scale = [ncols / columns.to_f, nrows / rows.to_f].max
421
- resize(scale * columns + 0.5, scale * rows + 0.5).replace do |image|
422
- image.crop(gravity, ncols, nrows, true) if ncols != columns or nrows != rows
423
- end
424
- else
425
- crop(gravity, ncols, nrows, true) if ncols != columns or nrows != rows
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 < Controler
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 TestImage
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
- TestImage.new
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 = TestImage.new.replace do |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
- TestImage.alive.should == 1
56
+ StubImage.alive.should == 1
57
57
  image.destroy!
58
58
 
59
- image = TestImage.new.replace do |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
- TestImage.alive.should == 1
71
+ StubImage.alive.should == 1
72
72
  image.destroy!
73
73
 
74
- image = TestImage.new.replace do |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
- TestImage.alive.should == 1
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 = TestImage.new.replace do |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
- TestImage.alive.should == 0
106
+ StubImage.alive.should == 0
107
107
 
108
108
  lambda {
109
- image = TestImage.new.replace do |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
- TestImage.alive.should == 0
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 = TestImage.new.use do |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
- TestImage.alive.should == 0
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
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require_relative 'spec_helper'
2
2
  require 'httpthumbnailer/thumbnail_specs'
3
3
 
4
4
  describe ThumbnailSpecs do
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.1.2
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: 2013-11-04 00:00:00.000000000 Z
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.1.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.1.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: 626389733843602187
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.25
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
@@ -1,5 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
4
- </project>
5
-
@@ -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
@@ -1,5 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectRootManager" version="2" project-jdk-name="RVM: ruby-1.9.3-p429" project-jdk-type="RUBY_SDK" />
4
- </project>
5
-
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
-
@@ -1,5 +0,0 @@
1
- <component name="DependencyValidationManager">
2
- <state>
3
- <option name="SKIP_IMPORT_STATEMENTS" value="false" />
4
- </state>
5
- </component>
data/.idea/vcs.xml DELETED
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
- </component>
6
- </project>
7
-