imgix 3.1.1 → 3.2.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 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: