ryodo

gem version build status dev dependency status code climate coverage status

ryōdo【領土】 りょうど — A domain name parser using public suffix list

Do you ever wanted to know if suspicious.domain.name.blerp is really a valid domain?

Do you ever wanted to know what is the domain portion of others.guy.awesome.domain.co.jp?

Then you should try ryodo and get the answers!

Notice: This gem does not check DNS records to verify if a name was taken and registered, this is not its purpose. I am a big fan of the UNIX philosophy: »Write programs that do one thing and do it well.«

My blog post about ryodo: ryodo - domain parser (2012-06-02)

Usage

dom = Ryodo.parse("my.awesome.domain.co.jp")
#=> Ryodo::Domain

                  #    SUBDOMAIN  DOMAIN   TLD
dom.tld           #=>                   "co.jp"  - returns only the public suffix
dom.domain        #=>            "domain.co.jp"  - returns only registered/registrable domain
dom.sld           #=>            "domain"        - returns only registered/registrable domain name w/o TLD
dom.subdomain     #=> "my.awesome"               - returns only subdomain parts
dom               #=> "my.awesome.domain.co.jp"  - returns full domain string
dom.fqdn          #=> "my.awesome.domain.co.jp." - full domain + trailing dot

# all parts also reversable
# mostly used on domain/FQDN
dom.reverse            #=> "jp.co.domain.awesome.my"
dom.fqdn.reverse       #=> ".jp.co.domain.awesome.my"

dom.to_a               #=> ["my","awesome","domain","co","jp"]
dom.domain.to_a        #=> ["domain","co","jp"]
dom.sld.to_a           #=> ["domain"]
dom.subdomain.to_a     #=> ["my","awesome"]
dom.fqdn.to_a          #=> ["my","awesome","domain","co","jp",""]

# .to_a also usable with parameter :reverse (or shorthand :r)
dom.domain.to_a(:reverse) #=> ["jp","co","domain","awesome","my"]
dom.fqdn.to_a(:reverse)   #=> ["","jp","co","domain","awesome","my"]
dom.fqdn.to_a(:r)         #=> ["","jp","co","domain","awesome","my"]

You can call it in different ways:

Ryodo.parse("my.awesome.domain.co.jp")
Ryodo("my.awesome.domain.co.jp")
Ryodo["my.awesome.domain.co.jp"]
ryodo("my.awesome.domain.co.jp")

Quick check (.domain_valid?)

Ryodo.domain_valid?("my.awesome.domain.co.jp") #=> true
Ryodo.domain_valid?("co.jp")                   #=> false

# aliases
Ryodo.valid_domain?("my.awesome.domain.co.jp")
Ryodo.valid?("my.awesome.domain.co.jp")
Ryodo?("my.awesome.domain.co.jp")
ryodo?("my.awesome.domain.co.jp")
valid_domain?("my.awesome.domain.co.jp")

String extension

require "ryodo/ext/string"

"my.awesome.domain.co.jp".to_domain
"my.awesome.domain.co.jp".ryodo

# validation
"my.awesome.domain.co.jp".valid_domain?

In Gemfile:

gem "ryodo", :require => ["ryodo", "ryodo/ext/string"]

UTF-8 junkie?

# coding: utf-8
require "ryodo/convenience/utf8"

ryōdo("my.awesome.domain.co.jp")
領土("my.awesome.domain.co.jp")
りょうど("my.awesome.domain.co.jp")

ryōdo?("my.awesome.domain.co.jp")
領土?("my.awesome.domain.co.jp")
りょうどか("my.awesome.domain.co.jp")

Extension of URI

Ryodo can transparently hook into URI, so you can use every described method on .host.

require "ryodo/ext/uri"

uri = URI.parse("http://my.awesome.domain.jp:5555/path")
uri.host
#=> "my.awesome.domain.jp"

uri.host.class
#=> Ryodo::Domain
# but decorates the String class transparently

uri.host.domain
#=> "domain.com"

# awesome quick check before doing further stuff with URI
# because why you would do a request to an URI with obviously invalid domain?
uri.host.is_valid?
#=> true

In Gemfile:

gem "ryodo", :require => ["ryodo", "ryodo/ext/uri"]

Benchmark

There is another gem called public_suffix, which does nearly the same (maybe with more features I don't need).

So I did a tiny benchmark.

Setup

A domain input list, taken by publicsuffix.org (checkPublicSuffix test script under publicsuffix.org/list/).

Some of them are also invalid (to test, if you implementation works correctly).

I added some very long domain names with many parts (for look-up time scale).

We only do a basic parsing and retrieve the registered/registrable domain. (Should hit the most important code of the gems.)

The benchmark script can be found at checks/benchmark.rb (branch: prof).

Notes

PublicSuffix.parse(…) will raise errors if domain input is invalid (e.g. not a registrable domain).

Ryodo.parse(…) won't raise but return nil values for invalid stuff (it only raises if input is not a String, of course).

Results

Ruby: MRI 2.1.0p0
ryodo (0.2.6)
public_suffix (1.3.3)
Machine: AMD Phenom(tm) II X6 1090T, 6 cores, 3.2 GHz; 8 GB RAM
Linux 3.12.7-1-ARCH x86_64 GNU/Linux

Benchmark of domain parsing
===========================

Number of loops: 1000
Number of items per loop: 100

Rehearsal ---------------------------------------------------
ryodo             2.110000   0.000000   2.110000 (  2.122766)
public_suffix    72.460000   0.020000  72.480000 ( 72.517502)
----------------------------------------- total: 74.590000sec

                      user     system      total        real
ryodo             2.130000   0.000000   2.130000 (  2.132354)
public_suffix    72.000000   2.220000  74.220000 ( 74.272943)

Ryodo is 34.83 times faster than PublicSuffix.
Ruby: MRI 2.0.0p247
ryodo (0.2.4.1)
public_suffix (1.3.1)
Machine: AMD Phenom(tm) II X6 1090T, 6 cores, 3.2 GHz; 8 GB RAM
Linux 3.10.9-1-ARCH x86_64 GNU/Linux

Benchmark of domain parsing
===========================

Number of loops: 1000
Number of items per loop: 100

Rehearsal ---------------------------------------------------
ryodo             2.780000   0.000000   2.780000 (  2.779690)
public_suffix    83.350000   0.000000  83.350000 ( 83.414578)
----------------------------------------- total: 86.130000sec

                      user     system      total        real
ryodo             2.830000   0.000000   2.830000 (  2.834630)
public_suffix    83.450000   0.000000  83.450000 ( 83.510037)

Ryodo vs. PublicSuffix

Ryodo is 29.46 times faster than PublicSuffix.

Interestingly the public_suffix gem got even slower over time (and/or ruby versions) compared to my first benchmark (http://codecraft.io/2012/06/02/ryodo-domain-parser/).

TODO

Lot of specs missing, this first version of second approach was developed in playground mode. ;o)

Foo …

"Uh, excuse me Sir … just one more question." — Columbo (Peter Falk †)

License

MIT/X11 — see LICENSE

(c) 2012—2014 Christoph Grabo