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
  @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:



361
362
363
364
365
366
367
# File 'lib/iri.rb', line 361

def append(part)
  raise ArgumentError, "The part can't be nil" if part.nil?
  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:



332
333
334
335
336
337
338
339
# File 'lib/iri.rb', line 332

def cut(path = '/')
  raise ArgumentError, "The path can't be nil" if path.nil?
  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:



286
287
288
289
290
291
# File 'lib/iri.rb', line 286

def fragment(val)
  raise ArgumentError, "The fragment can't be nil" if val.nil?
  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:



235
236
237
238
239
240
# File 'lib/iri.rb', line 235

def host(val)
  raise ArgumentError, "The host can't be nil" if val.nil?
  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:



269
270
271
272
273
274
# File 'lib/iri.rb', line 269

def path(val)
  raise ArgumentError, "The path can't be nil" if val.nil?
  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:



252
253
254
255
256
257
# File 'lib/iri.rb', line 252

def port(val)
  raise ArgumentError, "The port can't be nil" if val.nil?
  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:



307
308
309
310
311
312
# File 'lib/iri.rb', line 307

def query(val)
  raise ArgumentError, "The query can't be nil" if val.nil?
  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
# File 'lib/iri.rb', line 218

def scheme(val)
  raise ArgumentError, "The scheme can't be nil" if val.nil?
  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