iri 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/iri.rb CHANGED
@@ -1,76 +1,78 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # (The MIT License)
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
- # It is a simple URI builder.
9
+ # Iri is a simple, immutable URI builder with a fluent interface.
29
10
  #
30
- # require 'iri'
31
- # url = Iri.new('http://google.com/')
32
- # .add(q: 'books about OOP', limit: 50)
33
- # .del(:q) // remove this query parameter
34
- # .del('limit') // remove this one too
35
- # .over(q: 'books about tennis', limit: 10) // replace these params
36
- # .scheme('https')
37
- # .host('localhost')
38
- # .port('443')
39
- # .to_s
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.
40
14
  #
41
- # For more information read
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
26
+ #
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
- # When URI is not valid.
41
+ # Exception raised when a URI is not valid and safe mode is disabled.
49
42
  class InvalidURI < StandardError; end
50
43
 
51
- # When .add(), .over(), or .del() arguments are not valid.
44
+ # Exception raised when arguments to .add(), .over(), or .del() are not valid Hashes.
52
45
  class InvalidArguments < StandardError; end
53
46
 
54
- # Makes a new object.
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, by specifying safe as FALSE.
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.
55
+ #
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.
61
58
  #
62
- # @param [String] uri URI
63
- # @param [Boolean] local Is it local (no host, port, and scheme)?
64
- # @param [Boolean] safe Should it safe?
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
65
63
  def initialize(uri = '', local: false, safe: true)
66
- @uri = uri
64
+ raise ArgumentError, "The uri can't be nil" if uri.nil?
65
+ @uri = uri.to_s
67
66
  @local = local
68
67
  @safe = safe
69
68
  end
70
69
 
71
- # Convert it to a string.
70
+ # Converts the Iri object to a string representation of the URI.
72
71
  #
73
- # @return [String] New URI
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.
74
+ #
75
+ # @return [String] String representation of the URI
74
76
  def to_s
75
77
  u = the_uri
76
78
  if @local
@@ -84,44 +86,60 @@ class Iri
84
86
  end
85
87
  end
86
88
 
87
- # Inspect it, like a string can be inspected.
89
+ # Returns a string representation of the Iri object for inspection purposes.
90
+ #
91
+ # This method is used when the object is displayed in irb/console or with puts/p.
88
92
  #
89
- # @return [String] Details of it
93
+ # @return [String] String representation for inspection
90
94
  def inspect
91
95
  @uri.to_s.inspect
92
96
  end
93
97
 
94
- # Convert it to an object of class +URI+.
98
+ # Converts the Iri object to a Ruby standard library URI object.
95
99
  #
96
- # @return [String] New URI
100
+ # @return [URI] A cloned URI object from the underlying URI
97
101
  def to_uri
98
102
  the_uri.clone
99
103
  end
100
104
 
101
- # Removes the host, the port, and the scheme and returns
102
- # only the local address, for example, converting "https://google.com/foo"
103
- # into "/foo".
105
+ # Creates a new Iri object with only the local parts of the URI.
104
106
  #
105
- # @return [Iri] Iri with no host/port/scheme
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.
110
+ #
111
+ # @return [Iri] A new Iri object with local:true and the same URI
112
+ # @see #initialize
106
113
  def to_local
107
114
  Iri.new(@uri, local: true, safe: @safe)
108
115
  end
109
116
 
110
- # Add a few query arguments.
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.
111
121
  #
112
- # For example:
122
+ # @example Adding query parameters
123
+ # Iri.new('https://google.com').add(q: 'test', limit: 10)
124
+ # # => "https://google.com?q=test&limit=10"
113
125
  #
114
- # Iri.new('https://google.com').add(q: 'test', limit: 10)
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"
115
129
  #
116
- # You can add many of them and they will all be present in the resulting
117
- # URI, even if their names are the same. In order to make sure you have
118
- # only one instance of a query argument, use +del+ first:
130
+ # You can ensure only one instance of a parameter by using +del+ first:
119
131
  #
120
- # Iri.new('https://google.com').del(:q).add(q: 'test')
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"
121
135
  #
