ruby 49 lines · 7 steps

Building a plain-Ruby signup validator

A single class collects field-by-field validation errors into a keyed hash and reports whether the input passes.

Explained by highlit
1class SignupValidator
2 EMAIL_REGEX = /\A[^@\s]+@[^@\s]+\.[^@\s]+\z/
3 MIN_PASSWORD_LENGTH = 8
4 
5 attr_reader :errors
6 
7 def initialize(params)
8 @params = params
9 @errors = Hash.new { |hash, key| hash[key] = [] }
10 end
11 
12 def valid?
13 validate_username
14 validate_email
15 validate_password
16 validate_terms
17 errors.empty?
18 end
19 
20 private
21 
22 def validate_username
23 username = @params[:username].to_s.strip
24 errors[:username] << "can't be blank" if username.empty?
25 errors[:username] << "must be at least 3 characters" if username.length.between?(1, 2)
26 errors[:username] << "may only contain letters, numbers, and underscores" unless username.match?(/\A\w+\z/) || username.empty?
27 end
28 
29 def validate_email
30 email = @params[:email].to_s.strip
31 if email.empty?
32 errors[:email] << "can't be blank"
33 elsif !email.match?(EMAIL_REGEX)
34 errors[:email] << "is not a valid address"
35 end
36 end
37 
38 def validate_password
39 password = @params[:password].to_s
40 errors[:password] << "can't be blank" if password.empty?
41 errors[:password] << "must be at least #{MIN_PASSWORD_LENGTH} characters" if password.length.between?(1, MIN_PASSWORD_LENGTH - 1)
42 errors[:password] << "must include a number" unless password.match?(/\d/)
43 errors[:password_confirmation] << "doesn't match password" if password != @params[:password_confirmation]
44 end
45 
46 def validate_terms
47 errors[:terms] << "must be accepted" unless @params[:terms] == true || @params[:terms] == "1"
48 end
49end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A Hash with a default block lets you append to per-field error lists without checking whether the key exists yet.
  2. 2Running every validator before checking emptiness surfaces all problems at once instead of stopping at the first.
  3. 3Coercing input with to_s and strip before testing makes the rules robust against nil and stray whitespace.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a plain-Ruby signup validator — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code