image_vise 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|