iri 0.9.0 → 0.11.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 +4 -4
- data/.0pdd.yml +2 -21
- data/.github/workflows/actionlint.yml +4 -22
- data/.github/workflows/codecov.yml +5 -22
- data/.github/workflows/copyrights.yml +5 -22
- data/.github/workflows/markdown-lint.yml +5 -22
- data/.github/workflows/pdd.yml +4 -21
- data/.github/workflows/rake.yml +4 -23
- data/.github/workflows/reuse.yml +19 -0
- data/.github/workflows/typos.yml +19 -0
- data/.github/workflows/xcop.yml +4 -21
- data/.github/workflows/yamllint.yml +4 -21
- data/.gitignore +7 -2
- data/.rubocop.yml +5 -24
- data/.rultor.yml +4 -22
- data/Gemfile +13 -27
- data/Gemfile.lock +54 -37
- data/LICENSES/MIT.txt +21 -0
- data/README.md +9 -9
- data/REUSE.toml +36 -0
- data/Rakefile +8 -35
- data/iri.gemspec +3 -22
- data/lib/iri.rb +243 -106
- data/test/test__helper.rb +25 -20
- data/test/test_iri.rb +89 -24
- metadata +7 -6
data/lib/iri.rb
CHANGED
@@ -1,121 +1,145 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# (
|
4
|
-
#
|
5
|
-
# Copyright (c) 2019-2025 Yegor Bugayenko
|
6
|
-
#
|
7
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
-
# of this software and associated documentation files (the 'Software'), to deal
|
9
|
-
# in the Software without restriction, including without limitation the rights
|
10
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
-
# copies of the Software, and to permit persons to whom the Software is
|
12
|
-
# furnished to do so, subject to the following conditions:
|
13
|
-
#
|
14
|
-
# The above copyright notice and this permission notice shall be included in all
|
15
|
-
# copies or substantial portions of the Software.
|
16
|
-
#
|
17
|
-
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
-
# SOFTWARE.
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
24
5
|
|
25
6
|
require 'uri'
|
26
7
|
require 'cgi'
|
27
8
|
|
28
|
-
#
|
9
|
+
# Iri is a simple, immutable URI builder with a fluent interface.
|
10
|
+
#
|
11
|
+
# The Iri class provides methods to manipulate different parts of a URI,
|
12
|
+
# including the scheme, host, port, path, query parameters, and fragment.
|
13
|
+
# Each method returns a new Iri instance, maintaining immutability.
|
29
14
|
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
15
|
+
# @example Creating and manipulating a URI
|
16
|
+
# require 'iri'
|
17
|
+
# url = Iri.new('http://google.com/')
|
18
|
+
# .add(q: 'books about OOP', limit: 50)
|
19
|
+
# .del(:q) # remove this query parameter
|
20
|
+
# .del('limit') # remove this one too
|
21
|
+
# .over(q: 'books about tennis', limit: 10) # replace these params
|
22
|
+
# .scheme('https')
|
23
|
+
# .host('localhost')
|
24
|
+
# .port('443')
|
25
|
+
# .to_s
|
40
26
|
#
|
41
|
-
#
|
27
|
+
# @example Using the local option
|
28
|
+
# Iri.new('/path?foo=bar', local: true).to_s # => "/path?foo=bar"
|
29
|
+
#
|
30
|
+
# @example Using the safe mode
|
31
|
+
# Iri.new('invalid://uri', safe: true).to_s # => "/" (no exception thrown)
|
32
|
+
# Iri.new('invalid://uri', safe: false) # => raises Iri::InvalidURI
|
33
|
+
#
|
34
|
+
# For more information read the
|
42
35
|
# {README}[https://github.com/yegor256/iri/blob/master/README.md] file.
|
43
36
|
#
|
44
37
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
45
38
|
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
46
39
|
# License:: MIT
|
47
40
|
class Iri
|
48
|
-
#
|
41
|
+
# Exception raised when a URI is not valid and safe mode is disabled.
|
49
42
|
class InvalidURI < StandardError; end
|
50
43
|
|
51
|
-
#
|
44
|
+
# Exception raised when arguments to .add(), .over(), or .del() are not valid Hashes.
|
52
45
|
class InvalidArguments < StandardError; end
|
53
46
|
|
54
|
-
#
|
47
|
+
# Creates a new Iri object for URI manipulation.
|
55
48
|
#
|
56
|
-
# You can even ignore the argument, which will produce an empty URI.
|
49
|
+
# You can even ignore the argument, which will produce an empty URI ("/").
|
57
50
|
#
|
58
51
|
# By default, this class will never throw any exceptions, even if your URI
|
59
|
-
# is not valid. It will just assume that the URI is"/". However,
|
60
|
-
# you can turn this mode off
|
52
|
+
# is not valid. It will just assume that the URI is "/". However,
|
53
|
+
# you can turn this safe mode off by specifying safe as FALSE, which will
|
54
|
+
# cause InvalidURI to be raised if the URI is malformed.
|
61
55
|
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
|
56
|
+
# The local parameter can be used if you only want to work with the path,
|
57
|
+
# query, and fragment portions of a URI, without the scheme, host, and port.
|
58
|
+
#
|
59
|
+
# @param [String] uri URI string to parse
|
60
|
+
# @param [Boolean] local When true, ignores scheme, host and port parts
|
61
|
+
# @param [Boolean] safe When true, prevents InvalidURI exceptions
|
62
|
+
# @raise [InvalidURI] If the URI is malformed and safe is false
|
63
|
+
def initialize(uri = '', local: false, safe: true)
|
64
|
+
raise ArgumentError, "The uri can't be nil" if uri.nil?
|
65
65
|
@uri = uri
|
66
|
+
@local = local
|
66
67
|
@safe = safe
|
67
68
|
end
|
68
69
|
|
69
|
-
#
|
70
|
+
# Converts the Iri object to a string representation of the URI.
|
71
|
+
#
|
72
|
+
# When local mode is enabled, only the path, query, and fragment parts are included.
|
73
|
+
# Otherwise, the full URI including scheme, host, and port is returned.
|
70
74
|
#
|
71
|
-
# @return [String]
|
75
|
+
# @return [String] String representation of the URI
|
72
76
|
def to_s
|
73
|
-
|
77
|
+
u = the_uri
|
78
|
+
if @local
|
79
|
+
[
|
80
|
+
u.path,
|
81
|
+
u.query ? "?#{u.query}" : '',
|
82
|
+
u.fragment ? "##{u.fragment}" : ''
|
83
|
+
].join
|
84
|
+
else
|
85
|
+
u.to_s
|
86
|
+
end
|
74
87
|
end
|
75
88
|
|
76
|
-
#
|
89
|
+
# Returns a string representation of the Iri object for inspection purposes.
|
77
90
|
#
|
78
|
-
#
|
91
|
+
# This method is used when the object is displayed in irb/console or with puts/p.
|
92
|
+
#
|
93
|
+
# @return [String] String representation for inspection
|
79
94
|
def inspect
|
80
95
|
@uri.to_s.inspect
|
81
96
|
end
|
82
97
|
|
83
|
-
#
|
98
|
+
# Converts the Iri object to a Ruby standard library URI object.
|
84
99
|
#
|
85
|
-
# @return [
|
100
|
+
# @return [URI] A cloned URI object from the underlying URI
|
86
101
|
def to_uri
|
87
102
|
the_uri.clone
|
88
103
|
end
|
89
104
|
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
105
|
+
# Creates a new Iri object with only the local parts of the URI.
|
106
|
+
#
|
107
|
+
# Removes the host, the port, and the scheme, returning only the local address.
|
108
|
+
# For example, converting "https://google.com/foo" into "/foo".
|
109
|
+
# The path, query string, and fragment are preserved.
|
93
110
|
#
|
94
|
-
# @return [
|
111
|
+
# @return [Iri] A new Iri object with local:true and the same URI
|
112
|
+
# @see #initialize
|
95
113
|
def to_local
|
96
|
-
|
97
|
-
[
|
98
|
-
u.path,
|
99
|
-
u.query ? "?#{u.query}" : '',
|
100
|
-
u.fragment ? "##{u.fragment}" : ''
|
101
|
-
].join
|
114
|
+
Iri.new(@uri, local: true, safe: @safe)
|
102
115
|
end
|
103
116
|
|
104
|
-
#
|
117
|
+
# Adds query parameters to the URI.
|
118
|
+
#
|
119
|
+
# This method appends query parameters to existing ones. If a parameter with the same
|
120
|
+
# name already exists, both values will be present in the resulting URI.
|
105
121
|
#
|
106
|
-
#
|
122
|
+
# @example Adding query parameters
|
123
|
+
# Iri.new('https://google.com').add(q: 'test', limit: 10)
|
124
|
+
# # => "https://google.com?q=test&limit=10"
|
107
125
|
#
|
108
|
-
#
|
126
|
+
# @example Adding parameters with the same name
|
127
|
+
# Iri.new('https://google.com?q=foo').add(q: 'bar')
|
128
|
+
# # => "https://google.com?q=foo&q=bar"
|
109
129
|
#
|
110
|
-
# You can
|
111
|
-
# URI, even if their names are the same. In order to make sure you have
|
112
|
-
# only one instance of a query argument, use +del+ first:
|
130
|
+
# You can ensure only one instance of a parameter by using +del+ first:
|
113
131
|
#
|
114
|
-
#
|
132
|
+
# @example Replacing a parameter by deleting it first
|
133
|
+
# Iri.new('https://google.com?q=foo').del(:q).add(q: 'test')
|
134
|
+
# # => "https://google.com?q=test"
|
115
135
|
#
|
116
|
-
# @param [Hash] hash Hash of names/values to
|
117
|
-
# @return [Iri] A new
|
136
|
+
# @param [Hash] hash Hash of parameter names/values to add to the query part
|
137
|
+
# @return [Iri] A new Iri instance
|
138
|
+
# @raise [InvalidArguments] If the argument is not a Hash
|
139
|
+
# @see #del
|
140
|
+
# @see #over
|
118
141
|
def add(hash)
|
142
|
+
raise ArgumentError, "The hash can't be nil" if hash.nil?
|
119
143
|
raise InvalidArguments unless hash.is_a?(Hash)
|
120
144
|
modify_query do |params|
|
121
145
|
hash.each do |k, v|
|
@@ -124,15 +148,24 @@ class Iri
|
|
124
148
|
end
|
125
149
|
end
|
126
150
|
end
|
151
|
+
alias with add
|
127
152
|
|
128
|
-
#
|
153
|
+
# Deletes query parameters from the URI.
|
129
154
|
#
|
130
|
-
#
|
155
|
+
# This method removes all instances of the specified parameters from the query string.
|
131
156
|
#
|
132
|
-
#
|
157
|
+
# @example Deleting a query parameter
|
158
|
+
# Iri.new('https://google.com?q=test&limit=10').del(:q)
|
159
|
+
# # => "https://google.com?limit=10"
|
133
160
|
#
|
134
|
-
# @
|
135
|
-
#
|
161
|
+
# @example Deleting multiple parameters
|
162
|
+
# Iri.new('https://google.com?q=test&limit=10&sort=asc').del(:q, :limit)
|
163
|
+
# # => "https://google.com?sort=asc"
|
164
|
+
#
|
165
|
+
# @param [Array<Symbol, String>] keys List of parameter names to delete
|
166
|
+
# @return [Iri] A new Iri instance
|
167
|
+
# @see #add
|
168
|
+
# @see #over
|
136
169
|
def del(*keys)
|
137
170
|
modify_query do |params|
|
138
171
|
keys.each do |k|
|
@@ -140,94 +173,164 @@ class Iri
|
|
140
173
|
end
|
141
174
|
end
|
142
175
|
end
|
176
|
+
alias without del
|
143
177
|
|
144
|
-
#
|
178
|
+
# Replaces query parameters in the URI.
|
179
|
+
#
|
180
|
+
# Unlike #add, this method replaces any existing parameters with the same name
|
181
|
+
# rather than adding additional instances. If a parameter doesn't exist,
|
182
|
+
# it will be added.
|
145
183
|
#
|
146
|
-
#
|
184
|
+
# @example Replacing a query parameter
|
185
|
+
# Iri.new('https://google.com?q=test').over(q: 'hey you!')
|
186
|
+
# # => "https://google.com?q=hey+you%21"
|
147
187
|
#
|
148
|
-
# @
|
149
|
-
#
|
188
|
+
# @example Replacing multiple parameters
|
189
|
+
# Iri.new('https://google.com?q=test&limit=5').over(q: 'books', limit: 10)
|
190
|
+
# # => "https://google.com?q=books&limit=10"
|
191
|
+
#
|
192
|
+
# @param [Hash] hash Hash of parameter names/values to replace in the query part
|
193
|
+
# @return [Iri] A new Iri instance
|
194
|
+
# @raise [InvalidArguments] If the argument is not a Hash
|
195
|
+
# @see #add
|
196
|
+
# @see #del
|
150
197
|
def over(hash)
|
198
|
+
raise ArgumentError, "The hash can't be nil" if hash.nil?
|
151
199
|
raise InvalidArguments unless hash.is_a?(Hash)
|
152
200
|
modify_query do |params|
|
153
201
|
hash.each do |k, v|
|
154
|
-
params[k.to_s] = [] unless params[k]
|
202
|
+
params[k.to_s] = [] unless params[k.to_s]
|
155
203
|
params[k.to_s] = [v]
|
156
204
|
end
|
157
205
|
end
|
158
206
|
end
|
159
207
|
|
160
|
-
#
|
208
|
+
# Replaces the scheme part of the URI.
|
209
|
+
#
|
210
|
+
# @example Changing the scheme
|
211
|
+
# Iri.new('http://google.com').scheme('https')
|
212
|
+
# # => "https://google.com"
|
161
213
|
#
|
162
214
|
# @param [String] val New scheme to set, like "https" or "http"
|
163
|
-
# @return [Iri] A new
|
215
|
+
# @return [Iri] A new Iri instance
|
216
|
+
# @see #host
|
217
|
+
# @see #port
|
164
218
|
def scheme(val)
|
219
|
+
raise ArgumentError, "The scheme can't be nil" if val.nil?
|
165
220
|
modify do |c|
|
166
221
|
c.scheme = val
|
167
222
|
end
|
168
223
|
end
|
169
224
|
|
170
|
-
#
|
225
|
+
# Replaces the host part of the URI.
|
226
|
+
#
|
227
|
+
# @example Changing the host
|
228
|
+
# Iri.new('https://google.com').host('example.com')
|
229
|
+
# # => "https://example.com"
|
171
230
|
#
|
172
|
-
# @param [String] val New host to set, like "
|
173
|
-
# @return [Iri] A new
|
231
|
+
# @param [String] val New host to set, like "example.com" or "192.168.0.1"
|
232
|
+
# @return [Iri] A new Iri instance
|
233
|
+
# @see #scheme
|
234
|
+
# @see #port
|
174
235
|
def host(val)
|
236
|
+
raise ArgumentError, "The host can't be nil" if val.nil?
|
175
237
|
modify do |c|
|
176
238
|
c.host = val
|
177
239
|
end
|
178
240
|
end
|
179
241
|
|
180
|
-
#
|
242
|
+
# Replaces the port part of the URI.
|
243
|
+
#
|
244
|
+
# @example Changing the port
|
245
|
+
# Iri.new('https://example.com').port('8443')
|
246
|
+
# # => "https://example.com:8443"
|
181
247
|
#
|
182
248
|
# @param [String] val New TCP port to set, like "8080" or "443"
|
183
|
-
# @return [Iri] A new
|
249
|
+
# @return [Iri] A new Iri instance
|
250
|
+
# @see #scheme
|
251
|
+
# @see #host
|
184
252
|
def port(val)
|
253
|
+
raise ArgumentError, "The port can't be nil" if val.nil?
|
185
254
|
modify do |c|
|
186
255
|
c.port = val
|
187
256
|
end
|
188
257
|
end
|
189
258
|
|
190
|
-
#
|
259
|
+
# Replaces the path part of the URI.
|
260
|
+
#
|
261
|
+
# @example Changing the path
|
262
|
+
# Iri.new('https://example.com/foo').path('/bar/baz')
|
263
|
+
# # => "https://example.com/bar/baz"
|
191
264
|
#
|
192
265
|
# @param [String] val New path to set, like "/foo/bar"
|
193
|
-
# @return [Iri] A new
|
266
|
+
# @return [Iri] A new Iri instance
|
267
|
+
# @see #query
|
268
|
+
# @see #fragment
|
194
269
|
def path(val)
|
270
|
+
raise ArgumentError, "The path can't be nil" if val.nil?
|
195
271
|
modify do |c|
|
196
272
|
c.path = val
|
197
273
|
end
|
198
274
|
end
|
199
275
|
|
200
|
-
#
|
276
|
+
# Replaces the fragment part of the URI (the part after #).
|
201
277
|
#
|
202
|
-
# @
|
203
|
-
#
|
278
|
+
# @example Setting a fragment
|
279
|
+
# Iri.new('https://example.com/page').fragment('section2')
|
280
|
+
# # => "https://example.com/page#section2"
|
281
|
+
#
|
282
|
+
# @param [String] val New fragment to set, like "section2"
|
283
|
+
# @return [Iri] A new Iri instance
|
284
|
+
# @see #path
|
285
|
+
# @see #query
|
204
286
|
def fragment(val)
|
287
|
+
raise ArgumentError, "The fragment can't be nil" if val.nil?
|
205
288
|
modify do |c|
|
206
289
|
c.fragment = val.to_s
|
207
290
|
end
|
208
291
|
end
|
209
292
|
|
210
|
-
#
|
293
|
+
# Replaces the entire query part of the URI.
|
294
|
+
#
|
295
|
+
# Use this method to completely replace the query string. For modifying
|
296
|
+
# individual parameters, see #add, #del, and #over.
|
211
297
|
#
|
212
|
-
# @
|
213
|
-
#
|
298
|
+
# @example Setting a query string
|
299
|
+
# Iri.new('https://example.com/search').query('q=ruby&limit=10')
|
300
|
+
# # => "https://example.com/search?q=ruby&limit=10"
|
301
|
+
#
|
302
|
+
# @param [String] val New query string to set, like "a=1&b=2"
|
303
|
+
# @return [Iri] A new Iri instance
|
304
|
+
# @see #add
|
305
|
+
# @see #del
|
306
|
+
# @see #over
|
214
307
|
def query(val)
|
308
|
+
raise ArgumentError, "The query can't be nil" if val.nil?
|
215
309
|
modify do |c|
|
216
310
|
c.query = val
|
217
311
|
end
|
218
312
|
end
|
219
313
|
|
220
|
-
#
|
314
|
+
# Removes the entire path, query, and fragment parts and sets a new path.
|
221
315
|
#
|
222
|
-
#
|
316
|
+
# This method is useful for "cutting off" everything after the host:port
|
317
|
+
# and setting a new path, effectively removing query string and fragment.
|
223
318
|
#
|
224
|
-
#
|
319
|
+
# @example Cutting off path/query/fragment and setting a new path
|
320
|
+
# Iri.new('https://google.com/a/b?q=test').cut('/hello')
|
321
|
+
# # => "https://google.com/hello"
|
225
322
|
#
|
226
|
-
#
|
323
|
+
# @example Resetting to root path
|
324
|
+
# Iri.new('https://google.com/a/b?q=test#section2').cut()
|
325
|
+
# # => "https://google.com/"
|
227
326
|
#
|
228
|
-
# @param [String] path New path to set,
|
229
|
-
# @return [Iri] A new
|
327
|
+
# @param [String] path New path to set, defaults to "/"
|
328
|
+
# @return [Iri] A new Iri instance
|
329
|
+
# @see #path
|
330
|
+
# @see #query
|
331
|
+
# @see #fragment
|
230
332
|
def cut(path = '/')
|
333
|
+
raise ArgumentError, "The path can't be nil" if path.nil?
|
231
334
|
modify do |c|
|
232
335
|
c.query = nil
|
233
336
|
c.path = path
|
@@ -235,17 +338,28 @@ class Iri
|
|
235
338
|
end
|
236
339
|
end
|
237
340
|
|
238
|
-
#
|
341
|
+
# Appends a new segment to the existing path.
|
342
|
+
#
|
343
|
+
# This method adds a new segment to the existing path, automatically handling
|
344
|
+
# the slash between segments and URL encoding the new segment.
|
239
345
|
#
|
240
|
-
#
|
346
|
+
# @example Appending a path segment
|
347
|
+
# Iri.new('https://example.com/a/b?q=test').append('hello')
|
348
|
+
# # => "https://example.com/a/b/hello?q=test"
|
241
349
|
#
|
242
|
-
#
|
350
|
+
# @example Appending to a path with a trailing slash
|
351
|
+
# Iri.new('https://example.com/a/').append('hello')
|
352
|
+
# # => "https://example.com/a/hello?q=test"
|
243
353
|
#
|
244
|
-
#
|
354
|
+
# @example Appending a segment that needs URL encoding
|
355
|
+
# Iri.new('https://example.com/docs').append('section 1')
|
356
|
+
# # => "https://example.com/docs/section%201"
|
245
357
|
#
|
246
|
-
# @param [String] part New segment to add to existing path
|
247
|
-
# @return [Iri] A new
|
358
|
+
# @param [String, #to_s] part New segment to add to the existing path
|
359
|
+
# @return [Iri] A new Iri instance
|
360
|
+
# @see #path
|
248
361
|
def append(part)
|
362
|
+
raise ArgumentError, "The part can't be nil" if part.nil?
|
249
363
|
modify do |c|
|
250
364
|
tail = (c.path.end_with?('/') ? '' : '/') + CGI.escape(part.to_s)
|
251
365
|
c.path = c.path + tail
|
@@ -254,6 +368,14 @@ class Iri
|
|
254
368
|
|
255
369
|
private
|
256
370
|
|
371
|
+
# Parses the URI string into a URI object.
|
372
|
+
#
|
373
|
+
# This method handles the safe mode by catching and handling invalid URI errors.
|
374
|
+
# When safe mode is enabled (default), invalid URIs will return the root path URI "/"
|
375
|
+
# instead of raising an exception.
|
376
|
+
#
|
377
|
+
# @return [URI] The parsed URI object
|
378
|
+
# @raise [InvalidURI] If the URI is invalid and safe mode is disabled
|
257
379
|
def the_uri
|
258
380
|
@the_uri ||= URI(@uri)
|
259
381
|
rescue URI::InvalidURIError => e
|
@@ -261,12 +383,27 @@ class Iri
|
|
261
383
|
@the_uri = URI('/')
|
262
384
|
end
|
263
385
|
|
386
|
+
# Creates a new Iri object after modifying the underlying URI.
|
387
|
+
#
|
388
|
+
# This helper method clones the current URI, yields it to a block for modification,
|
389
|
+
# and then creates a new Iri object with the modified URI, preserving the local and safe flags.
|
390
|
+
#
|
391
|
+
# @yield [URI] The cloned URI object for modification
|
392
|
+
# @return [Iri] A new Iri instance with the modified URI
|
264
393
|
def modify
|
265
394
|
c = the_uri.clone
|
266
395
|
yield c
|
267
|
-
Iri.new(c)
|
396
|
+
Iri.new(c, local: @local, safe: @safe)
|
268
397
|
end
|
269
398
|
|
399
|
+
# Creates a new Iri object after modifying the query parameters.
|
400
|
+
#
|
401
|
+
# This helper method parses the current query string into a hash of parameter names
|
402
|
+
# to arrays of values, yields this hash for modification, and then encodes it back
|
403
|
+
# into a query string. It uses the modify method to create a new Iri object.
|
404
|
+
#
|
405
|
+
# @yield [Hash] The parsed query parameters for modification
|
406
|
+
# @return [Iri] A new Iri instance with the modified query string
|
270
407
|
def modify_query
|
271
408
|
modify do |c|
|
272
409
|
params = CGI.parse(the_uri.query || '').map do |p, a|
|
data/test/test__helper.rb
CHANGED
@@ -1,26 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright (c) 2019-2025 Yegor Bugayenko
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the 'Software'), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in all
|
13
|
-
# copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
-
# SOFTWARE.
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
22
5
|
|
23
6
|
$stdout.sync = true
|
24
7
|
|
25
8
|
require 'simplecov'
|
26
|
-
|
9
|
+
require 'simplecov-cobertura'
|
10
|
+
unless SimpleCov.running
|
11
|
+
SimpleCov.command_name('test')
|
12
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
|
13
|
+
[
|
14
|
+
SimpleCov::Formatter::HTMLFormatter,
|
15
|
+
SimpleCov::Formatter::CoberturaFormatter
|
16
|
+
]
|
17
|
+
)
|
18
|
+
SimpleCov.minimum_coverage 100
|
19
|
+
SimpleCov.minimum_coverage_by_file 100
|
20
|
+
SimpleCov.start do
|
21
|
+
add_filter 'test/'
|
22
|
+
add_filter 'vendor/'
|
23
|
+
add_filter 'target/'
|
24
|
+
track_files 'lib/**/*.rb'
|
25
|
+
track_files '*.rb'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'minitest/autorun'
|
30
|
+
require 'minitest/reporters'
|
31
|
+
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
|