imgix 3.1.1 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
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