imgix 3.1.1 → 3.4.0
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 +5 -5
- data/.github/CODEOWNERS +2 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
- data/.github/ISSUE_TEMPLATE/question.md +17 -0
- data/.github/pull_request_template.md +73 -0
- data/.travis.yml +14 -2
- data/CHANGELOG.md +31 -0
- data/Gemfile +2 -0
- data/README.md +144 -14
- data/Rakefile +2 -0
- data/imgix.gemspec +10 -4
- data/lib/imgix.rb +32 -8
- data/lib/imgix/client.rb +52 -15
- data/lib/imgix/param_helpers.rb +1 -0
- data/lib/imgix/path.rb +109 -43
- data/lib/imgix/version.rb +1 -1
- data/test/test_helper.rb +0 -1
- data/test/units/param_helpers_test.rb +23 -0
- data/test/units/path_test.rb +54 -4
- data/test/units/purge_test.rb +62 -17
- data/test/units/srcset_test.rb +751 -310
- data/test/units/url_test.rb +47 -5
- metadata +15 -5
data/Rakefile
CHANGED
data/imgix.gemspec
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
require 'imgix/version'
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/imgix/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
6
|
spec.name = 'imgix'
|
@@ -13,6 +12,13 @@ Gem::Specification.new do |spec|
|
|
13
12
|
spec.homepage = 'https://github.com/imgix/imgix-rb'
|
14
13
|
spec.license = 'MIT'
|
15
14
|
|
15
|
+
spec.metadata = {
|
16
|
+
'bug_tracker_uri' => 'https://github.com/imgix/imgix-rb/issues',
|
17
|
+
'changelog_uri' => 'https://github.com/imgix/imgix-rb/blob/main/CHANGELOG.md',
|
18
|
+
'documentation_uri' => "https://www.rubydoc.info/gems/imgix/#{spec.version}",
|
19
|
+
'source_code_uri' => "https://github.com/imgix/imgix-rb/tree/#{spec.version}"
|
20
|
+
}
|
21
|
+
|
16
22
|
spec.files = `git ls-files`.split($/)
|
17
23
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
24
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
data/lib/imgix.rb
CHANGED
@@ -6,22 +6,46 @@ require 'imgix/path'
|
|
6
6
|
|
7
7
|
module Imgix
|
8
8
|
# regex pattern used to determine if a domain is valid
|
9
|
-
DOMAIN_REGEX = /^(?:[a-z\d\-_]{1,62}\.){0,125}(?:[a-z\d](?:\-(?=\-*[a-z\d])|[a-z]|\d){0,62}\.)[a-z\d]{1,63}$/i
|
9
|
+
DOMAIN_REGEX = /^(?:[a-z\d\-_]{1,62}\.){0,125}(?:[a-z\d](?:\-(?=\-*[a-z\d])|[a-z]|\d){0,62}\.)[a-z\d]{1,63}$/i.freeze
|
10
|
+
|
11
|
+
# determines the growth rate when building out srcset pair widths
|
12
|
+
DEFAULT_WIDTH_TOLERANCE = 0.08
|
13
|
+
|
14
|
+
# the default minimum srcset width
|
15
|
+
MIN_WIDTH = 100
|
16
|
+
|
17
|
+
# the default maximum srcset width, also the max width supported by imgix
|
18
|
+
MAX_WIDTH = 8192
|
10
19
|
|
11
20
|
# returns an array of width values used during scrset generation
|
12
|
-
TARGET_WIDTHS = lambda {
|
13
|
-
increment_percentage =
|
14
|
-
|
21
|
+
TARGET_WIDTHS = lambda { |tolerance, min, max|
|
22
|
+
increment_percentage = tolerance || DEFAULT_WIDTH_TOLERANCE
|
23
|
+
|
24
|
+
unless increment_percentage.is_a?(Numeric) && increment_percentage > 0
|
25
|
+
width_increment_error = 'error: `width_tolerance` must be a positive `Numeric` value'
|
26
|
+
raise ArgumentError, width_increment_error
|
27
|
+
end
|
28
|
+
|
29
|
+
max_size = max || MAX_WIDTH
|
15
30
|
resolutions = []
|
16
|
-
prev =
|
31
|
+
prev = min || MIN_WIDTH
|
17
32
|
|
18
|
-
while
|
33
|
+
while prev < max_size
|
19
34
|
# ensures that each width is even
|
20
|
-
resolutions.push(
|
21
|
-
prev *= 1 + (
|
35
|
+
resolutions.push(prev.round)
|
36
|
+
prev *= 1 + (increment_percentage * 2)
|
22
37
|
end
|
23
38
|
|
24
39
|
resolutions.push(max_size)
|
25
40
|
return resolutions
|
26
41
|
}
|
42
|
+
|
43
|
+
# hash of default quality parameter values mapped by each dpr srcset entry
|
44
|
+
DPR_QUALITY = {
|
45
|
+
1 => 75,
|
46
|
+
2 => 50,
|
47
|
+
3 => 35,
|
48
|
+
4 => 23,
|
49
|
+
5 => 20
|
50
|
+
}.freeze
|
27
51
|
end
|
data/lib/imgix/client.rb
CHANGED
@@ -7,54 +7,91 @@ require 'uri'
|
|
7
7
|
|
8
8
|
module Imgix
|
9
9
|
class Client
|
10
|
-
DEFAULTS = { use_https: true }
|
10
|
+
DEFAULTS = { use_https: true }.freeze
|
11
11
|
|
12
12
|
def initialize(options = {})
|
13
13
|
options = DEFAULTS.merge(options)
|
14
|
+
host, domain = options[:host], options[:domain]
|
15
|
+
|
16
|
+
host_deprecated = "Warning: The identifier `host' has been deprecated and " \
|
17
|
+
"will\nappear as `domain' in the next major version, e.g. " \
|
18
|
+
"`@host'\nbecomes `@domain', `options[:host]' becomes " \
|
19
|
+
"`options[:domain]'.\n"
|
20
|
+
|
21
|
+
if host
|
22
|
+
warn host_deprecated
|
23
|
+
@host = host
|
24
|
+
elsif domain
|
25
|
+
@host = domain
|
26
|
+
else
|
27
|
+
@host = host
|
28
|
+
end
|
14
29
|
|
15
|
-
@host = options[:host]
|
16
30
|
validate_host!
|
31
|
+
|
17
32
|
@secure_url_token = options[:secure_url_token]
|
18
33
|
@api_key = options[:api_key]
|
19
34
|
@use_https = options[:use_https]
|
20
35
|
@include_library_param = options.fetch(:include_library_param, true)
|
21
|
-
@library = options.fetch(:library_param,
|
36
|
+
@library = options.fetch(:library_param, 'rb')
|
22
37
|
@version = options.fetch(:library_version, Imgix::VERSION)
|
23
38
|
end
|
24
39
|
|
25
40
|
def path(path)
|
26
|
-
p = Path.new(
|
41
|
+
p = Path.new(new_prefix, @secure_url_token, path)
|
27
42
|
p.ixlib("#{@library}-#{@version}") if @include_library_param
|
28
43
|
p
|
29
44
|
end
|
30
45
|
|
31
46
|
def purge(path)
|
32
|
-
|
33
|
-
|
47
|
+
api_key_deprecated = \
|
48
|
+
"Warning: Your `api_key` will no longer work after upgrading to\n" \
|
49
|
+
"imgix-rb version >= 4.0.0.\n"
|
50
|
+
warn api_key_deprecated
|
51
|
+
|
52
|
+
api_key_error = 'A valid api key is required to send purge requests'
|
53
|
+
raise api_key_error if @api_key.nil?
|
54
|
+
|
55
|
+
url = new_prefix + path
|
34
56
|
uri = URI.parse('https://api.imgix.com/v2/image/purger')
|
35
|
-
|
57
|
+
|
58
|
+
user_agent = { 'User-Agent' => "imgix #{@library}-#{@version}" }
|
59
|
+
|
60
|
+
req = Net::HTTP::Post.new(uri.path, user_agent)
|
36
61
|
req.basic_auth @api_key, ''
|
37
|
-
req.set_form_data({
|
62
|
+
req.set_form_data({ url: url })
|
63
|
+
|
38
64
|
sock = Net::HTTP.new(uri.host, uri.port)
|
39
65
|
sock.use_ssl = true
|
40
|
-
res = sock.start {|http| http.request(req) }
|
66
|
+
res = sock.start { |http| http.request(req) }
|
67
|
+
|
41
68
|
res
|
42
69
|
end
|
43
70
|
|
44
71
|
def prefix(path)
|
72
|
+
msg = "Warning: `Client::prefix' will take zero arguments " \
|
73
|
+
"in the next major version.\n"
|
74
|
+
warn msg
|
75
|
+
new_prefix
|
76
|
+
end
|
77
|
+
|
78
|
+
def new_prefix
|
45
79
|
"#{@use_https ? 'https' : 'http'}://#{@host}"
|
46
80
|
end
|
47
81
|
|
48
82
|
private
|
49
83
|
|
50
84
|
def validate_host!
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
85
|
+
host_error = 'The :host option must be specified'
|
86
|
+
raise ArgumentError, host_error if @host.nil?
|
87
|
+
|
88
|
+
domain_error = 'Domains must be passed in as fully-qualified'\
|
89
|
+
'domain names and should not include a protocol'\
|
90
|
+
'or any path element, i.e. "example.imgix.net"'\
|
91
|
+
|
92
|
+
if @host.match(DOMAIN_REGEX).nil?
|
93
|
+
raise ArgumentError, domain_error
|
56
94
|
end
|
57
95
|
end
|
58
|
-
|
59
96
|
end
|
60
97
|
end
|
data/lib/imgix/param_helpers.rb
CHANGED
data/lib/imgix/path.rb
CHANGED
@@ -10,26 +10,26 @@ module Imgix
|
|
10
10
|
include ParamHelpers
|
11
11
|
|
12
12
|
ALIASES = {
|
13
|
-
width:
|
14
|
-
height:
|
15
|
-
rotation:
|
13
|
+
width: :w,
|
14
|
+
height: :h,
|
15
|
+
rotation: :rot,
|
16
16
|
noise_reduction: :nr,
|
17
|
-
sharpness:
|
18
|
-
exposure:
|
19
|
-
vibrance:
|
20
|
-
saturation:
|
21
|
-
brightness:
|
22
|
-
contrast:
|
23
|
-
highlight:
|
24
|
-
shadow:
|
25
|
-
gamma:
|
26
|
-
pixelate:
|
27
|
-
halftone:
|
28
|
-
watermark:
|
29
|
-
text:
|
30
|
-
format:
|
31
|
-
quality:
|
32
|
-
}
|
17
|
+
sharpness: :sharp,
|
18
|
+
exposure: :exp,
|
19
|
+
vibrance: :vib,
|
20
|
+
saturation: :sat,
|
21
|
+
brightness: :bri,
|
22
|
+
contrast: :con,
|
23
|
+
highlight: :high,
|
24
|
+
shadow: :shad,
|
25
|
+
gamma: :gam,
|
26
|
+
pixelate: :px,
|
27
|
+
halftone: :htn,
|
28
|
+
watermark: :mark,
|
29
|
+
text: :txt,
|
30
|
+
format: :fm,
|
31
|
+
quality: :q
|
32
|
+
}.freeze
|
33
33
|
|
34
34
|
def initialize(prefix, secure_url_token, path = '/')
|
35
35
|
@prefix = prefix
|
@@ -39,7 +39,7 @@ module Imgix
|
|
39
39
|
|
40
40
|
@path = CGI.escape(@path) if /^https?/ =~ @path
|
41
41
|
@path = "/#{@path}" if @path[0] != '/'
|
42
|
-
@target_widths = TARGET_WIDTHS.call
|
42
|
+
@target_widths = TARGET_WIDTHS.call(DEFAULT_WIDTH_TOLERANCE, MIN_WIDTH, MAX_WIDTH)
|
43
43
|
end
|
44
44
|
|
45
45
|
def to_url(opts = {})
|
@@ -53,12 +53,12 @@ module Imgix
|
|
53
53
|
end
|
54
54
|
|
55
55
|
@options = prev_options
|
56
|
-
|
56
|
+
url
|
57
57
|
end
|
58
58
|
|
59
59
|
def defaults
|
60
60
|
@options = {}
|
61
|
-
|
61
|
+
self
|
62
62
|
end
|
63
63
|
|
64
64
|
def method_missing(method, *args, &block)
|
@@ -70,38 +70,44 @@ module Imgix
|
|
70
70
|
end
|
71
71
|
|
72
72
|
@options[key] = args.join(',')
|
73
|
-
|
73
|
+
self
|
74
74
|
end
|
75
75
|
|
76
76
|
ALIASES.each do |from, to|
|
77
77
|
define_method from do |*args|
|
78
|
+
warn "Warning: `Path.#{from}' has been deprecated and " \
|
79
|
+
"will be removed in the next major version (along " \
|
80
|
+
"with all parameter `ALIASES`).\n"
|
78
81
|
self.send(to, *args)
|
79
82
|
end
|
80
83
|
|
81
84
|
define_method "#{from}=" do |*args|
|
85
|
+
warn "Warning: `Path.#{from}=' has been deprecated and " \
|
86
|
+
"will be removed in the next major version (along " \
|
87
|
+
"with all parameter `ALIASES`).\n"
|
82
88
|
self.send("#{to}=", *args)
|
83
89
|
return self
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
87
|
-
def to_srcset(
|
93
|
+
def to_srcset(options: {}, **params)
|
88
94
|
prev_options = @options.dup
|
89
95
|
@options.merge!(params)
|
90
96
|
|
91
|
-
width = @options[
|
92
|
-
height = @options[
|
93
|
-
aspect_ratio = @options[
|
97
|
+
width = @options[:w]
|
98
|
+
height = @options[:h]
|
99
|
+
aspect_ratio = @options[:ar]
|
94
100
|
|
95
|
-
if
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
101
|
+
srcset = if width || (height && aspect_ratio)
|
102
|
+
build_dpr_srcset(options: options, params: @options)
|
103
|
+
else
|
104
|
+
build_srcset_pairs(options: options, params: @options)
|
105
|
+
end
|
100
106
|
|
101
107
|
@options = prev_options
|
102
|
-
|
108
|
+
srcset
|
103
109
|
end
|
104
|
-
|
110
|
+
|
105
111
|
private
|
106
112
|
|
107
113
|
def signature
|
@@ -128,27 +134,87 @@ module Imgix
|
|
128
134
|
query.length > 0
|
129
135
|
end
|
130
136
|
|
131
|
-
def build_srcset_pairs(params)
|
137
|
+
def build_srcset_pairs(options:, params:)
|
132
138
|
srcset = ''
|
133
139
|
|
134
|
-
|
135
|
-
|
140
|
+
widths = options[:widths] || []
|
141
|
+
width_tolerance = options[:width_tolerance] || DEFAULT_WIDTH_TOLERANCE
|
142
|
+
min_width = options[:min_width] || MIN_WIDTH
|
143
|
+
max_width = options[:max_width] || MAX_WIDTH
|
144
|
+
|
145
|
+
if !widths.empty?
|
146
|
+
validate_widths!(widths)
|
147
|
+
srcset_widths = widths
|
148
|
+
elsif width_tolerance != DEFAULT_WIDTH_TOLERANCE || min_width != MIN_WIDTH || max_width != MAX_WIDTH
|
149
|
+
validate_range!(min_width, max_width)
|
150
|
+
validate_width_tolerance!(width_tolerance)
|
151
|
+
srcset_widths = TARGET_WIDTHS.call(width_tolerance, min_width, max_width)
|
152
|
+
else
|
153
|
+
srcset_widths = @target_widths
|
154
|
+
end
|
155
|
+
|
156
|
+
srcset_widths.each do |width|
|
157
|
+
params[:w] = width
|
136
158
|
srcset += "#{to_url(params)} #{width}w,\n"
|
137
159
|
end
|
138
160
|
|
139
|
-
|
161
|
+
srcset[0..-3]
|
140
162
|
end
|
141
163
|
|
142
|
-
def build_dpr_srcset(params)
|
164
|
+
def build_dpr_srcset(options:, params:)
|
143
165
|
srcset = ''
|
144
|
-
target_ratios = [1,2,3,4,5]
|
145
166
|
|
146
|
-
|
147
|
-
|
167
|
+
disable_variable_quality = options[:disable_variable_quality] || false
|
168
|
+
validate_variable_qualities!(disable_variable_quality)
|
169
|
+
|
170
|
+
target_ratios = [1, 2, 3, 4, 5]
|
171
|
+
quality = params[:q]
|
172
|
+
|
173
|
+
target_ratios.each do |ratio|
|
174
|
+
params[:dpr] = ratio
|
175
|
+
|
176
|
+
unless disable_variable_quality
|
177
|
+
params[:q] = quality || DPR_QUALITY[ratio]
|
178
|
+
end
|
179
|
+
|
148
180
|
srcset += "#{to_url(params)} #{ratio}x,\n"
|
149
181
|
end
|
150
182
|
|
151
|
-
|
183
|
+
srcset[0..-3]
|
184
|
+
end
|
185
|
+
|
186
|
+
def validate_width_tolerance!(width_tolerance)
|
187
|
+
width_increment_error = 'error: `width_tolerance` must be a positive `Numeric` value'
|
188
|
+
|
189
|
+
if !width_tolerance.is_a?(Numeric) || width_tolerance <= 0
|
190
|
+
raise ArgumentError, width_increment_error
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def validate_widths!(widths)
|
195
|
+
widths_error = 'error: `widths` must be an array of positive `Numeric` values'
|
196
|
+
raise ArgumentError, widths_error unless widths.is_a?(Array)
|
197
|
+
|
198
|
+
all_positive_integers = widths.all? { |i| i.is_a?(Integer) && i > 0 }
|
199
|
+
raise ArgumentError, widths_error unless all_positive_integers
|
200
|
+
end
|
201
|
+
|
202
|
+
def validate_range!(min_srcset, max_srcset)
|
203
|
+
range_numeric_error = 'error: `min_width` and `max_width` must be positive `Numeric` values'
|
204
|
+
unless min_srcset.is_a?(Numeric) && max_srcset.is_a?(Numeric)
|
205
|
+
raise ArgumentError, range_numeric_error
|
206
|
+
end
|
207
|
+
|
208
|
+
unless min_srcset > 0 && max_srcset > 0
|
209
|
+
raise ArgumentError, range_numeric_error
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def validate_variable_qualities!(disable_quality)
|
214
|
+
disable_quality_error = 'error: `disable_quality` must be a Boolean value'
|
215
|
+
unless disable_quality.is_a?(TrueClass) || disable_quality.is_a?(FalseClass)
|
216
|
+
raise ArgumentError, disable_quality_error
|
217
|
+
end
|
152
218
|
end
|
153
219
|
end
|
154
220
|
end
|
data/lib/imgix/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class ParamHelpers < Imgix::Test
|
6
|
+
def test_param_helpers_emits_dep_warning
|
7
|
+
host_warn = "Warning: The identifier `host' has been deprecated and " \
|
8
|
+
"will\nappear as `domain' in the next major version, e.g. " \
|
9
|
+
"`@host'\nbecomes `@domain', `options[:host]' becomes " \
|
10
|
+
"`options[:domain]'.\n"
|
11
|
+
|
12
|
+
assert_output(nil, host_warn) {
|
13
|
+
client = Imgix::Client.new(host: 'test.imgix.net')
|
14
|
+
|
15
|
+
rect_warn = "Warning: `ParamHelpers.rect` has been deprecated and " \
|
16
|
+
"will be removed in the next major version.\n"
|
17
|
+
|
18
|
+
assert_output(nil, rect_warn){
|
19
|
+
client.path('/images/demo.png').rect(x: 0, y: 50, width: 200, height: 300)
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|