Skip to Content
Course content

80: URI Module for Parsing URLs

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this happen more times than I can count: a developer needs to grab a specific piece of data from a URL, and their first instinct is to treat that URL as a simple string. It seems harmless enough until the edge cases hit you like a ton of bricks.

# The "Quick and Dirty" approach that will eventually break
url = "https://api.service.com:8080/v1/search?q=ruby+uri&limit=10#results"

# I just want the domain and the search query
domain = url.split('/')[2] 
query_string = url.split('?')[1].split('&')[0].split('=')[1]

puts "Domain: #{domain}" # "api.service.com:8080" (Wait, I wanted just the host!)
puts "Query: #{query_string}" # "ruby+uri" (Wait, it's still URL-encoded!)

The Fragility of String Splitting

The code above looks like it works for a moment, but it's a landmine. Notice how the domain includes the port :8080? If you were trying to compare that domain against a whitelist, your check would fail. Even worse, the query value is ruby+uri. In a real application, you can't just leave the + or %20 characters in there; you need the actual decoded string.

Then there's the fragment #results. If the URL didn't have a query string but did have a fragment, url.split('?')[1] would return nil, and your app would crash with a NoMethodError. You can't possibly write enough if/else statements to cover every permutation of a valid URI.

Letting the URI Module Handle the Heavy Lifting

Ruby comes with a built-in URI module specifically designed to tear these strings apart according to RFC standards. Instead of guessing where the slashes and question marks are, you wrap the string in a URI object.

require 'uri'

url_string = "https://api.service.com:8080/v1/search?q=ruby+uri&limit=10#results"
uri = URI.parse(url_string)

puts uri.scheme   # => "https"
puts uri.host     # => "api.service.com" (No port here!)
puts uri.port     # => 8080
puts uri.path     # => "/v1/search"
puts uri.query    # => "q=ruby+uri&limit=10"
puts uri.fragment # => "results"

See how much cleaner that is? uri.host gives you exactly the domain, stripped of the port and the protocol. I personally love how this turns a messy string into a structured object with named methods. It makes your intent clear to anyone else reading your code.

Handling the Query Hash

While uri.query is great, it still returns a string. You almost never want to manually split that string again. Instead, use URI.decode_www_form. This method takes that query string and turns it into an array of arrays, which you can easily convert into a hash.

# Turning the query string into a usable Ruby Hash
params = URI.decode_www_form(uri.query).to_h

puts params["q"]     # => "ruby uri" (Automatically decoded!)
puts params["limit"] # => "10"

One quick tip: URI.parse will throw a URI::InvalidURIError if the string is completely malformed. In a production environment, I always wrap my parsing in a begin/rescue block if the URL is coming from an untrusted user input. You don't want a typo in a URL to bring down your entire worker process.




📋 Practical Task

Build a Website Metadata Extractor

Create a script that takes an array of complex URLs and extracts a "Clean Summary" for each one. Your script should process the following list:

urls = [
  "https://blog.ruby-lang.org/en/entries/123?utm_source=twitter&ref=nav#comments",
  "http://dev.example.com:3000/projects/ruby-uri/wiki?action=edit&user=admin",
  "https://shop.store.com/items/search?category=books&sort=price_asc"
]

For each URL, your program must output a string in this exact format: "Host: [host] | Path: [path] | First Param: [first_param_key]=[first_param_value]"

Requirements:

  • Use URI.parse to extract the host and path.
  • Use URI.decode_www_form to access the query parameters.
  • Ensure that the first parameter value is properly decoded (e.g., spaces instead of +).
  • Handle the output for all three URLs in the array.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.