imgix 3.1.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 93aa8838354b1565c1691c817c4ed212d76b3222
4
- data.tar.gz: bc28c0311f08510f4fc4ff96c73774b59d83e59f
3
+ metadata.gz: 2888effee0dc9f00b05832a337fc22d08973703a
4
+ data.tar.gz: 0d55efe17b819635689e9e9a361339702ae2df73
5
5
  SHA512:
6
- metadata.gz: e7e492f9c63a641a6c2dc6f3f86ef96008c965187ce5439448e878d37a0b825871672447b8f113eba1137c97353a358e7cc6a3be68b8720c5a37472bc5f4515f
7
- data.tar.gz: b2b3799e866a64680842a6193d5ca1e0794ff5d918052c732bfe704de63bc9bb5b29a7e6f4f608fc5642344e01ee38b22990951273e16402ccd98022a1906252
6
+ metadata.gz: 6a4c006d3efb6e4e60552a7330e22632f40f57f8fee2a0b5ea845f354890a8cf1eb8c7594e5e6ca64fc933bcb2fbab6017c0b9f8da276657d25ce8b4abaf968e
7
+ data.tar.gz: 0127e8b6c7643b63e2d89437499493bf4d9eb1cf7fb7dd5392436e17a55e480f5e7a923064204f2490084b79339832ec6e9da85af4ccf6aaac03d192ac37d3a8
@@ -3,6 +3,15 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
+ ## [3.2.0](https://github.com/imgix/imgix-rb/compare/3.1.1...3.2.0) - November 15, 2019
7
+
8
+ * feat: append variable qualities to dpr srcsets ([#58](https://github.com/imgix/imgix-rb/pull/58))
9
+ * refactor: pass srcset modifiers through the `options` parameter ([#57](https://github.com/imgix/imgix-rb/pull/57))
10
+ * feat: support defining a min and max width range ([#56](https://github.com/imgix/imgix-rb/pull/56))
11
+ * feat: add ability to pass in custom widths ([#55](https://github.com/imgix/imgix-rb/pull/55))
12
+ * feat: add ability to configure the srcset width tolerance ([#54](https://github.com/imgix/imgix-rb/pull/54))
13
+ * style: drop redundant explicit return statements ([#52](https://github.com/imgix/imgix-rb/pull/52))
14
+
6
15
  ## [3.1.1](https://github.com/imgix/imgix-rb/compare/3.1.0...3.1.1) - July 28, 2019
7
16
 
8
17
  * fix: include dpr parameter when generating a DPR srcset ([#48](https://github.com/imgix/imgix-rb/pull/48))
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Official Ruby Gem for signing [imgix](http://imgix.com) URLs. Tested under 2.3.0, 2.2.4, 2.1.8, jruby-9.0.5.0, and rbx-2.11.
4
4
 
5
- [![Build Status](https://travis-ci.org/imgix/imgix-rb.png?branch=master)](https://travis-ci.org/imgix/imgix-rb)
5
+ [![Build Status](https://travis-ci.org/imgix/imgix-rb.svg?branch=master)](https://travis-ci.org/imgix/imgix-rb)
6
6
 
7
7
  ## Installation
8
8
 
@@ -23,7 +23,7 @@ Or install it yourself as:
23
23
 
24
24
  ## Usage
25
25
 
26
- Simply initialize a client with a `:host` and your `:secure_url_token`. By default, HTTPS URLs are generated, but you can toggle that by passing `use_https: false`.
26
+ Initialize a client with a `:host` and your `:secure_url_token`. By default, HTTPS URLs are generated, but you can toggle that by passing `use_https: false`.
27
27
 
28
28
  Call `Imgix::Client#path` with the resource path to get an `Imgix::Path` object back. You can then manipulate the path parameters, and call `Imgix#Path#to_url` when you're done.
29
29
 
@@ -68,6 +68,8 @@ https://your-subdomain.imgix.net/images/demo.png?w=7400&s=a5dd7dda1dbac613f0475f
68
68
  https://your-subdomain.imgix.net/images/demo.png?w=8192&s=9fbd257c53e770e345ce3412b64a3452 8192w
69
69
  ```
70
70
 
71
+ ### Fixed image rendering
72
+
71
73
  In cases where enough information is provided about an image's dimensions, `to_srcset` will instead build a `srcset` that will allow for an image to be served at different resolutions. The parameters taken into consideration when determining if an image is fixed-width are `w`, `h`, and `ar`. By invoking `to_srcset` with either a width **or** the height and aspect ratio (along with `fit=crop`, typically) provided, a different `srcset` will be generated for a fixed-size image instead.
72
74
 
73
75
  ```rb
@@ -89,6 +91,101 @@ https://your-subdomain.imgix.net/images/demo.png?h=800&ar=3%3A2&fit=crop&dpr=5&s
89
91
 
90
92
  For more information to better understand `srcset`, we highly recommend [Eric Portis' "Srcset and sizes" article](https://ericportis.com/posts/2014/srcset-sizes/) which goes into depth about the subject.
91
93
 
94
+ ### Custom Widths
95
+
96
+ In situations where specific widths are desired when generating `srcset` pairs, a user can specify them by passing an array of integers via `widths` to the `options` keyword argument.
97
+
98
+ ```rb
99
+ @client ||= Imgix::Client.new(host: 'testing.imgix.net', include_library_param: false)
100
+ .path('image.jpg')
101
+ .to_srcset(options: { widths: [100, 500, 1000, 1800] })
102
+ ```
103
+
104
+ Will generate the following `srcset` of width pairs:
105
+
106
+ ```html
107
+ https://testing.imgix.net/image.jpg?w=100 100w,
108
+ https://testing.imgix.net/image.jpg?w=500 500w,
109
+ https://testing.imgix.net/image.jpg?w=1000 1000w,
110
+ https://testing.imgix.net/image.jpg?w=1800 1800w
111
+ ```
112
+
113
+ Please note that in situations where a `srcset` is being rendered as a [fixed image](#fixed-image-rendering), any custom `widths` passed in will be ignored. Additionally, if both `widths` and a `width_tolerance` are passed to the `options` parameter in the `to_srcset` method, the custom widths list will take precedence.
114
+
115
+ ### Width Tolerance
116
+
117
+ The `srcset` width tolerance dictates the maximum tolerated size difference between an image's downloaded size and its rendered size. For example: setting this value to 0.1 means that an image will not render more than 10% larger or smaller than its native size. In practice, the image URLs generated for a width-based srcset attribute will grow by twice this rate. A lower tolerance means images will render closer to their native size (thereby reducing rendering artifacts), but a large srcset list will be generated and consequently users may experience lower rates of cache-hit for pre-rendered images on your site.
118
+
119
+ By default this rate is set to 8 percent, which we consider to be the ideal rate for maximizing cache hits without sacrificing visual quality. Users can specify their own width tolerance by passing a positive numeric value to `width_tolerance` within the `options` keyword argument:
120
+
121
+ ```rb
122
+ client = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false)
123
+ client.path('image.jpg').to_srcset(options: { width_tolerance: 0.20 })
124
+ ```
125
+
126
+ In this case, the `width_tolerance` is set to 20 percent, which will be reflected in the difference between subsequent widths in a srcset pair:
127
+
128
+ ```
129
+ https://testing.imgix.net/image.jpg?w=100 100w,
130
+ https://testing.imgix.net/image.jpg?w=140 140w,
131
+ https://testing.imgix.net/image.jpg?w=196 196w,
132
+ ...
133
+ https://testing.imgix.net/image.jpg?w=8192 8192w
134
+ ```
135
+
136
+ ### Minimum and Maximum Width Ranges
137
+
138
+ If the exact number of minimum/maximum physical pixels that an image will need to be rendered at is known, a user can specify them by passing an integer via `min_srcset` and/or `max_srcset` to the `options` keyword parameters:
139
+
140
+ ```rb
141
+ client = Imgix::Client.new(host: 'testing.imgix.net', include_library_param: false)
142
+ client.path('image.jpg').to_srcset(options: { min_srcset: 500, max_srcset: 2000 })
143
+ ```
144
+
145
+ Will result in a smaller, more tailored srcset.
146
+
147
+ ```
148
+ https://testing.imgix.net/image.jpg?w=500 500w,
149
+ https://testing.imgix.net/image.jpg?w=580 580w,
150
+ https://testing.imgix.net/image.jpg?w=672 672w,
151
+ https://testing.imgix.net/image.jpg?w=780 780w,
152
+ https://testing.imgix.net/image.jpg?w=906 906w,
153
+ https://testing.imgix.net/image.jpg?w=1050 1050w,
154
+ https://testing.imgix.net/image.jpg?w=1218 1218w,
155
+ https://testing.imgix.net/image.jpg?w=1414 1414w,
156
+ https://testing.imgix.net/image.jpg?w=1640 1640w,
157
+ https://testing.imgix.net/image.jpg?w=1902 1902w,
158
+ https://testing.imgix.net/image.jpg?w=2000 2000w
159
+ ```
160
+
161
+ Remember that browsers will apply a device pixel ratio as a multiplier when selecting which image to download from a `srcset`. For example, even if you know your image will render no larger than 1000px, specifying `options: { max_srcset: 1000 }` will give your users with DPR higher than 1 no choice but to download and render a low-resolution version of the image. Therefore, it is vital to factor in any potential differences when choosing a minimum or maximum range.
162
+
163
+ Also please note that according to the [imgix API](https://docs.imgix.com/apis/url/size/w), the maximum renderable image width is 8192 pixels.
164
+
165
+ ### Variable Qualities
166
+
167
+ This gem will automatically append a variable `q` parameter mapped to each `dpr` parameter when generating a [fixed-image](https://github.com/imgix/imgix-rb#fixed-image-rendering) srcset. This technique is commonly used to compensate for the increased filesize of high-DPR images. Since high-DPR images are displayed at a higher pixel density on devices, image quality can be lowered to reduce overall filesize without sacrificing perceived visual quality. For more information and examples of this technique in action, see [this blog post](https://blog.imgix.com/2016/03/30/dpr-quality).
168
+
169
+ This behavior will respect any overriding `q` value passed in as a parameter. Additionally, it can be disabled altogether by passing `options: { disable_variable_quality: true }` to `Imgix:Path#to_srcset`.
170
+
171
+ This behavior specifically occurs when a [fixed-size image](https://github.com/imgix/imgix-rb#fixed-image-rendering) is rendered, for example:
172
+
173
+ ```rb
174
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', include_library_param: false)
175
+ .path('image.jpg')
176
+ .to_srcset(w:100)
177
+ ```
178
+
179
+ will generate a srcset with the following `q` to `dpr` mapping:
180
+
181
+ ```html
182
+ https://testing.imgix.net/image.jpg?w=100&dpr=1&q=75 1x,
183
+ https://testing.imgix.net/image.jpg?w=100&dpr=2&q=50 2x,
184
+ https://testing.imgix.net/image.jpg?w=100&dpr=3&q=35 3x,
185
+ https://testing.imgix.net/image.jpg?w=100&dpr=4&q=23 4x,
186
+ https://testing.imgix.net/image.jpg?w=100&dpr=5&q=20 5x
187
+ ```
188
+
92
189
  ## Multiple Parameters
93
190
 
94
191
  When the imgix api requires multiple parameters you have to use the method rather than an accessor.
@@ -99,7 +196,6 @@ For example to use the noise reduction:
99
196
  path.noise_reduction(50,50)
100
197
  ```
101
198
 
102
-
103
199
  ## Purge Cache
104
200
 
105
201
  If you need to remove or update an image on imgix, you can purge it from our cache by initializing a client with your api_key, then calling Imgix::Client#purge with the resource path.
@@ -13,6 +13,13 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = 'https://github.com/imgix/imgix-rb'
14
14
  spec.license = 'MIT'
15
15
 
16
+ spec.metadata = {
17
+ 'bug_tracker_uri' => 'https://github.com/imgix/imgix-rb/issues',
18
+ 'changelog_uri' => 'https://github.com/imgix/imgix-rb/blob/master/CHANGELOG.md',
19
+ 'documentation_uri' => "https://www.rubydoc.info/gems/imgix/#{spec.version}",
20
+ 'source_code_uri' => "https://github.com/imgix/imgix-rb/tree/#{spec.version}"
21
+ }
22
+
16
23
  spec.files = `git ls-files`.split($/)
17
24
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
25
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
@@ -8,20 +8,42 @@ module Imgix
8
8
  # regex pattern used to determine if a domain is valid
9
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
10
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
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
+ unless increment_percentage.is_a? Numeric and increment_percentage > 0
24
+ raise ArgumentError, "The width_tolerance argument must be passed a positive scalar value"
25
+ end
26
+
27
+ max_size = max || MAX_WIDTH
15
28
  resolutions = []
16
- prev = 100
29
+ prev = min || MIN_WIDTH
17
30
 
18
31
  while(prev <= max_size)
19
32
  # ensures that each width is even
20
33
  resolutions.push((2 * (prev / 2).round))
21
- prev *= 1 + ((increment_percentage.to_f) / 100) * 2
34
+ prev *= 1 + (increment_percentage * 2)
22
35
  end
23
36
 
24
37
  resolutions.push(max_size)
25
38
  return resolutions
26
39
  }
40
+
41
+ # hash of default quality parameter values mapped by each dpr srcset entry
42
+ DPR_QUALITY = {
43
+ 1 => 75,
44
+ 2 => 50,
45
+ 3 => 35,
46
+ 4 => 23,
47
+ 5 => 20
48
+ }
27
49
  end
@@ -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,7 +70,7 @@ 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|
@@ -84,7 +84,7 @@ module Imgix
84
84
  end
85
85
  end
86
86
 
87
- def to_srcset(params = {})
87
+ def to_srcset(options: {}, **params)
88
88
  prev_options = @options.dup
89
89
  @options.merge!(params)
90
90
 
@@ -93,13 +93,13 @@ module Imgix
93
93
  aspect_ratio = @options['ar'.to_sym]
94
94
 
95
95
  if ((width) || (height && aspect_ratio))
96
- srcset = build_dpr_srcset(@options)
96
+ srcset = build_dpr_srcset(options: options, params:@options)
97
97
  else
98
- srcset = build_srcset_pairs(@options)
98
+ srcset = build_srcset_pairs(options: options, params: @options)
99
99
  end
100
100
 
101
101
  @options = prev_options
102
- return srcset
102
+ srcset
103
103
  end
104
104
 
105
105
  private
@@ -128,27 +128,79 @@ module Imgix
128
128
  query.length > 0
129
129
  end
130
130
 
131
- def build_srcset_pairs(params)
131
+ def build_srcset_pairs(options:, params:)
132
132
  srcset = ''
133
133
 
134
- for width in @target_widths do
134
+ widths = options['widths'.to_sym] || []
135
+ width_tolerance = options['width_tolerance'.to_sym] || DEFAULT_WIDTH_TOLERANCE
136
+ min_srcset = options['min_width'.to_sym] || MIN_WIDTH
137
+ max_srcset = options['max_width'.to_sym] || MAX_WIDTH
138
+
139
+ if !widths.empty?
140
+ validate_widths!(widths)
141
+ srcset_widths = widths
142
+ elsif width_tolerance != DEFAULT_WIDTH_TOLERANCE or min_srcset != MIN_WIDTH or max_srcset != MAX_WIDTH
143
+ validate_range!(min_srcset, max_srcset)
144
+ srcset_widths = TARGET_WIDTHS.call(width_tolerance, min_srcset, max_srcset)
145
+ else
146
+ srcset_widths = @target_widths
147
+ end
148
+
149
+ for width in srcset_widths do
135
150
  params['w'.to_sym] = width
136
151
  srcset += "#{to_url(params)} #{width}w,\n"
137
152
  end
138
153
 
139
- return srcset[0..-3]
154
+ srcset[0..-3]
140
155
  end
141
156
 
142
- def build_dpr_srcset(params)
157
+ def build_dpr_srcset(options:, params:)
143
158
  srcset = ''
159
+
160
+ disable_variable_quality = options['disable_variable_quality'.to_sym] || false
161
+ validate_variable_qualities!(disable_variable_quality)
162
+
144
163
  target_ratios = [1,2,3,4,5]
164
+ quality = params['q'.to_sym]
145
165
 
146
166
  for ratio in target_ratios do
147
167
  params['dpr'.to_sym] = ratio
168
+
169
+ unless disable_variable_quality
170
+ params['q'.to_sym] = quality || DPR_QUALITY[ratio]
171
+ end
172
+
148
173
  srcset += "#{to_url(params)} #{ratio}x,\n"
149
174
  end
150
175
 
151
- return srcset[0..-3]
176
+ srcset[0..-3]
177
+ end
178
+
179
+ def validate_widths!(widths)
180
+ unless widths.is_a? Array
181
+ raise ArgumentError, "The widths argument must be passed a valid array of integers"
182
+ else
183
+ positive_integers = widths.all? {|i| i.is_a?(Integer) and i > 0}
184
+ unless positive_integers
185
+ raise ArgumentError, "A custom widths array must only contain positive integer values"
186
+ end
187
+ end
188
+ end
189
+
190
+ def validate_range!(min_srcset, max_srcset)
191
+ if min_srcset.is_a? Numeric and max_srcset.is_a? Numeric
192
+ unless min_srcset > 0 and max_srcset > 0
193
+ raise ArgumentError, "The min and max arguments must be passed positive Numeric values"
194
+ end
195
+ else
196
+ raise ArgumentError, "The min and max arguments must be passed positive Numeric values"
197
+ end
198
+ end
199
+
200
+ def validate_variable_qualities!(disable_variable_quality)
201
+ unless disable_variable_quality.is_a?(TrueClass) || disable_variable_quality.is_a?(FalseClass)
202
+ raise ArgumentError, "The disable_variable_quality argument must be passed a Boolean value"
203
+ end
152
204
  end
153
205
  end
154
206
  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.2.0'
5
5
  end
@@ -8,7 +8,7 @@ module SrcsetTest
8
8
  expected_number_of_pairs = 31
9
9
  assert_equal expected_number_of_pairs, srcset.split(',').length
10
10
  end
11
-
11
+
12
12
  def test_srcset_pair_values
13
13
  resolutions = [100, 116, 134, 156, 182, 210, 244, 282,
14
14
  328, 380, 442, 512, 594, 688, 798, 926,
@@ -19,11 +19,11 @@ module SrcsetTest
19
19
  srcset_split.split(' ')[1].to_i
20
20
  }
21
21
 
22
- for i in 0..srclist.length-1 do
22
+ for i in 0..srclist.length - 1 do
23
23
  assert_equal(srclist[i], resolutions[i])
24
24
  end
25
25
  end
26
-
26
+
27
27
  private
28
28
  def path
29
29
  @client ||= Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg')
@@ -56,10 +56,10 @@ module SrcsetTest
56
56
  assert_includes src, 's='
57
57
 
58
58
  # parses out all parameters except for 's=...'
59
- params = src[src.index('?')..src.index('s=')-2]
59
+ params = src[src.index('?')..src.index('s=') - 2]
60
60
 
61
61
  # parses out the 's=...' parameter
62
- generated_signature = src.slice(src.index('s=')+2, src.length)
62
+ generated_signature = src.slice(src.index('s=') + 2, src.length)
63
63
 
64
64
  signature_base = 'MYT0KEN' + '/image.jpg' + params;
65
65
  expected_signature = Digest::MD5.hexdigest(signature_base)
@@ -67,8 +67,44 @@ module SrcsetTest
67
67
  assert_equal expected_signature, generated_signature
68
68
  }
69
69
  end
70
-
70
+
71
+ def test_srcset_has_variable_qualities
72
+ i = 0
73
+ srcset.split(',').map { |src|
74
+ assert_includes src, "q=#{DPR_QUALITY[i]}"
75
+ i += 1
76
+ }
77
+ end
78
+
79
+ def test_srcset_respects_overriding_quality
80
+ quality_override = 100
81
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100, q:quality_override)
82
+
83
+ srcset.split(',').map { |src|
84
+ assert_includes src, "q=#{quality_override}"
85
+ }
86
+ end
87
+
88
+ def test_disable_variable_quality
89
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100, options: { disable_variable_quality: true })
90
+
91
+ srcset.split(',').map { |src|
92
+ assert(not(src.include? "q="))
93
+ }
94
+ end
95
+
96
+ def test_respects_quality_param_when_disabled
97
+ quality_override = 100
98
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100, q:100, options: { disable_variable_quality: true })
99
+
100
+ srcset.split(',').map { |src|
101
+ assert_includes src, "q=#{quality_override}"
102
+ }
103
+ end
104
+
71
105
  private
106
+ DPR_QUALITY = [75, 50, 35, 23, 20]
107
+
72
108
  def srcset
73
109
  @client ||= Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100)
74
110
  end
@@ -89,7 +125,7 @@ module SrcsetTest
89
125
  srcset_split.split(' ')[1].to_i
90
126
  }
91
127
 
92
- for i in 0..srclist.length-1 do
128
+ for i in 0..srclist.length - 1 do
93
129
  assert_equal(srclist[i], resolutions[i])
94
130
  end
95
131
  end
@@ -105,14 +141,15 @@ module SrcsetTest
105
141
 
106
142
  # parse out the width descriptor as an integer
107
143
  min = min.split(' ')[1].to_i
108
- max = max[max.length-1].split(' ')[1].to_i
144
+ max = max[max.length - 1].split(' ')[1].to_i
109
145
 
110
146
  assert_operator min, :>=, 100
111
147
  assert_operator max, :<=, 8192
112
148
  end
113
149
 
114
- def test_srcset_iterates_18_percent
115
- increment_allowed = 0.18
150
+ # a 17% testing threshold is used to account for rounding
151
+ def test_srcset_iterates_17_percent
152
+ increment_allowed = 0.17
116
153
 
117
154
  # create an array of widths
118
155
  widths = srcset.split(',').map { |src|
@@ -121,9 +158,9 @@ module SrcsetTest
121
158
 
122
159
  prev = widths[0]
123
160
 
124
- for i in 1..widths.length-1 do
161
+ for i in 1..widths.length - 1 do
125
162
  element = widths[i]
126
- assert_operator (element / prev), :<, (1 + increment_allowed)
163
+ assert_operator (element.to_f / prev.to_f), :<, (1 + increment_allowed)
127
164
  prev = element
128
165
  end
129
166
  end
@@ -134,10 +171,10 @@ module SrcsetTest
134
171
  assert_includes src, 's='
135
172
 
136
173
  # parses out all parameters except for 's=...'
137
- params = src[src.index('?')..src.index('s=')-2]
174
+ params = src[src.index('?')..src.index('s=') - 2]
138
175
 
139
176
  # parses out the 's=...' parameter
140
- generated_signature = src.slice(src.index('s=')+2, src.length)
177
+ generated_signature = src.slice(src.index('s=') + 2, src.length)
141
178
 
142
179
  signature_base = 'MYT0KEN' + '/image.jpg' + params;
143
180
  expected_signature = Digest::MD5.hexdigest(signature_base)
@@ -155,7 +192,6 @@ module SrcsetTest
155
192
  class SrcsetGivenWidthAndHeight < Imgix::Test
156
193
  def test_srcset_in_dpr_form
157
194
  device_pixel_ratio = 1
158
-
159
195
  srcset.split(',').map { |src|
160
196
  ratio = src.split(' ')[1]
161
197
  assert_equal ("#{device_pixel_ratio}x"), ratio
@@ -178,19 +214,55 @@ module SrcsetTest
178
214
  assert_includes src, 's='
179
215
 
180
216
  # parses out all parameters except for 's=...'
181
- params = src[src.index('?')..src.index('s=')-2]
217
+ params = src[src.index('?')..src.index('s=') - 2]
182
218
 
183
219
  # parses out the 's=...' parameter
184
- generated_signature = src.slice(src.index('s=')+2, src.length)
220
+ generated_signature = src.slice(src.index('s=') + 2, src.length)
185
221
 
186
222
  signature_base = 'MYT0KEN' + '/image.jpg' + params;
187
223
  expected_signature = Digest::MD5.hexdigest(signature_base)
188
-
224
+
189
225
  assert_equal expected_signature, generated_signature
190
226
  }
191
227
  end
192
228
 
229
+ def test_srcset_has_variable_qualities
230
+ i = 0
231
+ srcset.split(',').map { |src|
232
+ assert_includes src, "q=#{DPR_QUALITY[i]}"
233
+ i += 1
234
+ }
235
+ end
236
+
237
+ def test_srcset_respects_overriding_quality
238
+ quality_override = 100
239
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100, h:100, q:quality_override)
240
+
241
+ srcset.split(',').map { |src|
242
+ assert_includes src, "q=#{quality_override}"
243
+ }
244
+ end
245
+
246
+ def test_disable_variable_quality
247
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100, h:100, options: { disable_variable_quality: true })
248
+
249
+ srcset.split(',').map { |src|
250
+ assert(not(src.include? "q="))
251
+ }
252
+ end
253
+
254
+ def test_respects_quality_param_when_disabled
255
+ quality_override = 100
256
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100, h:100, q:100, options: { disable_variable_quality: true })
257
+
258
+ srcset.split(',').map { |src|
259
+ assert_includes src, "q=#{quality_override}"
260
+ }
261
+ end
262
+
193
263
  private
264
+ DPR_QUALITY = [75, 50, 35, 23, 20]
265
+
194
266
  def srcset
195
267
  @client ||= Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100,h:100)
196
268
  end
@@ -211,7 +283,7 @@ module SrcsetTest
211
283
  srcset_split.split(' ')[1].to_i
212
284
  }
213
285
 
214
- for i in 0..srclist.length-1 do
286
+ for i in 0..srclist.length - 1 do
215
287
  assert_equal(srclist[i], resolutions[i])
216
288
  end
217
289
  end
@@ -221,14 +293,15 @@ module SrcsetTest
221
293
 
222
294
  # parse out the width descriptor as an integer
223
295
  min = min.split(' ')[1].to_i
224
- max = max[max.length-1].split(' ')[1].to_i
296
+ max = max[max.length - 1].split(' ')[1].to_i
225
297
 
226
298
  assert_operator min, :>=, 100
227
299
  assert_operator max, :<=, 8192
228
300
  end
229
301
 
230
- def test_srcset_iterates_18_percent
231
- increment_allowed = 0.18
302
+ # a 17% testing threshold is used to account for rounding
303
+ def test_srcset_iterates_17_percent
304
+ increment_allowed = 0.17
232
305
 
233
306
  # create an array of widths
234
307
  widths = srcset.split(',').map { |src|
@@ -237,9 +310,9 @@ module SrcsetTest
237
310
 
238
311
  prev = widths[0]
239
312
 
240
- for i in 1..widths.length-1 do
313
+ for i in 1..widths.length - 1 do
241
314
  element = widths[i]
242
- assert_operator (element / prev), :<, (1 + increment_allowed)
315
+ assert_operator (element.to_f / prev.to_f), :<, (1 + increment_allowed)
243
316
  prev = element
244
317
  end
245
318
  end
@@ -250,10 +323,10 @@ module SrcsetTest
250
323
  assert_includes src, 's='
251
324
 
252
325
  # parses out all parameters except for 's=...'
253
- params = src[src.index('?')..src.index('s=')-2]
326
+ params = src[src.index('?')..src.index('s=') - 2]
254
327
 
255
328
  # parses out the 's=...' parameter
256
- generated_signature = src.slice(src.index('s=')+2, src.length)
329
+ generated_signature = src.slice(src.index('s=') + 2, src.length)
257
330
 
258
331
  signature_base = 'MYT0KEN' + '/image.jpg' + params;
259
332
  expected_signature = Digest::MD5.hexdigest(signature_base)
@@ -294,21 +367,341 @@ module SrcsetTest
294
367
  assert_includes src, 's='
295
368
 
296
369
  # parses out all parameters except for 's=...'
297
- params = src[src.index('?')..src.index('s=')-2]
370
+ params = src[src.index('?')..src.index('s=') - 2]
298
371
 
299
372
  # parses out the 's=...' parameter
300
- generated_signature = src.slice(src.index('s=')+2, src.length)
373
+ generated_signature = src.slice(src.index('s=') + 2, src.length)
301
374
 
302
375
  signature_base = 'MYT0KEN' + '/image.jpg' + params;
303
376
  expected_signature = Digest::MD5.hexdigest(signature_base)
304
-
377
+
305
378
  assert_equal expected_signature, generated_signature
306
379
  }
307
380
  end
308
381
 
382
+ def test_srcset_has_variable_qualities
383
+ i = 0
384
+ srcset.split(',').map { |src|
385
+ assert_includes src, "q=#{DPR_QUALITY[i]}"
386
+ i += 1
387
+ }
388
+ end
389
+
390
+ def test_srcset_respects_overriding_quality
391
+ quality_override = 100
392
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100, ar:'3:2', q:quality_override)
393
+
394
+ srcset.split(',').map { |src|
395
+ assert_includes src, "q=#{quality_override}"
396
+ }
397
+ end
398
+
399
+ def test_disable_variable_quality
400
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100, ar:'3:2', options: { disable_variable_quality: true })
401
+
402
+ srcset.split(',').map { |src|
403
+ assert(not(src.include? "q="))
404
+ }
405
+ end
406
+
407
+ def test_respects_quality_param_when_disabled
408
+ quality_override = 100
409
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(w:100, h:100, q:100, options: { disable_variable_quality: true })
410
+
411
+ srcset.split(',').map { |src|
412
+ assert_includes src, "q=#{quality_override}"
413
+ }
414
+ end
415
+
309
416
  private
417
+ DPR_QUALITY = [75, 50, 35, 23, 20]
418
+
310
419
  def srcset
311
420
  @client ||= Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset({h:100,ar:'3:2'})
312
421
  end
313
422
  end
314
- end
423
+
424
+ class SrcsetWidthTolerance < Imgix::Test
425
+ def test_srcset_generates_width_pairs
426
+ expected_number_of_pairs = 15
427
+ assert_equal expected_number_of_pairs, srcset.split(',').length
428
+ end
429
+
430
+ def test_srcset_pair_values
431
+ resolutions = [100,140,196,274,384,538,752,1054,1476,2066,2892,4050,5670,7938,8192]
432
+ srclist = srcset.split(',').map { |srcset_split|
433
+ srcset_split.split(' ')[1].to_i
434
+ }
435
+
436
+ for i in 0..srclist.length - 1 do
437
+ assert_equal(srclist[i], resolutions[i])
438
+ end
439
+ end
440
+
441
+ def test_srcset_within_bounds
442
+ min, *max = srcset.split(',')
443
+
444
+ # parse out the width descriptor as an integer
445
+ min = min.split(' ')[1].to_i
446
+ max = max[max.length - 1].split(' ')[1].to_i
447
+ assert_operator min, :>=, 100
448
+ assert_operator max, :<=, 8192
449
+ end
450
+
451
+ # a 41% testing threshold is used to account for rounding
452
+ def test_srcset_iterates_41_percent
453
+ increment_allowed = 0.41
454
+
455
+ # create an array of widths
456
+ widths = srcset.split(',').map { |src|
457
+ src.split(' ')[1].to_i
458
+ }
459
+
460
+ prev = widths[0]
461
+
462
+ for i in 1..widths.length - 1 do
463
+ element = widths[i]
464
+ assert_operator (element.to_f / prev.to_f), :<, (1 + increment_allowed)
465
+ prev = element
466
+ end
467
+ end
468
+
469
+ def test_invalid_tolerance_emits_error
470
+ assert_raises(ArgumentError) {
471
+ Imgix::Client.new(host: 'testing.imgix.net')
472
+ .path('image.jpg')
473
+ .to_srcset(options: {width_tolerance: 'abc'})
474
+ }
475
+ end
476
+
477
+ def test_negative_tolerance_emits_error
478
+ assert_raises(ArgumentError) {
479
+ Imgix::Client.new(host: 'testing.imgix.net')
480
+ .path('image.jpg')
481
+ .to_srcset(options: {width_tolerance: -0.10})
482
+ }
483
+ end
484
+
485
+ def test_with_param_after
486
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false)
487
+ .path('image.jpg')
488
+ .to_srcset(options: {width_tolerance: 0.20}, h:1000, fit:"clip")
489
+ assert_includes(srcset, "h=")
490
+ assert(not(srcset.include? "width_tolerance="))
491
+ end
492
+
493
+ def test_with_param_before
494
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false)
495
+ .path('image.jpg')
496
+ .to_srcset(h:1000, fit:"clip", options: {width_tolerance: 0.20})
497
+ assert_includes(srcset, "h=")
498
+ assert(not(srcset.include? "width_tolerance="))
499
+ end
500
+
501
+ private
502
+ def srcset
503
+ @client ||= Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(options: {width_tolerance: 0.20})
504
+ end
505
+ end
506
+
507
+ class SrcsetCustomWidths < Imgix::Test
508
+ def test_srcset_generates_width_pairs
509
+ expected_number_of_pairs = 4
510
+ assert_equal expected_number_of_pairs, srcset.split(',').length
511
+ end
512
+
513
+ def test_srcset_pair_values
514
+ resolutions = [100, 500, 1000, 1800]
515
+ srclist = srcset.split(',').map { |srcset_split|
516
+ srcset_split.split(' ')[1].to_i
517
+ }
518
+
519
+ for i in 0..srclist.length - 1 do
520
+ assert_equal(srclist[i], resolutions[i])
521
+ end
522
+ end
523
+
524
+ def test_srcset_within_bounds
525
+ min, *max = srcset.split(',')
526
+
527
+ # parse out the width descriptor as an integer
528
+ min = min.split(' ')[1].to_i
529
+ max = max[max.length - 1].split(' ')[1].to_i
530
+
531
+ assert_operator min, :>=, @widths[0]
532
+ assert_operator max, :<=, @widths[-1]
533
+ end
534
+
535
+ def test_invalid_widths_input_emits_error
536
+ assert_raises(ArgumentError) {
537
+ Imgix::Client.new(host: 'testing.imgix.net')
538
+ .path('image.jpg')
539
+ .to_srcset(options: {widths: 'abc'})
540
+ }
541
+ end
542
+
543
+ def test_non_integer_array_emits_error
544
+ assert_raises(ArgumentError) {
545
+ Imgix::Client.new(host: 'testing.imgix.net')
546
+ .path('image.jpg')
547
+ .to_srcset(options: {widths: [100, 200, false]})
548
+ }
549
+ end
550
+
551
+ def test_negative_integer_array_emits_error
552
+ assert_raises(ArgumentError) {
553
+ Imgix::Client.new(host: 'testing.imgix.net')
554
+ .path('image.jpg')
555
+ .to_srcset(options: {widths: [100, 200, -100]})
556
+ }
557
+ end
558
+
559
+ def test_with_param_after
560
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false)
561
+ .path('image.jpg')
562
+ .to_srcset(options: {widths: [100, 200, 300]}, h:1000, fit:"clip")
563
+ assert_includes(srcset, "h=")
564
+ assert(not(srcset.include? "widths="))
565
+ end
566
+
567
+ def test_with_param_before
568
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false)
569
+ .path('image.jpg')
570
+ .to_srcset(h:1000, fit:"clip", options: {widths: [100, 200, 300]})
571
+ assert_includes(srcset, "h=")
572
+ assert(not(srcset.include? "widths="))
573
+ end
574
+
575
+ private
576
+ def srcset
577
+ @widths = [100, 500, 1000, 1800]
578
+ @client ||= Imgix::Client.new(
579
+ host: 'testing.imgix.net',
580
+ include_library_param: false)
581
+ .path('image.jpg')
582
+ .to_srcset(options: {widths: @widths})
583
+ end
584
+ end
585
+
586
+ class SrcsetMinMaxWidths < Imgix::Test
587
+ def test_srcset_generates_width_pairs
588
+ expected_number_of_pairs = 11
589
+ assert_equal expected_number_of_pairs, srcset.split(',').length
590
+ end
591
+
592
+ def test_srcset_pair_values
593
+ resolutions = [500,580,672,780,906,1050,1218,1414,1640,1902,2000]
594
+ srclist = srcset.split(',').map { |srcset_split|
595
+ srcset_split.split(' ')[1].to_i
596
+ }
597
+
598
+ for i in 0..srclist.length - 1 do
599
+ assert_equal(srclist[i], resolutions[i])
600
+ end
601
+ end
602
+
603
+ def test_srcset_within_bounds
604
+ min, *max = srcset.split(',')
605
+
606
+ # parse out the width descriptor as an integer
607
+ min = min.split(' ')[1].to_i
608
+ max = max[max.length - 1].split(' ')[1].to_i
609
+
610
+ assert_operator min, :>=, @MIN
611
+ assert_operator max, :<=, @MAX
612
+ end
613
+
614
+ # a 41% testing threshold is used to account for rounding
615
+ def test_with_custom_width_tolerance
616
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', secure_url_token: 'MYT0KEN', include_library_param: false).path('image.jpg').to_srcset(options: {min_width: 500, max_width: 2000, width_tolerance: 0.20})
617
+
618
+ increment_allowed = 0.41
619
+
620
+ # create an array of widths
621
+ widths = srcset.split(',').map { |src|
622
+ src.split(' ')[1].to_i
623
+ }
624
+
625
+ prev = widths[0]
626
+
627
+ for i in 1..widths.length - 1 do
628
+ element = widths[i]
629
+ assert_operator (element.to_f / prev.to_f), :<, (1 + increment_allowed)
630
+ prev = element
631
+ end
632
+ end
633
+
634
+ def test_invalid_min_emits_error
635
+ assert_raises(ArgumentError) {
636
+ Imgix::Client.new(host: 'testing.imgix.net')
637
+ .path('image.jpg')
638
+ .to_srcset(options: {min_width: 'abc'})
639
+ }
640
+ end
641
+
642
+ def test_negative_max_emits_error
643
+ assert_raises(ArgumentError) {
644
+ Imgix::Client.new(host: 'testing.imgix.net')
645
+ .path('image.jpg')
646
+ .to_srcset(options: {max_width: -100})
647
+ }
648
+ end
649
+
650
+ def test_with_param_after
651
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', include_library_param: false)
652
+ .path('image.jpg')
653
+ .to_srcset(options: {min_width: 500, max_width:2000}, h:1000, fit:"clip")
654
+
655
+ assert_includes(srcset, "h=")
656
+ assert(not(srcset.include? "min_width="))
657
+ assert(not(srcset.include? "max_width="))
658
+ end
659
+
660
+ def test_with_param_before
661
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', include_library_param: false)
662
+ .path('image.jpg')
663
+ .to_srcset(h:1000, fit:"clip", options: {min_width: 500, max_width:2000})
664
+
665
+ assert_includes(srcset, "h=")
666
+ assert(not(srcset.include? "min_width="))
667
+ assert(not(srcset.include? "max_width="))
668
+ end
669
+
670
+ def test_only_min
671
+ min_width = 1000
672
+ max_width = 8192
673
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', include_library_param: false).path('image.jpg').to_srcset(options: {min_width: min_width})
674
+
675
+ min, *max = srcset.split(',')
676
+
677
+ # parse out the width descriptor as an integer
678
+ min = min.split(' ')[1].to_i
679
+ max = max[max.length - 1].split(' ')[1].to_i
680
+
681
+ assert_operator min, :>=, min_width
682
+ assert_operator max, :<=, max_width
683
+ end
684
+
685
+ def test_only_max
686
+ min_width = 100
687
+ max_width = 1000
688
+ srcset = Imgix::Client.new(host: 'testing.imgix.net', include_library_param: false).path('image.jpg').to_srcset(options: {max_width: max_width})
689
+ min, *max = srcset.split(',')
690
+
691
+ # parse out the width descriptor as an integer
692
+ min = min.split(' ')[1].to_i
693
+ max = max[max.length - 1].split(' ')[1].to_i
694
+
695
+ assert_operator min, :>=, min_width
696
+ assert_operator max, :<=, max_width
697
+
698
+ end
699
+
700
+ private
701
+ def srcset
702
+ @MIN = 500
703
+ @MAX = 2000
704
+ @client ||= Imgix::Client.new(host: 'testing.imgix.net', include_library_param: false).path('image.jpg').to_srcset(options: {min_width: @MIN, max_width: @MAX})
705
+ end
706
+ end
707
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imgix
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kelly Sutton
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2019-09-05 00:00:00.000000000 Z
16
+ date: 2019-11-15 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: addressable
@@ -78,7 +78,11 @@ files:
78
78
  homepage: https://github.com/imgix/imgix-rb
79
79
  licenses:
80
80
  - MIT
81
- metadata: {}
81
+ metadata:
82
+ bug_tracker_uri: https://github.com/imgix/imgix-rb/issues
83
+ changelog_uri: https://github.com/imgix/imgix-rb/blob/master/CHANGELOG.md
84
+ documentation_uri: https://www.rubydoc.info/gems/imgix/3.2.0
85
+ source_code_uri: https://github.com/imgix/imgix-rb/tree/3.2.0
82
86
  post_install_message:
83
87
  rdoc_options: []
84
88
  require_paths: