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.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
 
3
5
  require 'rake/testtask'
@@ -1,7 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
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)/})
@@ -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 = 8
14
- max_size = 8192
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 = 100
31
+ prev = min || MIN_WIDTH
17
32
 
18
- while(prev <= max_size)
33
+ while prev < max_size
19
34
  # ensures that each width is even
20
- resolutions.push((2 * (prev / 2).round))
21
- prev *= 1 + ((increment_percentage.to_f) / 100) * 2
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
@@ -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, "rb")
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(prefix(path), @secure_url_token, path)
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
- raise "Authentication token required" unless !!(@api_key)
33
- url = prefix(path)+path
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
- req = Net::HTTP::Post.new(uri.path, {"User-Agent" => "imgix #{@library}-#{@version}"})
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({'url' => url})
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
- unless @host != nil
52
- raise ArgumentError, "The :host option must be specified"
53
- end
54
- if @host.match(DOMAIN_REGEX) == nil
55
- raise ArgumentError, "Domains must be passed in as fully-qualified domain names and should not include a protocol or any path element, i.e. \"example.imgix.net\"."
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
@@ -3,6 +3,7 @@
3
3
  module Imgix
4
4
  module ParamHelpers
5
5
  def rect(position)
6
+ warn "Warning: `ParamHelpers.rect` has been deprecated and will be removed in the next major version.\n"
6
7
  @options[:rect] = position and return self if position.is_a?(String)
7
8
 
8
9
  @options[:rect] = [
@@ -10,26 +10,26 @@ module Imgix
10
10
  include ParamHelpers
11
11
 
12
12
  ALIASES = {
13
- width: :w,
14
- height: :h,
15
- rotation: :rot,
13
+ width: :w,
14
+ height: :h,
15
+ rotation: :rot,
16
16
  noise_reduction: :nr,
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
- }
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
- return url
56
+ url
57
57
  end
58
58
 
59
59
  def defaults
60
60
  @options = {}
61
- return self
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
- return self
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(params = {})
93
+ def to_srcset(options: {}, **params)
88
94
  prev_options = @options.dup
89
95
  @options.merge!(params)
90
96
 
91
- width = @options['w'.to_sym]
92
- height = @options['h'.to_sym]
93
- aspect_ratio = @options['ar'.to_sym]
97
+ width = @options[:w]
98
+ height = @options[:h]
99
+ aspect_ratio = @options[:ar]
94
100
 
95
- if ((width) || (height && aspect_ratio))
96
- srcset = build_dpr_srcset(@options)
97
- else
98
- srcset = build_srcset_pairs(@options)
99
- end
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
- return srcset
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
- for width in @target_widths do
135
- params['w'.to_sym] = width
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
- return srcset[0..-3]
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
- for ratio in target_ratios do
147
- params['dpr'.to_sym] = ratio
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
- return srcset[0..-3]
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Imgix
4
- VERSION = '3.1.1'
4
+ VERSION = '3.4.0'
5
5
  end
@@ -7,7 +7,6 @@ Bundler.require :test
7
7
  require 'minitest/autorun'
8
8
  require 'imgix'
9
9
  require 'webmock/minitest'
10
- include WebMock::API
11
10
 
12
11
  class Imgix::Test < MiniTest::Test
13
12
  end
@@ -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