ruby 32 lines · 8 steps

Writing a custom hostname validator in Rails

A reusable ActiveModel validator that checks a hostname's length, labels, and structure against RFC-style rules.

Explained by highlit
1class HostnameValidator < ActiveModel::EachValidator
2 MAX_LENGTH = 253
3 LABEL = /\A(?!-)[a-z0-9-]{1,63}(?<!-)\z/i
4 
5 def validate_each(record, attribute, value)
6 return if value.blank? && options[:allow_blank]
7 
8 host = value.to_s
9 host = host.chomp(".") if options[:allow_trailing_dot]
10 
11 unless valid_hostname?(host)
12 record.errors.add(attribute, :invalid_hostname, message: options[:message])
13 end
14 end
15 
16 private
17 
18 def valid_hostname?(host)
19 return false if host.empty? || host.length > MAX_LENGTH
20 return false if host.start_with?(".") || host.end_with?(".")
21 return false if host.include?("..")
22 
23 labels = host.split(".")
24 return false if options[:require_fqdn] && labels.size < 2
25 
26 labels.all? { |label| label.match?(LABEL) } && !numeric_tld?(labels)
27 end
28 
29 def numeric_tld?(labels)
30 labels.size > 1 && labels.last.match?(/\A\d+\z/)
31 end
32end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Subclassing EachValidator lets you reuse attribute-level validation logic across any model.
  2. 2Encoding format rules as constants and small predicate methods keeps complex validation readable.
  3. 3Honoring the options hash makes a custom validator configurable just like Rails' built-ins.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Writing a custom hostname validator in Rails — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code