Chaîner des conditions dans les expressions régulières

J’ai récemment dû effectuer une vérification semi-complexe sur une chaîne de caractères pour la valider.
En gros, j’ai cette méthode de modèle qui produit un numéro de série basé sur l’ID de l’objet. Les valeurs de retour valides pour cette méthode sont L00000002, L00000587 ou L00014522. En résumé, elles doivent être constituées d’un L majuscule suivi de 8 chiffres composés de l’id de l’objet complété avec des zéros.
Je devais donc trouver un moyen de créer simplement un matcher pour cela à utiliser dans ma spec de modèle. J’aurais pu construire deux attentes, une pour la longueur et une pour la composition. Mais j’ai simplement décidé de croire que les expressions régulières sont suffisamment puissantes pour faire les deux en même temps. Je me suis donc rendu sur rubular et j’ai commencé à bidouiller et à chercher sur Google.
La solution à laquelle je suis arrivé était la suivante (avec un id de 12, par exemple) :
/^L(?=[0-9]{8}$)0*12$/
Ce que cela fait, c’est - après le caractère de départ ^L - créer une condition correspondant à tout ce qui suit le ?= à l’intérieur des parenthèses. Si cette condition est vraie, alors le reste de l’expression est lu. Je vérifie donc n’importe quel chiffre ([0-9] - ça aurait pu être \d mais la lisibilité fait de la première option un meilleur choix à mon avis) exactement 8 fois ({8}) puis la fin de la chaîne avec $. Si cela correspond, alors je vérifie n’importe quel nombre de zéros (0*) suivi de l’id (12) et de la fin de la chaîne ($).
Maintenant, je peux utiliser cette expression dans ma spec, en utilisant la méthode Regexp.escape sur l’id de mon modèle dans une interpolation de chaîne :
# 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
… et créer la méthode correspondante comme ceci :
# app/models/my_model.rb
def serial_number
"L#{id.to_s.rjust(8, "0")}"
end
Qui a dit qu’il n’aimait pas les expressions régulières ? ;)
MoskitoHero