Regexp hell

I recently had to make a semi-complex check on a string to validate it.

Basically, I have this model method which produces a serial number based on the ID of the object. Valid returned values for this method are L00000002, L00000587 or L00014522. In short, they should be constituted of a capital L followed by 8 digits composed of the id of the object padded with zeros.

So I had to find a way to simply created a matcher for this to use in my model spec. I could have built two expectations, one for the length, and one for the composition. But I just decided to believe that regular expressions are powerful enough to do both at the same time. So I headed to rubular and started fiddling and googling around.

The solution I came up with was the following (with an id of 12, for instance):

/^L(?=[0-9]{8}$)0*12$/

What this does, is - after the starting ^L character - create a condition matching whatever follows the ?= inside the parentheses. If this condition is true, then the rest of the expression is read. So I just check for any digit ([0-9] - it could have been \d but readability make the first option a better fit IMHO) exacty 8 times ({8}) and then the end of the string with $. If this is matched, then I check any number of zeros (0*) followed by the id (12) and the end of the string ($).

Now I could use this expression in my spec, using the Regexp.escape method on my model’s id within a string interpolation:

# spec/models/my_model_spec.rb
describe "serial_number" do
  let(:my_model) { create(:my_model) } # I use the FactoryBot gem

  it "returns a valid serial number" do
    expect(my_model.serial_number).to match(/^L(?=[0-9]{8}$)0*#{Regexp.escape(my_model.id.to_s)}$/)
  end
end

… and create the corresponding method like so:

# app/models/my_model.rb
def serial_number
  "L#{id.to_s.rjust(8, "0")}"
end

Don’t you just love regular expressions?