Mateus Nava

Mateus Nava

June 15, 2022

Validate params with Pattern Matching

By JJ Ying (unsplash.com)
By JJ Ying (unsplash.com)
I want to study and understand Pattern Matching and nothing is better than writing some code that can be useful for a real project, for me is the best way to understand something.

First of all, let's contextualize what Pattern Matching is - Pattern matching is a mechanism for checking a value against a pattern. To clearly this abstract concept, there are some examples in the excellent blog post An Introduction to Pattern Matching in Ruby by Pulkit Goyal:
  1. Matching against an expected data type
  2. Matching against an expected hash structure (e.g. presence of specific keys)
  3. Matching against an expected array length
  4. Assigning the matches (or a part of them) to some variables

The way to use Pattern Matching in ruby (only present in Ruby 2.7+) is using case...in syntax:
case <expression>
in <pattern1>
  # ...
in <pattern2>
  # ...
else
  # ...
end 

There is actually an experimental feature to use pattern matching in a single line but that is not relevant for this post.

Basically, it is possible to use Pattern Matching in Array and Hash. For our purpose (validate params) we are just using a hash with a nested array (you can find more examples in the blog post mentioned above).

The problem


Imagine if you want to validate params with complex rules, for params, I mean the hash of parameters that you get in some endpoint. To illustrate imagine you're developing an endpoint to generate a sales report and you need to check if the params are correct, the params look like this:
{
   start: {year: 2021, month: 5}, 
   end: {year: 2022, month: 1},
   salesman_ids: ['92e24fe4-5265-4ce3-8bc4-d5280ea17638'] 
}
    
 
The rule basically is: that all the parameters are required and need to be in this format.

The solution


There are a few ways to validate this structure but right now my challenge is to use Pattern Matching and I'm really excited about the solution because it's so simple 🥰.

def validate!(params)
  case params
  in {start: {year: Integer, month: Integer}, end: {year: Integer, month: Integer}, salesman_ids: [*]}
    puts "Params are valid"
    SalesReport.new(params).call
  end
rescue NoMatchingPatternError => e
  puts "Params are invalid - #{e.message}"
end

It's pretty cool, right? There are a lot of possibilities with Pattern Matching, this example is very simple. But for now, I think it's enough, you can explore other possibilities. For example, extracting values of the pattern in variables. 

Thanks for reading :).