Class: Iri

Inherits:
Object
  • Object
show all
Defined in:
lib/iri.rb

Overview

Iri is a simple, immutable URI builder with a fluent interface.

The Iri class provides methods to manipulate different parts of a URI, including the scheme, host, port, path, query parameters, and fragment. Each method returns a new Iri instance, maintaining immutability.

For more information read the README file.

Author

Yegor Bugayenko ([email protected])

Copyright

Copyright © 2019-2025 Yegor Bugayenko

License

MIT

Examples:

Creating and manipulating a URI

require 'iri'
url = Iri.new('http://google.com/')
  .add(q: 'books about OOP', limit: 50)
  .del(:q) # remove this query parameter
  .del('limit') # remove this one too
  .over(q: 'books about tennis', limit: 10) # replace these params
  .scheme('https')
  .host('localhost')
  .port('443')
  .to_s

Using the local option

Iri.new('/path?foo=bar', local: true).to_s # => "/path?foo=bar"

Using the safe mode

Iri.new('invalid://uri', safe: true).to_s # => "/" (no exception thrown)
Iri.new('invalid://uri', safe: false) # => raises Iri::InvalidURI

Defined Under Namespace

Classes: InvalidArguments, InvalidURI

Instance Method Summary collapse

Constructor Details

#initialize(uri = '', local: false, safe: true) ⇒ Iri

Creates a new Iri object for URI manipulation.

You can even ignore the argument, which will produce an empty URI (“/”).

By default, this class will never throw any exceptions, even if your URI is not valid. It will just assume that the URI is “/”. However, you can turn this safe mode off by specifying safe as FALSE, which will cause InvalidURI to be raised if the URI is malformed.

The local parameter can be used if you only want to work with the path, query, and fragment portions of a URI, without the scheme, host, and port.

Parameters:

  • uri (String) (defaults to: '')

    URI string to parse

  • local (Boolean) (defaults to: false)

    When true, ignores scheme, host and port parts

  • safe (Boolean) (defaults to: true)

    When true, prevents InvalidURI exceptions

Raises:

  • (InvalidURI)

    If the URI is malformed and safe is false



63
64
65
66
67
68
# File 'lib/iri.rb', line 63

def initialize(uri = '', local: false, safe: true)
  raise ArgumentError, "The uri can't be nil" if uri.nil?
  @uri = uri.to_s
  @local = local
  @safe = safe
end

Instance Method Details

#add(hash) ⇒ Iri Also known as: with

Adds query parameters to the URI.

This method appends query parameters to existing ones. If a parameter with the same name already exists, both values will be present in the resulting URI.

You can ensure only one instance of a parameter by using del first:

Examples:

Adding query parameters

Iri.new('https://google.com').add(q: 'test', limit: 10)
# => "https://google.com?q=test&limit=10"

Adding parameters with the same name

Iri.new('https://google.com?q=foo').add(q: 'bar')
# => "https://google.com?q=foo&q=bar"

Replacing a parameter by deleting it first

Iri.new('https://google.com?q=foo').del(:q).add(q: 'test')
# => "https://google.com?q=test"

Parameters:

  • hash (Hash)

    Hash of parameter names/values to add to the query part

Returns:

  • (Iri)

    A new Iri instance

Raises:

See Also:



141
142
143
144
145
146
147
148
149
150
# File 'lib/iri.rb', line 141

def add(hash)
  raise ArgumentError, "The hash can't be nil" if hash.nil?
  raise InvalidArguments unless hash.is_a?(Hash)
  modify_query do |params|
    hash.each do |k, v|
      params[k.to_s] = [] unless params[k.to_s]
      params[k.to_s] << v
    end
  end
end

#append(part) ⇒ Iri

Appends a new segment to the existing path.

This method adds a new segment to the existing path, automatically handling the slash between segments and URL encoding the new segment.

Examples:

Appending a path segment

Iri.new('https://example.com/a/b?q=test').append('hello')
# => "https://example.com/a/b/hello?q=test"

Appending to a path with a trailing slash

Iri.new('https://example.com/a/').append('hello')
# => "https://example.com/a/hello?q=test"

Appending a segment that needs URL encoding

Iri.new('https://example.com/docs').append('section 1')
# => "https://example.com/docs/section%201"

