Class: MonoVM::Whois::DomainName

Inherits:
Object
  • Object
show all
Defined in:
lib/monovm/whois/domain_name.rb

Overview

An immutable, normalised domain name.

This class knows about the name and nothing else: how to clean up what a user pasted, how to move between Unicode and Punycode, and whether the result is a legal host name. Deciding which part of it is the TLD needs the server definitions, so that belongs to Registry::ServerRegistry — a name cannot know on its own whether .co.uk is a suffix or two labels.

name = DomainName.parse("  HTTPS://WWW.MÜNCHEN.de:8080/path?q=1  ")
name.to_s   # => "www.münchen.de"
name.ascii  # => "www.xn--mnchen-3ya.de"
name.valid? # => true

Constant Summary collapse

ACE_PREFIX =
"xn--"
MAX_LENGTH =
253
MAX_LABEL_LENGTH =
63
LABEL =

A single legal DNS label: alphanumeric ends, hyphens allowed inside.

/\A[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input) ⇒ DomainName

Everything is computed here rather than memoised on demand, because the instance is frozen: a value object that lazily fills in an ivar would raise FrozenError the first time anyone asked for it.



56
57
58
59
60
61
62
63
64
# File 'lib/monovm/whois/domain_name.rb', line 56

def initialize(input)
  @input = input
  @unicode = normalise(input).freeze
  @labels = @unicode.split(".", -1).freeze
  @ascii = @labels.map { |label| label_to_ascii(label) }.join(".").freeze
  @ascii_labels = @ascii.split(".", -1).freeze
  @valid = compute_valid?
  freeze
end

Instance Attribute Details

#asciiObject (readonly)

The ASCII (Punycode) form — what goes on the wire and into URLs.

Registries are queried in this form by default: Verisign answers "No match" to a UTF-8 query, which would read as availability.



75
76
77
# File 'lib/monovm/whois/domain_name.rb', line 75

def ascii
  @ascii
end

#ascii_labelsObject (readonly)

The ASCII (Punycode) form — what goes on the wire and into URLs.

Registries are queried in this form by default: Verisign answers "No match" to a UTF-8 query, which would read as availability.



75
76
77
# File 'lib/monovm/whois/domain_name.rb', line 75

def ascii_labels
  @ascii_labels
end

#inputObject (readonly)

Returns the value of attribute input.



28
29
30
# File 'lib/monovm/whois/domain_name.rb', line 28

def input
  @input
end

#labelsObject (readonly)

The ASCII (Punycode) form — what goes on the wire and into URLs.

Registries are queried in this form by default: Verisign answers "No match" to a UTF-8 query, which would read as availability.



75
76
77
# File 'lib/monovm/whois/domain_name.rb', line 75

def labels
  @labels
end

#unicodeObject (readonly)

Returns the value of attribute unicode.



28
29
30
# File 'lib/monovm/whois/domain_name.rb', line 28

def unicode
  @unicode
end

Class Method Details

.parse(input) ⇒ DomainName

Normalise input into a name. Never raises: an unusable string yields an instance whose #valid? is false, which is what bulk callers want so one bad entry does not abort a batch.

Parameters:

  • input (String)

Returns:

Raises:



37
38
39
40
41
# File 'lib/monovm/whois/domain_name.rb', line 37

def parse(input)
  raise InvalidDomainError, input unless input.is_a?(String)

  new(input)
end

.parse!(input) ⇒ Object

Like parse but raises when the result is not a usable host name.

Raises:



46
47
48
49
50
# File 'lib/monovm/whois/domain_name.rb', line 46

def parse!(input)
  parse(input).tap do |name|
    raise InvalidDomainError, input unless name.valid?
  end
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



103
104
105
# File 'lib/monovm/whois/domain_name.rb', line 103

def ==(other)
  other.is_a?(DomainName) && other.unicode == unicode
end

#bare?Boolean

True when the name carries no dot, so no TLD was supplied.

Returns:

  • (Boolean)


90
91
92
# File 'lib/monovm/whois/domain_name.rb', line 90

def bare?
  !unicode.include?(".")
end

#hashObject



108
109
110
# File 'lib/monovm/whois/domain_name.rb', line 108

def hash
  [self.class, unicode].hash
end

#idn?Boolean

True when the name needed Punycode, i.e. it has non-ASCII labels.

Returns:

  • (Boolean)


85
86
87
# File 'lib/monovm/whois/domain_name.rb', line 85

def idn?
  ascii != unicode
end

#inspectObject



112
113
114
# File 'lib/monovm/whois/domain_name.rb', line 112

def inspect
  "#<#{self.class.name} #{unicode.inspect}#{" invalid" unless valid?}>"
end

#join(suffix) ⇒ Object

Build a new name by appending suffix to this one.

DomainName.parse("monovm").join(".com").to_s # => "monovm.com"


97
98
99
100
101
# File 'lib/monovm/whois/domain_name.rb', line 97

def join(suffix)
  suffix = suffix.to_s
  suffix = ".#{suffix}" unless suffix.start_with?(".")
  self.class.parse("#{unicode}#{suffix}")
end

#to_sObject

The Unicode form — what a human reads.



67
68
69
# File 'lib/monovm/whois/domain_name.rb', line 67

def to_s
  unicode
end

#valid?Boolean

True when every label is a legal DNS label and the whole name fits in a DNS query. A single-label name like "monovm" is valid — callers append a TLD to it — so this deliberately does not require a dot.

Returns:

  • (Boolean)


80
81
82
# File 'lib/monovm/whois/domain_name.rb', line 80

def valid?
  @valid
end