122
- # @param [Hash] hash Hash of names/values to set into the query part
123
- # @return [Iri] A new iri
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
124
141
  def add(hash)
142
+ raise ArgumentError, "The hash can't be nil" if hash.nil?
125
143
  raise InvalidArguments unless hash.is_a?(Hash)
126
144
  modify_query do |params|
127
145
  hash.each do |k, v|
@@ -130,15 +148,24 @@ class Iri
130
148
  end
131
149
  end
132
150
  end
151
+ alias with add
133
152
 
134
- # Delete a few query arguments.
153
+ # Deletes query parameters from the URI.
135
154
  #
136
- # For example:
155
+ # This method removes all instances of the specified parameters from the query string.
137
156
  #
138
- # Iri.new('https://google.com?q=test').del(:q)
157
+ # @example Deleting a query parameter
158
+ # Iri.new('https://google.com?q=test&limit=10').del(:q)
159
+ # # => "https://google.com?limit=10"
139
160
  #
140
- # @param [Array] keys List of keys to delete
141
- # @return [Iri] A new iri
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
142
169
  def del(*keys)
143
170
  modify_query do |params|
144
171
  keys.each do |k|
@@ -146,94 +173,178 @@ class Iri
146
173
  end
147
174
  end
148
175
  end
176
+ alias without del
149
177
 
150
- # Replace query argument(s).
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.
183
+ #
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"
151
187
  #
152
- # Iri.new('https://google.com?q=test').over(q: 'hey you!')
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"
153
191
  #
154
- # @param [Hash] hash Hash of names/values to set into the query part
155
- # @return [Iri] A new iri
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
156
197
  def over(hash)
198
+ raise ArgumentError, "The hash can't be nil" if hash.nil?
157
199
  raise InvalidArguments unless hash.is_a?(Hash)
158
200
  modify_query do |params|
159
201
  hash.each do |k, v|
160
- params[k.to_s] = [] unless params[k]
202
+ params[k.to_s] = [] unless params[k.to_s]
161
203
  params[k.to_s] = [v]
162
204
  end
163
205
  end
164
206
  end
165
207
 
166
- # Replace the scheme.
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"
167
213
  #
168
214
  # @param [String] val New scheme to set, like "https" or "http"
169
- # @return [Iri] A new iri
215
+ # @return [Iri] A new Iri instance
216
+ # @see #host
217
+ # @see #port
170
218
  def scheme(val)
219
+ raise ArgumentError, "The scheme can't be nil" if val.nil?
220
+ raise ArgumentError, 'The scheme must be a String' unless val.is_a?(String)
221
+ raise ArgumentError, "The scheme can't be empty" if val.empty?
171
222
  modify do |c|
172
223
  c.scheme = val
173
224
  end
174
225
  end
175
226
 
176
- # Replace the host.
227
+ # Replaces the host part of the URI.
177
228
  #
178
- # @param [String] val New host to set, like "google.com" or "192.168.0.1"
179
- # @return [Iri] A new iri
229
+ # @example Changing the host
230
+ # Iri.new('https://google.com').host('example.com')
231
+ # # => "https://example.com"
232
+ #
233
+ # @param [String] val New host to set, like "example.com" or "192.168.0.1"
234
+ # @return [Iri] A new Iri instance
235
+ # @see #scheme
236
+ # @see #port
180
237
  def host(val)
238
+ raise ArgumentError, "The host can't be nil" if val.nil?
239
+ raise ArgumentError, 'The host must be a String' unless val.is_a?(String)
240
+ raise ArgumentError, "The host can't be empty" if val.empty?
181
241
  modify do |c|
182
242
  c.host = val
183
243
  end
184
244
  end
185
245
 
186
- # Replace the port.
246
+ # Replaces the port part of the URI.
247
+ #
248
+ # @example Changing the port
249
+ # Iri.new('https://example.com').port('8443')
250
+ # # => "https://example.com:8443"
187
251
  #
188
252
  # @param [String] val New TCP port to set, like "8080" or "443"
189
- # @return [Iri] A new iri
253
+ # @return [Iri] A new Iri instance
254
+ # @see #scheme
255
+ # @see #host
190
256
  def port(val)
