ruby
48 lines · 9 steps
Parsing nested query strings in Ruby
A parser that turns a raw query string into a nested hash, handling bracket syntax like a[b][]=1.
Explained by
highlit
1class QueryParser
2 def self.parse(query_string)
3 new(query_string).parse
4 end
5
6 def initialize(query_string)
7 @query_string = query_string.to_s.sub(/\A\?/, "")
8 end
9
10 def parse
11 params = {}
12 @query_string.split("&").each do |pair|
13 next if pair.empty?
14
15 key, value = pair.split("=", 2)
16 assign(params, tokenize(unescape(key)), unescape(value))
17 end
18 params
19 end
20
21 private
22
23 def tokenize(key)
24 name, rest = key.split("[", 2)
25 keys = [name]
26 keys.concat(rest.scan(/[^\[\]]*/).reject(&:empty?)) if rest
27 keys << "" if rest&.end_with?("[]")
28 keys
29 end
30
31 def assign(container, keys, value)
32 head, *tail = keys
33
34 if tail.empty?
35 container[head] = value
36 elsif tail == [""]
37 (container[head] ||= []) << value
38 else
39 nested = container[head] ||= {}
40 assign(nested, tail, value)
41 end
42 end
43
44 def unescape(str)
45 return if str.nil?
46 CGI.unescape(str)
47 end
48end
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A convenience class method plus a per-instance method keeps stateful parsing clean and reusable.
- 2Splitting a key into path segments turns flat query strings into recursively-built nested structures.
- 3Distinguishing scalar, array, and hash cases lets one assign routine handle arbitrarily deep nesting.
Related explainers
ruby
class LRUCache def initialize(capacity) raise ArgumentError, "capacity must be positive" unless capacity.positive?
How an LRU cache works in Ruby
caching
eviction
hash-ordering
Intermediate
7 steps
go
package tsvio import ( "bufio"
Reading and writing TSV records in Go
parsing
io-streams
error-handling
Intermediate
10 steps
ruby
class Article < ApplicationRecord belongs_to :author, class_name: "User" has_many :taggings, dependent: :destroy has_many :tags, through: :taggings
How scopes compose in Rails
scopes
activerecord
query-composition
Intermediate
8 steps
ruby
class Order class InvalidTransition < StandardError; end TRANSITIONS = {
A state machine for order transitions in Ruby
state-machine
data-driven
error-handling
Intermediate
8 steps
ruby
class CreateOrderItems < ActiveRecord::Migration[7.1] def change create_table :order_items do |t| t.references :order, null: false, foreign_key: { on_delete: :cascade }
Enforcing order-item integrity in Rails
migrations
foreign-keys
validations
Intermediate
7 steps
rust
use std::cmp::Ordering; use std::str::FromStr; #[derive(Debug, Clone, PartialEq, Eq)]
Parsing and ordering semantic versions in Rust
parsing
trait-implementation
ordering
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/parsing-nested-query-strings-in-ruby-explained-ruby-3143/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.