ruby
48 lines · 8 steps
How a chainable SQL query builder works
A Ruby class that accumulates query parts through chained calls and assembles them into a SQL string on demand.
Explained by
highlit
1class QueryBuilder
2 def initialize(table)
3 @table = table
4 @wheres = []
5 @orders = []
6 @limit = nil
7 @offset = nil
8 @selects = ['*']
9 end
10
11 def select(*columns)
12 @selects = columns.flatten.map(&:to_s) unless columns.empty?
13 self
14 end
15
16 def where(condition, *bindings)
17 @wheres << { sql: condition, bindings: bindings }
18 self
19 end
20
21 def order(column, direction = :asc)
22 @orders << "#{column} #{direction.to_s.upcase}"
23 self
24 end
25
26 def limit(count)
27 @limit = count.to_i
28 self
29 end
30
31 def offset(count)
32 @offset = count.to_i
33 self
34 end
35
36 def to_sql
37 sql = "SELECT #{@selects.join(', ')} FROM #{@table}"
38 sql << " WHERE #{@wheres.map { |w| w[:sql] }.join(' AND ')}" unless @wheres.empty?
39 sql << " ORDER BY #{@orders.join(', ')}" unless @orders.empty?
40 sql << " LIMIT #{@limit}" if @limit
41 sql << " OFFSET #{@offset}" if @offset
42 sql
43 end
44
45 def bindings
46 @wheres.flat_map { |w| w[:bindings] }
47 end
48end
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Returning self from each mutator method is what enables a fluent, chainable API.
- 2Separating accumulation from rendering lets you build state incrementally and generate output only when needed.
- 3Keeping bindings apart from the SQL string preserves safe parameterized queries.
Related explainers
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
ruby
class UserAgentParser BROWSERS = [ [/Edg\/([\d.]+)/, "Edge"], [/OPR\/([\d.]+)/, "Opera"],
Parsing user-agent strings in Ruby
regex
pattern-matching
lookup-tables
Intermediate
8 steps
ruby
class LogAggregator BUCKET_FORMAT = "%Y-%m-%dT%H:%M" def initialize(entries)
Bucketing log entries by the minute in Ruby
aggregation
hashing
enumerable
Intermediate
5 steps
ruby
class WeeklySignupsReport DEFAULT_WEEKS = 12 def initialize(weeks: DEFAULT_WEEKS, source: User.all)
Building a weekly signups report in Rails
service object
aggregation
group by
Intermediate
7 steps
ruby
class ApplicationController < ActionController::Base EXPERIMENTS = { checkout_button_color: %w[control blue green], onboarding_flow: %w[control streamlined]
How A/B test cohorts are assigned in Rails
a-b-testing
cookies
hashing
Intermediate
8 steps
ruby
class SnippetHighlighter CONTEXT_RADIUS = 60 MAX_TERMS = 8
Building search-result snippets in Ruby
regular-expressions
text-processing
search
Intermediate
9 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/how-a-chainable-sql-query-builder-works-explained-ruby-a222/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.