257
+ raise ArgumentError, "The port can't be nil" if val.nil?
258
+ raise ArgumentError, 'The port must be an Integer' unless val.is_a?(Integer)
259
+ raise ArgumentError, "The port can'be negative" if val.negative?
260
+ raise ArgumentError, "The port can'be zero" if val.zero?
261
+ raise ArgumentError, "The port can'be larger than 65536" if val > 65_536
191
262
  modify do |c|
192
263
  c.port = val
193
264
  end
194
265
  end
195
266
 
196
- # Replace the path part of the URI.
267
+ # Replaces the path part of the URI.
268
+ #
269
+ # @example Changing the path
270
+ # Iri.new('https://example.com/foo').path('/bar/baz')
271
+ # # => "https://example.com/bar/baz"
197
272
  #
198
273
  # @param [String] val New path to set, like "/foo/bar"
199
- # @return [Iri] A new iri
274
+ # @return [Iri] A new Iri instance
275
+ # @see #query
276
+ # @see #fragment
200
277
  def path(val)
278
+ raise ArgumentError, "The path can't be nil" if val.nil?
279
+ raise ArgumentError, 'The path must be a String' unless val.is_a?(String)
280
+ raise ArgumentError, "The path can't be empty" if val.empty?
201
281
  modify do |c|
202
282
  c.path = val
203
283
  end
204
284
  end
205
285
 
206
- # Replace the fragment part of the URI.
286
+ # Replaces the fragment part of the URI (the part after #).
287
+ #
288
+ # @example Setting a fragment
289
+ # Iri.new('https://example.com/page').fragment('section2')
290
+ # # => "https://example.com/page#section2"
207
291
  #
208
- # @param [String] val New fragment to set, like "hello"
209
- # @return [Iri] A new iri
292
+ # @param [String] val New fragment to set, like "section2"
293
+ # @return [Iri] A new Iri instance
294
+ # @see #path
295
+ # @see #query
210
296
  def fragment(val)
297
+ raise ArgumentError, "The fragment can't be nil" if val.nil?
298
+ val = val.to_s
211
299
  modify do |c|
212
300
  c.fragment = val.to_s
213
301
  end
214
302
  end
215
303
 
216
- # Replace the query part of the URI.
304
+ # Replaces the entire query part of the URI.
305
+ #
306
+ # Use this method to completely replace the query string. For modifying
307
+ # individual parameters, see #add, #del, and #over.
308
+ #
309
+ # @example Setting a query string
310
+ # Iri.new('https://example.com/search').query('q=ruby&limit=10')
311
+ # # => "https://example.com/search?q=ruby&limit=10"
217
312
  #
218
- # @param [String] val New query to set, like "a=1&b=2"
219
- # @return [Iri] A new iri
313
+ # @param [String] val New query string to set, like "a=1&b=2"
314
+ # @return [Iri] A new Iri instance
315
+ # @see #add
316
+ # @see #del
317
+ # @see #over
220
318
  def query(val)
319
+ raise ArgumentError, "The query can't be nil" if val.nil?
320
+ raise ArgumentError, 'The query must be a String' unless val.is_a?(String)
221
321
  modify do |c|
222
322
  c.query = val
223
323
  end
224
324
  end
225
325
 
226
- # Remove the entire path+query+fragment part.
326
+ # Removes the entire path, query, and fragment parts and sets a new path.
227
327
  #
228
- # For example:
328
+ # This method is useful for "cutting off" everything after the host:port
329
+ # and setting a new path, effectively removing query string and fragment.
229
330
  #
230
- # Iri.new('https://google.com/a/b?q=test').cut('/hello')
331
+ # @example Cutting off path/query/fragment and setting a new path
332
+ # Iri.new('https://google.com/a/b?q=test').cut('/hello')
333
+ # # => "https://google.com/hello"
231
334
  #
232
- # The result will contain "https://google.com/hello".
335
+ # @example Resetting to root path
336
+ # Iri.new('https://google.com/a/b?q=test#section2').cut()
337
+ # # => "https://google.com/"
233
338
  #