Parameters:

  • part (String, #to_s)

    New segment to add to the existing path

Returns:

  • (Iri)

    A new Iri instance

Raises:

  • (ArgumentError)

See Also:



375
376
377
378
379
380
381
382
383
# File 'lib/iri.rb', line 375

def append(part)
  raise ArgumentError, "The part can't be nil" if part.nil?
  part = part.to_s
  raise ArgumentError, "The part can't be empty" if part.empty?
  modify do |c|
    tail = (c.path.end_with?('/') ? '' : '/') + CGI.escape(part.to_s)
    c.path = c.path + tail
  end
end

#cut(path = '/') ⇒ Iri

Removes the entire path, query, and fragment parts and sets a new path.

This method is useful for “cutting off” everything after the host:port and setting a new path, effectively removing query string and fragment.

Examples:

Cutting off path/query/fragment and setting a new path

Iri.new('https://google.com/a/b?q=test').cut('/hello')
# => "https://google.com/hello"

Resetting to root path

Iri.new('https://google.com/a/b?q=test#section2').cut()
# => "https://google.com/"

Parameters:

  • path (String) (defaults to: '/')

    New path to set, defaults to “/”

Returns:

  • (Iri)

    A new Iri instance

Raises:

  • (ArgumentError)

See Also:



344
345
346
347
348
349
350
351
352
353
# File 'lib/iri.rb', line 344

def cut(path = '/')
  raise ArgumentError, "The path can't be nil" if path.nil?
  path = path.to_s
  raise ArgumentError, "The path can't be empty" if path.empty?
  modify do |c|
    c.query = nil
    c.path = path
    c.fragment = nil
  end
end

#del(*keys) ⇒ Iri Also known as: without

Deletes query parameters from the URI.

This method removes all instances of the specified parameters from the query string.

Examples:

Deleting a query parameter

Iri.new('https://google.com?q=test&limit=10').del(:q)
# => "https://google.com?limit=10"

Deleting multiple parameters

Iri.new('https://google.com?q=test&limit=10&sort=asc').del(:q, :limit)
# => "https://google.com?sort=asc"

Parameters:

  • keys (Array<Symbol, String>)

    List of parameter names to delete

Returns:

  • (Iri)

    A new Iri instance

See Also:



169
170
171
172
173
174
175
# File 'lib/iri.rb', line 169

def del(*keys)
  modify_query do |params|
    keys.each do |k|
      params.delete(k.to_s)
    end
  end
end

#fragment(val) ⇒ Iri

Replaces the fragment part of the URI (the part after #).

Examples:

Setting a fragment

Iri.new('https://example.com/page').fragment('section2')
# => "https://example.com/page#section2"

Parameters:

  • val (String)

    New fragment to set, like “section2”

Returns:

  • (Iri)

    A new Iri instance

Raises:

  • (ArgumentError)

See Also:



296
297
298
299
300
301
302
# File 'lib/iri.rb', line 296

def fragment(val)
  raise ArgumentError, "The fragment can't be nil" if val.nil?
  val = val.to_s
  modify do |c|
    c.fragment = val.to_s
  end
end

#host(val) ⇒ Iri

Replaces the host part of the URI.

Examples:

Changing the host

Iri.new('https://google.com').host('example.com')
# => "https://example.com"

Parameters:

  • val (String)

    New host to set, like “example.com” or “192.168.0.1”

Returns:

  • (Iri)

    A new Iri instance

Raises:

  • (ArgumentError)

See Also:



237
238
239
240
241
242
243
244
# File 'lib/iri.rb', line 237

def host(val)
  raise ArgumentError, "The host can't be nil" if val.nil?
  val = val.to_s
  raise ArgumentError, "The host can't be empty" if val.empty?
  modify do |c|
    c.host = val
  end
end

#inspectString

Returns a string representation of the Iri object for inspection purposes.

This method is used when the object is displayed in irb/console or with puts/p.

Returns:

  • (String)

    String representation for inspection



94
95
96
# File 'lib/iri.rb', line 94

def inspect
  @uri.to_s.inspect
end

#over(hash) ⇒ Iri

Replaces query parameters in the URI.

Unlike #add, this method replaces any existing parameters with the same name rather than adding additional instances. If a parameter doesn’t exist, it will be added.

Examples:

Replacing a query parameter

Iri.new('https://google.com?q=test').over(q: 'hey you!')
# => "https://google.com?q=hey+you%21"

Replacing multiple parameters

Iri.new('https://google.com?q=test&limit=5').over(q: 'books', limit: 10)
# => "https://google.com?q=books&limit=10"

Parameters:

  • hash (Hash)

    Hash of parameter names/values to replace in the query part

Returns:

  • (Iri)

    A new Iri instance

Raises:

See Also:



197
198
199
200
201
202
203
204
205
206
# File 'lib/iri.rb', line 197

def over(hash)
  raise ArgumentError, "The hash can't be nil" if hash.nil?
  raise InvalidArguments unless hash.is_a?(Hash)
  modify_query do |params|
    hash.each do |k, v|
      params[k.to_s] = [] unless params[k.to_s]
      params[k.to_s] = [v]
    end
  end
end

#path(val) ⇒ Iri

Replaces the path part of the URI.

Examples:

Changing the path

Iri.new('https://example.com/foo').path('/bar/baz')
# => "https://example.com/bar/baz"

Parameters:

  • val (String)

    New path to set, like “/foo/bar”

Returns:

  • (Iri)

    A new Iri instance

Raises:

  • (ArgumentError)

See Also:



277
278
279
280
281
282
283
284
# File 'lib/iri.rb', line 277

def path(val)
  raise ArgumentError, "The path can't be nil" if val.nil?
  val = val.to_s
  raise ArgumentError, "The path can't be empty" if val.empty?
  modify do |c|
    c.path = val
  end
end

#port(val) ⇒ Iri

Replaces the port part of the URI.

Examples:

Changing the port

Iri.new('https://example.com').port('8443')
# => "https://example.com:8443"

Parameters:

  • val (String)

    New TCP port to set, like “8080” or “443”

Returns:

  • (Iri)

    A new Iri instance

Raises:

  • (ArgumentError)

See Also:



256
257
258
259
260
261
262
263
264
265
# File 'lib/iri.rb', line 256

def port(val)
  raise ArgumentError, "The port can't be nil" if val.nil?
  val = val.to_i
  raise ArgumentError, "The port can'be negative" if val.negative?
  raise ArgumentError, "The port can'be zero" if val.zero?
  raise ArgumentError, "The port can'be larger than 65536" if val > 65_536
  modify do |c|
    c.port = val
  end
end

#query(val) ⇒ Iri

Replaces the entire query part of the URI.

Use this method to completely replace the query string. For modifying individual parameters, see #add, #del, and #over.

Examples:

Setting a query string

Iri.new('https://example.com/search').query('q=ruby&limit=10')
# => "https://example.com/search?q=ruby&limit=10"

Parameters:

  • val (String)

    New query string to set, like “a=1&b=2”

Returns:

  • (Iri)

    A new Iri instance

Raises:

  • (ArgumentError)

See Also:



318
319
320
321
322
323
324
# File 'lib/iri.rb', line 318

def query(val)
  raise ArgumentError, "The query can't be nil" if val.nil?
  val = val.to_s
  modify do |c|
    c.query = val
  end
end

#scheme(val) ⇒ Iri

Replaces the scheme part of the URI.

Examples:

Changing the scheme

Iri.new('http://google.com').scheme('https')
# => "https://google.com"

Parameters:

  • val (String)

    New scheme to set, like “https” or “http”

Returns:

  • (Iri)

    A new Iri instance

Raises:

  • (ArgumentError)

See Also:



218
219
220
221
222
223
224
225
# File 'lib/iri.rb', line 218

def scheme(val)
  raise ArgumentError, "The scheme can't be nil" if val.nil?
  val = val.to_s
  raise ArgumentError, "The scheme can't be empty" if val.empty?
  modify do |c|
    c.scheme = val
  end
end

#to_localIri

Creates a new Iri object with only the local parts of the URI.

Removes the host, the port, and the scheme, returning only the local address. For example, converting “google.com/foo” into “/foo”. The path, query string, and fragment are preserved.

Returns:

  • (Iri)

    A new Iri object with local:true and the same URI

See Also:



113
114
115
# File 'lib/iri.rb', line 113

def to_local
  Iri.new(@uri, local: true, safe: @safe)
end

#to_sString

Converts the Iri object to a string representation of the URI.

When local mode is enabled, only the path, query, and fragment parts are included. Otherwise, the full URI including scheme, host, and port is returned.

Returns:

  • (String)

    String representation of the URI



76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/iri.rb', line 76

def to_s
  u = the_uri
  if @local
    [
      u.path,
      u.query ? "?#{u.query}" : '',
      u.fragment ? "##{u.fragment}" : ''
    ].join
  else
    u.to_s
  end
end

#to_uriURI

Converts the Iri object to a Ruby standard library URI object.

Returns:

  • (URI)

    A cloned URI object from the underlying URI



101
102
103
# File 'lib/iri.rb', line 101

def to_uri
  the_uri.clone
end