image_vise 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/image_vise.gemspec +1 -0
- data/lib/image_vise.rb +18 -0
- data/lib/image_vise/operators/srgb.rb +3 -0
- data/lib/image_vise/render_engine.rb +5 -4
- data/lib/image_vise/version.rb +1 -1
- data/spec/image_vise/render_engine_spec.rb +32 -0
- data/spec/image_vise/srgb_spec.rb +13 -0
- data/spec/image_vise_spec.rb +38 -10
- data/spec/spec_helper.rb +5 -1
- data/spec/worker_in_tube.jpg +0 -0
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a71e26e83e715290293b8e512fe6f72aaa39098
|
4
|
+
data.tar.gz: 4dd57c319fe8728e0bc8cf0e2fdd67ab41bc028b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c53127c2ef018e94200a23281d6923497d5466d19ae63b8d18ad0a292f3eb6e6615baccee577d01cee774e50b32fa04e6c5e910d2e9a9cd445fc47f5bfb814f
|
7
|
+
data.tar.gz: f0fbe07c4fb401161714862fff8833300821d51f710140a0f16f6336251243ae48679a4ff5ac9df48970baaed97c4ba4080e0562fcb4c5cf7dbe3b6be437cbc9
|
data/README.md
CHANGED
@@ -208,6 +208,7 @@ The gem version is specified in `image_vise.rb`. When contributing, please follo
|
|
208
208
|
|
209
209
|
Copyright (c) 2016 WeTransfer. See LICENSE.txt for further details.
|
210
210
|
The licensing terms also apply to the `waterside_magic_hour.jpg` test image.
|
211
|
+
The `worker_in_tube.jpg` is used with permission from Arcadis Nederland B.V.
|
211
212
|
|
212
213
|
The sRGB color profiles are [downloaded from the ICC](http://www.color.org/srgbprofiles.xalter) and it's
|
213
214
|
use is governed by the terms present in the LICENSE.txt
|
data/image_vise.gemspec
CHANGED
data/lib/image_vise.rb
CHANGED
@@ -11,12 +11,15 @@ class ImageVise
|
|
11
11
|
require_relative 'image_vise/version'
|
12
12
|
S_MUTEX = Mutex.new
|
13
13
|
private_constant :S_MUTEX
|
14
|
+
# The default cache liftime is 30 days, and will be used if no custom lifetime is set.
|
15
|
+
DEFAULT_CACHE_LIFETIME = 2_592_000
|
14
16
|
|
15
17
|
@allowed_hosts = Set.new
|
16
18
|
@keys = Set.new
|
17
19
|
@operators = {}
|
18
20
|
@allowed_glob_patterns = Set.new
|
19
21
|
@fetchers = {}
|
22
|
+
@cache_lifetime = DEFAULT_CACHE_LIFETIME
|
20
23
|
|
21
24
|
class << self
|
22
25
|
# Resets all allowed hosts
|
@@ -51,6 +54,21 @@ class ImageVise
|
|
51
54
|
S_MUTEX.synchronize { @allowed_glob_patterns.clear }
|
52
55
|
end
|
53
56
|
|
57
|
+
def cache_lifetime_seconds=(length)
|
58
|
+
Integer(length)
|
59
|
+
S_MUTEX.synchronize { @cache_lifetime = length.to_i }
|
60
|
+
rescue => e
|
61
|
+
raise ArgumentError, "The custom cache lifetime value must be an integer"
|
62
|
+
end
|
63
|
+
|
64
|
+
def cache_lifetime_seconds
|
65
|
+
S_MUTEX.synchronize { @cache_lifetime }
|
66
|
+
end
|
67
|
+
|
68
|
+
def reset_cache_lifetime_seconds!
|
69
|
+
S_MUTEX.synchronize { @cache_lifetime = DEFAULT_CACHE_LIFETIME }
|
70
|
+
end
|
71
|
+
|
54
72
|
# Adds a key against which the parameters are going to be verified.
|
55
73
|
# Multiple applications may have their own different keys,
|
56
74
|
# so we need to have multiple keys.
|
@@ -25,6 +25,9 @@ class ImageVise::SRGB
|
|
25
25
|
PROFILE_PATH = File.expand_path(__dir__ + '/sRGB_v4_ICC_preference_displayclass.icc')
|
26
26
|
def apply!(magick_image)
|
27
27
|
magick_image.add_profile(PROFILE_PATH)
|
28
|
+
rescue Magick::ImageMagickError
|
29
|
+
magick_image.strip!
|
30
|
+
magick_image.add_profile(PROFILE_PATH)
|
28
31
|
end
|
29
32
|
ImageVise.add_operator 'srgb', self
|
30
33
|
end
|
@@ -20,13 +20,13 @@
|
|
20
20
|
'Cache-Control' => 'public, max-age=5'
|
21
21
|
}).freeze
|
22
22
|
|
23
|
-
# "public" of course. Add max-age so that there is _some_
|
23
|
+
# Cache details: "public" of course. Add max-age so that there is _some_
|
24
24
|
# revalidation after a time (otherwise some proxies treat it
|
25
25
|
# as "must-revalidate" always), and "no-transform" so that
|
26
26
|
# various deflate schemes are not applied to it (does happen
|
27
27
|
# with Rack::Cache and leads Chrome to throw up on content
|
28
28
|
# decoding for example).
|
29
|
-
IMAGE_CACHE_CONTROL =
|
29
|
+
IMAGE_CACHE_CONTROL = "public, no-transform, max-age=%d"
|
30
30
|
|
31
31
|
# How long is a render (the ImageMagick/write part) is allowed to
|
32
32
|
# take before we kill it
|
@@ -166,7 +166,8 @@
|
|
166
166
|
# `process_image_request` unsplatted, and returns a triplet that
|
167
167
|
# can be returned as a Rack response. The Rack response will contain
|
168
168
|
# an iterable body object that is designed to automatically delete
|
169
|
-
# the Tempfile it wraps on close.
|
169
|
+
# the Tempfile it wraps on close. Sets the cache lifetime to either the default
|
170
|
+
# value of 2592000 or the value the user selected using add_custom_cache_max_length.
|
170
171
|
#
|
171
172
|
# @param render_destination_file[File] the File handle to the rendered image
|
172
173
|
# @param render_file_type[MagicBytes::FileType] the rendered file type
|
@@ -175,7 +176,7 @@
|
|
175
176
|
response_headers = DEFAULT_HEADERS.merge({
|
176
177
|
'Content-Type' => render_file_type.mime,
|
177
178
|
'Content-Length' => '%d' % render_destination_file.size,
|
178
|
-
'Cache-Control' => IMAGE_CACHE_CONTROL,
|
179
|
+
'Cache-Control' => IMAGE_CACHE_CONTROL % ImageVise.cache_lifetime_seconds,
|
179
180
|
'ETag' => etag
|
180
181
|
})
|
181
182
|
|
data/lib/image_vise/version.rb
CHANGED
@@ -37,6 +37,7 @@ describe ImageVise::RenderEngine do
|
|
37
37
|
after :each do
|
38
38
|
ImageVise.reset_allowed_hosts!
|
39
39
|
ImageVise.reset_secret_keys!
|
40
|
+
ImageVise.reset_cache_lifetime_seconds!
|
40
41
|
end
|
41
42
|
|
42
43
|
it 'halts with 400 when the requested image cannot be opened by ImageMagick' do
|
@@ -137,6 +138,37 @@ describe ImageVise::RenderEngine do
|
|
137
138
|
expect(last_response.status).to eq(304)
|
138
139
|
end
|
139
140
|
|
141
|
+
it 'allows for setting a custom cache lifetime' do
|
142
|
+
uri = Addressable::URI.parse(public_url)
|
143
|
+
ImageVise.add_allowed_host!(uri.host)
|
144
|
+
ImageVise.add_secret_key!('l33tness')
|
145
|
+
|
146
|
+
ImageVise.cache_lifetime_seconds = '900'
|
147
|
+
p = ImageVise::Pipeline.new.fit_crop(width: 10, height: 35, gravity: 'c')
|
148
|
+
image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
|
149
|
+
|
150
|
+
req_path = image_request.to_path_params('l33tness')
|
151
|
+
|
152
|
+
get req_path, {}
|
153
|
+
expect(last_response).to be_ok
|
154
|
+
expect(last_response['Cache-Control']).to match(/max-age=900/)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'uses the correct default cache lifetime if one is not specified' do
|
158
|
+
uri = Addressable::URI.parse(public_url)
|
159
|
+
ImageVise.add_allowed_host!(uri.host)
|
160
|
+
ImageVise.add_secret_key!('l33tness')
|
161
|
+
|
162
|
+
p = ImageVise::Pipeline.new.fit_crop(width: 10, height: 35, gravity: 'c')
|
163
|
+
image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
|
164
|
+
|
165
|
+
req_path = image_request.to_path_params('l33tness')
|
166
|
+
|
167
|
+
get req_path, {}
|
168
|
+
expect(last_response).to be_ok
|
169
|
+
expect(last_response['Cache-Control']).to match(/max-age=2592000/)
|
170
|
+
end
|
171
|
+
|
140
172
|
it 'responds with an image that passes through all the processing steps' do
|
141
173
|
uri = Addressable::URI.parse(public_url)
|
142
174
|
ImageVise.add_allowed_host!(uri.host)
|
@@ -20,4 +20,17 @@ describe ImageVise::SRGB do
|
|
20
20
|
image.strip!
|
21
21
|
examine_image(image, "from-srgb-SHOULD-LOOK-IDENTICAL")
|
22
22
|
end
|
23
|
+
|
24
|
+
it 'applies the profile for an image with non-matching colorspace and profile' do
|
25
|
+
image = Magick::Image.read(test_image_mismatched_colorspace_profile_path).first
|
26
|
+
examine_image(image, 'pre-mismatched-colors')
|
27
|
+
described_class.new.apply!(image)
|
28
|
+
examine_image(image, 'post-mismatched-colors')
|
29
|
+
end
|
30
|
+
|
31
|
+
it "strips the image's profile if the profile and colorspace are non-matching" do
|
32
|
+
non_matching_image = Magick::Image.read(test_image_mismatched_colorspace_profile_path).first
|
33
|
+
expect(non_matching_image).to receive(:strip!).and_call_original
|
34
|
+
described_class.new.apply!(non_matching_image)
|
35
|
+
end
|
23
36
|
end
|
data/spec/image_vise_spec.rb
CHANGED
@@ -3,16 +3,16 @@ require 'rack/test'
|
|
3
3
|
|
4
4
|
describe ImageVise do
|
5
5
|
include Rack::Test::Methods
|
6
|
-
|
6
|
+
|
7
7
|
def app
|
8
8
|
described_class.new
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
context 'ImageVise.allowed_hosts' do
|
12
12
|
it 'returns the allowed hosts and is empty by default' do
|
13
13
|
expect(described_class.allowed_hosts).to be_empty
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
it 'allows add_allowed_host! and reset_allowed_hosts!' do
|
17
17
|
described_class.add_allowed_host!('www.imageboard.im')
|
18
18
|
expect(described_class.allowed_hosts).to include('www.imageboard.im')
|
@@ -20,14 +20,14 @@ describe ImageVise do
|
|
20
20
|
expect(described_class.allowed_hosts).not_to include('www.imageboard.im')
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
context 'ImageVise.secret_keys' do
|
25
25
|
it 'raises when asked for a key and no keys has been set' do
|
26
26
|
expect {
|
27
27
|
described_class.secret_keys
|
28
28
|
}.to raise_error("No keys set, add a key using `ImageVise.add_secret_key!(key)'")
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
it 'allows add_secret_key!(key) and reset_secret_keys!' do
|
32
32
|
described_class.add_secret_key!('l33t')
|
33
33
|
expect(described_class.secret_keys).to include('l33t')
|
@@ -38,6 +38,34 @@ describe ImageVise do
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
context 'ImageVise.cache_lifetime_seconds=' do
|
42
|
+
it 'raises when given something other than an integer' do
|
43
|
+
expect {
|
44
|
+
described_class.cache_lifetime_seconds = "Wh0ops!"
|
45
|
+
}.to raise_error("The custom cache lifetime value must be an integer")
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'succeeds when given an integer' do
|
49
|
+
expect {
|
50
|
+
described_class.cache_lifetime_seconds=(900)
|
51
|
+
}.not_to raise_error
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'ImageVise.cache_lifetime_seconds' do
|
56
|
+
it 'allows cache_lifetime_seconds to be set' do
|
57
|
+
described_class.cache_lifetime_seconds = 100
|
58
|
+
expect(described_class.cache_lifetime_seconds).to eq(100)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'allows reset_cache_lifetime_seconds!' do
|
62
|
+
described_class.cache_lifetime_seconds = 100
|
63
|
+
expect(described_class.cache_lifetime_seconds).to eq(100)
|
64
|
+
described_class.reset_cache_lifetime_seconds!
|
65
|
+
expect(described_class.cache_lifetime_seconds).to eq(2592000)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
41
69
|
describe 'ImageVise.new.call' do
|
42
70
|
it 'instantiates a new app and performs call() on it' do
|
43
71
|
expect_any_instance_of(ImageVise::RenderEngine).to receive(:call).with(:mock_env) { :yes }
|
@@ -51,7 +79,7 @@ describe ImageVise do
|
|
51
79
|
ImageVise.call(:mock_env)
|
52
80
|
end
|
53
81
|
end
|
54
|
-
|
82
|
+
|
55
83
|
describe '.image_params' do
|
56
84
|
it 'generates a Hash with paremeters for processing the resized image' do
|
57
85
|
params = ImageVise.image_params(src_url: 'http://host.com/image.jpg', secret: 'l33t') do |pipe|
|
@@ -69,7 +97,7 @@ describe ImageVise do
|
|
69
97
|
expect(http).to respond_to(:fetch_uri_to_tempfile)
|
70
98
|
file = ImageVise.fetcher_for('file')
|
71
99
|
expect(http).to respond_to(:fetch_uri_to_tempfile)
|
72
|
-
|
100
|
+
|
73
101
|
expect {
|
74
102
|
ImageVise.fetcher_for('undernet')
|
75
103
|
}.to raise_error(/No fetcher registered/)
|
@@ -84,14 +112,14 @@ describe ImageVise do
|
|
84
112
|
expect(path).to start_with('/')
|
85
113
|
end
|
86
114
|
end
|
87
|
-
|
115
|
+
|
88
116
|
describe 'methods dealing with the operator list' do
|
89
117
|
it 'have the basic operators already set up' do
|
90
118
|
oplist = ImageVise.defined_operator_names
|
91
119
|
expect(oplist).to include('sharpen')
|
92
120
|
expect(oplist).to include('crop')
|
93
121
|
end
|
94
|
-
|
122
|
+
|
95
123
|
it 'allows an operator to be added and retrieved' do
|
96
124
|
class CustomOp; end
|
97
125
|
ImageVise.add_operator 'custom_op', CustomOp
|
@@ -99,7 +127,7 @@ describe ImageVise do
|
|
99
127
|
expect(ImageVise.operator_name_for(CustomOp.new)).to eq('custom_op')
|
100
128
|
expect(ImageVise.defined_operator_names).to include('custom_op')
|
101
129
|
end
|
102
|
-
|
130
|
+
|
103
131
|
it 'raises an exception when an operator key is requested that does not exist' do
|
104
132
|
class UnknownOp; end
|
105
133
|
expect {
|
data/spec/spec_helper.rb
CHANGED
@@ -8,9 +8,9 @@ Bundler.require
|
|
8
8
|
|
9
9
|
require 'tmpdir'
|
10
10
|
require 'securerandom'
|
11
|
-
|
12
11
|
require 'addressable/uri'
|
13
12
|
require 'strenv'
|
13
|
+
require 'pry'
|
14
14
|
require_relative 'test_server'
|
15
15
|
|
16
16
|
TEST_RENDERS_DIR = Dir.mktmpdir
|
@@ -82,6 +82,10 @@ RSpec.configure do | config |
|
|
82
82
|
File.expand_path(__dir__ + '/waterside_magic_hour_adobergb.jpg')
|
83
83
|
end
|
84
84
|
|
85
|
+
def test_image_mismatched_colorspace_profile_path
|
86
|
+
File.expand_path(__dir__ + '/worker_in_tube.jpg')
|
87
|
+
end
|
88
|
+
|
85
89
|
def test_image_png_transparency
|
86
90
|
File.expand_path(__dir__ + '/waterside_magic_hour_transp.png')
|
87
91
|
end
|
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: image_vise
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: patron
|
@@ -178,6 +178,20 @@ dependencies:
|
|
178
178
|
- - ">="
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: pry
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
181
195
|
description: Image processing via URLs
|
182
196
|
email:
|
183
197
|
- me@julik.nl
|
@@ -246,6 +260,7 @@ files:
|
|
246
260
|
- spec/waterside_magic_hour_adobergb.jpg
|
247
261
|
- spec/waterside_magic_hour_gray.tif
|
248
262
|
- spec/waterside_magic_hour_transp.png
|
263
|
+
- spec/worker_in_tube.jpg
|
249
264
|
homepage: https://github.com/WeTransfer/image_vise
|
250
265
|
licenses:
|
251
266
|
- MIT
|
@@ -267,7 +282,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
267
282
|
version: '0'
|
268
283
|
requirements: []
|
269
284
|
rubyforge_project:
|
270
|
-
rubygems_version: 2.
|
285
|
+
rubygems_version: 2.5.2
|
271
286
|
signing_key:
|
272
287
|
specification_version: 4
|
273
288
|
summary: Runtime thumbnailing proxy
|