35+ Years Experience Netherlands Based ⚡ Fast Response Times Ruby on Rails Experts AI-Powered Development Fixed Pricing Available Senior Architects Dutch & English 35+ Years Experience Netherlands Based ⚡ Fast Response Times Ruby on Rails Experts AI-Powered Development Fixed Pricing Available Senior Architects Dutch & English
Ruby Pattern Matching: Van Basis case/in tot Productie-toepassingen

Ruby Pattern Matching: Van Basis case/in tot Productie-toepassingen

TTB Software
ruby

Ruby’s pattern matching, geïntroduceerd in Ruby 2.7 en gestabiliseerd in Ruby 3.0, biedt een manier om complexe datastructuren te destructureren en matchen in één expressie. Als je pattern matching kent van Elixir of Haskell, zal Ruby’s variant vertrouwd aanvoelen — maar met eigen idiomen die de moeite waard zijn om te leren.

Zo ziet het er op z’n simpelst uit:

case [1, 2, 3]
in [Integer => first, *rest]
  puts "Eerste: #{first}, rest: #{rest}"
end
# Eerste: 1, rest: [2, 3]

Deze gids behandelt elk pattern type dat Ruby 3.3+ ondersteunt, met echte code die je kunt gebruiken in productie Rails-apps en plain Ruby-projecten.

Array Patterns

Array patterns matchen tegen geordende collecties. Je kunt specifieke waarden pinnen, variabelen capturen en splats gebruiken:

case response
in [200, body]
  process_success(body)
in [404, _]
  handle_not_found
in [500, String => error_message]
  log_error(error_message)
  retry_request
end

Het underscore _ negeert die positie. De => operator vangt gematchte waarden op in variabelen.

Geneste arrays werken ook:

case matrix
in [[1, *], *]
  puts "Eerste rij begint met 1"
end

Hash Patterns

Hash patterns zijn waar Ruby’s pattern matching echt uitblinkt voor API- en JSON-werk. Ze matchen tegen key-value paren en negeren extra keys standaard:

case api_response
in { status: 200, data: { users: [{ name: String => first_user }, *] } }
  puts "Eerste gebruiker: #{first_user}"
in { status: 401 }
  refresh_auth_token
in { status: (500..) }
  raise ServerError
end

Die geneste destructurering vervangt wat anders meerdere regels zou zijn van door hashes graven met nil-checks. Een paar dingen om te weten over hash patterns:

  • Ze gebruiken standaard symbol keys
  • Extra keys in de hash veroorzaken geen match-fout (anders dan arrays, die qua lengte moeten matchen)
  • Je kunt hash en array patterns vrij combineren

Het Find Pattern

Toegevoegd in Ruby 3.1, het find pattern laat je zoeken binnen arrays:

case users
in [*, { role: "admin", email: String => admin_email }, *]
  notify_admin(admin_email)
end

Dit vindt de eerste hash in de array waar role gelijk is aan "admin" en vangt het e-mailadres op. Zonder pattern matching zou je users.find { |u| u[:role] == "admin" }&.dig(:email) schrijven — werkbaar, maar de pattern versie leest duidelijker bij complexe structuren.

Pin Operator

De pin operator (^) matcht tegen de waarde van een bestaande variabele in plaats van een nieuwe binding te creëren:

expected_status = 200

case response
in { status: ^expected_status }
  puts "Verwachte status ontvangen"
in { status: Integer => actual }
  puts "Onverwachte status: #{actual}"
end

Zonder de pin zou status: expected_status de statuswaarde opvangen in een nieuwe variabele genaamd expected_status, die de buitenste overschaduwt. De pin zegt “match tegen deze waarde.”

Guard Clauses

Voeg if-condities toe aan pattern branches:

case order
in { total: (100..) => amount, currency: "USD" } if amount < 10_000
  process_standard(order)
in { total: (10_000..) => amount, currency: "USD" }
  process_large_order(order)
end

Pattern Matching in Gewone Methodes

Je hebt niet altijd case/in nodig. Ruby 3.1+ ondersteunt de in operator als boolean check:

if api_response in { data: { id: Integer => id } }
  fetch_details(id)
end

En de => operator voor one-liner destructurering:

response => { data: { users: } }
# `users` is nu beschikbaar

Deze one-liner vorm gooit een NoMatchingPatternError als het pattern niet matcht, dus gebruik het wanneer je zeker bent van de structuur.

Praktisch Voorbeeld: Webhook Payloads Parsen

Hier is een patroon dat ik gebruik in een Rails controller voor het afhandelen van Stripe webhooks:

class WebhooksController < ApplicationController
  def stripe
    event = JSON.parse(request.body.read, symbolize_names: true)

    case event
    in { type: "checkout.session.completed",
         data: { object: { customer: String => customer_id,
                           subscription: String => sub_id } } }
      SubscriptionActivator.call(customer_id:, sub_id:)

    in { type: "customer.subscription.deleted",
         data: { object: { id: String => sub_id } } }
      SubscriptionCanceller.call(sub_id:)

    in { type: /^invoice\./ }
      InvoiceProcessor.call(event)

    else
      Rails.logger.info("Onverwerkt webhook type: #{event[:type]}")
    end

    head :ok
  end