234
- # @param [String] path New path to set, like "/foo"
235
- # @return [Iri] A new iri
339
+ # @param [String] path New path to set, defaults to "/"
340
+ # @return [Iri] A new Iri instance
341
+ # @see #path
342
+ # @see #query
343
+ # @see #fragment
236
344
  def cut(path = '/')
345
+ raise ArgumentError, "The path can't be nil" if path.nil?
346
+ raise ArgumentError, 'The path must be a String' unless path.is_a?(String)
347
+ raise ArgumentError, "The path can't be empty" if path.empty?
237
348
  modify do |c|
238
349
  c.query = nil
239
350
  c.path = path
@@ -241,17 +352,30 @@ class Iri
241
352
  end
242
353
  end
243
354
 
244
- # Append something new to the path.
355
+ # Appends a new segment to the existing path.
245
356
  #
246
- # For example:
357
+ # This method adds a new segment to the existing path, automatically handling
358
+ # the slash between segments and URL encoding the new segment.
247
359
  #
248
- # Iri.new('https://google.com/a/b?q=test').append('/hello')
360
+ # @example Appending a path segment
361
+ # Iri.new('https://example.com/a/b?q=test').append('hello')
362
+ # # => "https://example.com/a/b/hello?q=test"
249
363
  #
250
- # The result will contain "https://google.com/a/b/hello?q=test".
364
+ # @example Appending to a path with a trailing slash
365
+ # Iri.new('https://example.com/a/').append('hello')
366
+ # # => "https://example.com/a/hello?q=test"
251
367
  #
252
- # @param [String] part New segment to add to existing path
253
- # @return [Iri] A new iri
368
+ # @example Appending a segment that needs URL encoding
369
+ # Iri.new('https://example.com/docs').append('section 1')
370
+ # # => "https://example.com/docs/section%201"
371
+ #
372
+ # @param [String, #to_s] part New segment to add to the existing path
373
+ # @return [Iri] A new Iri instance
374
+ # @see #path
254
375
  def append(part)
376
+ raise ArgumentError, "The part can't be nil" if part.nil?
377
+ raise ArgumentError, 'The part must be a String' unless part.is_a?(String)
378
+ raise ArgumentError, "The part can't be empty" if part.empty?
255
379
  modify do |c|
256
380
  tail = (c.path.end_with?('/') ? '' : '/') + CGI.escape(part.to_s)
257
381
  c.path = c.path + tail
@@ -260,6 +384,14 @@ class Iri
260
384
 
261
385
  private
262
386
 
387
+ # Parses the URI string into a URI object.
388
+ #
389
+ # This method handles the safe mode by catching and handling invalid URI errors.
390
+ # When safe mode is enabled (default), invalid URIs will return the root path URI "/"
391
+ # instead of raising an exception.
392
+ #
393
+ # @return [URI] The parsed URI object
394
+ # @raise [InvalidURI] If the URI is invalid and safe mode is disabled
263
395
  def the_uri
264
396
  @the_uri ||= URI(@uri)
265
397
  rescue URI::InvalidURIError => e
@@ -267,12 +399,27 @@ class Iri
267
399
  @the_uri = URI('/')
268
400
  end
269
401
 
402
+ # Creates a new Iri object after modifying the underlying URI.
403
+ #
404
+ # This helper method clones the current URI, yields it to a block for modification,
405
+ # and then creates a new Iri object with the modified URI, preserving the local and safe flags.
406
+ #
407
+ # @yield [URI] The cloned URI object for modification
408
+ # @return [Iri] A new Iri instance with the modified URI
270
409
  def modify
271
410
  c = the_uri.clone
272
411
  yield c
273
412
  Iri.new(c, local: @local, safe: @safe)
274
413
  end
275
414
 
415
+ # Creates a new Iri object after modifying the query parameters.
416
+ #
417
+ # This helper method parses the current query string into a hash of parameter names
418
+ # to arrays of values, yields this hash for modification, and then encodes it back
419
+ # into a query string. It uses the modify method to create a new Iri object.
420
+ #
421
+ # @yield [Hash] The parsed query parameters for modification
422
+ # @return [Iri] A new Iri instance with the modified query string
276
423
  def modify_query
277
424
  modify do |c|
278
425
  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
- SimpleCov.start
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]