end

Vergelijk dit met de typische if/elsif-keten die event["type"] checkt en dan in geneste hashes graaft. De pattern matching versie declareert de verwachte structuur en extraheert wat je nodig hebt in één keer.

Praktisch Voorbeeld: Command Objects

Pattern matching combineert goed met service objects bij het routeren van commands:

def execute(command)
  case command
  in { action: :create, resource: :user, params: Hash => attrs }
    User.create!(attrs)
  in { action: :update, resource: :user, id: Integer => id, params: Hash => attrs }
    User.find(id).update!(attrs)
  in { action: :delete, resource: :user, id: Integer => id }
    User.find(id).destroy!
  end
end

Custom Pattern Matching met deconstruct en deconstruct_keys

Elk Ruby object kan pattern matching ondersteunen door deconstruct (voor array patterns) en deconstruct_keys (voor hash patterns) te implementeren:

class Temperature
  attr_reader :value, :unit

  def initialize(value, unit = :celsius)
    @value = value
    @unit = unit
  end

  def deconstruct_keys(keys)
    { value: @value, unit: @unit }
  end
end

temp = Temperature.new(37.5, :celsius)

case temp
in { value: (38..), unit: :celsius }
  puts "Koorts"
in { value: (..36), unit: :celsius }
  puts "Onderkoeling"
in { value:, unit: :celsius }
  puts "Normaal: #{value}°C"
end

Rails maakt hier uitgebreid gebruik van — ActiveRecord models reageren op deconstruct_keys, dus je kunt pattern matchen tegen model-attributen in Ruby 3.2+:

case user
in { role: "admin", active: true }
  grant_admin_access
in { role: "admin", active: false }
  prompt_reactivation
end

Performance Overwegingen

Pattern matching compileert naar efficiënte bytecode in Ruby 3.1+. Ik heb case/in gebenchmarkt tegen equivalente case/when met handmatige destructurering op Ruby 3.3.0:

case/in (pattern match):    2.1M iteraties/sec
case/when + handmatig dig:  2.4M iteraties/sec
if/elsif keten:             2.5M iteraties/sec

Pattern matching is ruwweg 12-15% langzamer dan handmatige alternatieven in microbenchmarks. In de praktijk is dit verschil onzichtbaar — je database queries en netwerkaanroepen domineren met ordes van grootte. Gebruik pattern matching waar het de leesbaarheid verbetert; vermijd het niet om performance-redenen.

Wanneer Niet Pattern Matching Gebruiken

Pattern matching is niet altijd het juiste gereedschap:

  • Simpele waarde-checks: case/when is duidelijker voor het matchen tegen een platte lijst van waarden
  • Enkele-key hash toegang: hash[:key] of hash.fetch(:key) is simpeler dan een pattern
  • Hete inner loops: Als je miljoenen records in-memory verwerkt, telt de 12-15% overhead

De sweet spot is complexe, geneste datastructuren waar je zowel de vorm moet valideren als waarden moet extraheren — API responses, webhook payloads, configuratie-parsing en command routing.

FAQ

Is Ruby pattern matching stabiel genoeg voor productie?

Ja. Pattern matching was experimenteel in Ruby 2.7, maar werd een stabiele, niet-experimentele feature in Ruby 3.0 (uitgebracht december 2020). Vanaf Ruby 3.3 is het volledig volwassen zonder geplande breaking changes. De in operator voor standalone boolean checks en de => one-liner vorm werden gestabiliseerd in Ruby 3.1.

Hoe verschilt Ruby pattern matching van Elixir’s variant?

Ruby’s pattern matching is expression-based (case/in), terwijl Elixir pattern matching overal gebruikt — in functie-heads, variable binding en de = operator zelf. Ruby’s versie is beperkter in scope maar integreert schoon met het object-georiënteerde model via deconstruct en deconstruct_keys. Je krijgt geen function-head matching in Ruby, maar voor data destructurering zijn de mogelijkheden vergelijkbaar.

Kan ik pattern matching gebruiken met ActiveRecord objecten in Rails?

Ja, vanaf Ruby 3.2 en Rails 7.0+. ActiveRecord models implementeren deconstruct_keys, dus case user in { name: "Alice", active: true } werkt tegen model-attributen. Let op dat dit de attribute-methodes aanroept, dus het respecteert custom getters en attribute overrides.

Werkt pattern matching met string keys in hashes?

Standaard matchen hash patterns tegen symbol keys. Voor string keys moet je expliciete string key syntax gebruiken: in { "status" => Integer => code }. Dit komt vaak voor bij het parsen van JSON met JSON.parse (dat standaard string keys retourneert) — gebruik symbolize_names: true of match expliciet tegen string keys.

#ruby #pattern-matching #ruby-3
T

About the Author

Roger Heykoop is een senior Ruby on Rails ontwikkelaar met 19+ jaar Rails ervaring en 35+ jaar ervaring in softwareontwikkeling. Hij is gespecialiseerd in Rails modernisering, performance optimalisatie, en AI-ondersteunde ontwikkeling.

Get in Touch

Share this article

Need Expert Rails Development?

Let's discuss how we can help you build or modernize your Rails application with 19+ years of expertise

Schedule a Free Consultation