PageSourceSearch

https://matthuggins.com/assets/parsing-domain-names-now-in-gem-form-DvMHHSmn.js

js matthuggins.com collected 2026-09-25 15:15:55 UTC 10,046 bytes, 38 lines download raw bytes

1import{t as n}from"./jsx-runtime-D_bcST6I.js";var e=n(),t={title:"Parsing Domain Names - Now in Gem Form!",date:"2015-01-02",published:!0,tags:["ruby"],summary:"A feature for an internal Ruby project here at Quick Left necessitated parsing the domain from a URL. This seems like a problem for which there must already exist a solution, but it surprisingly turns out that there is no available solution for this seemingly simple task.",note:"This article was originally published as part of the QuickLeft blog while employeed there. Given that the company has been acquired and the site no longer exists, I've rehosted the content here."};function a(i){const s={a:"a",button:"button",code:"code",div:"div",em:"em",p:"p",pre:"pre",span:"span",...i.components};return(0,e.jsxs)(e.Fragment,{children:[(0,e.jsx)(s.p,{children:(0,e.jsxs)(s.em,{children:["Featured in ",(0,e.jsx)(s.a,{href:"https://web.archive.org/web/20161014150512/http://rubyweekly.com/issues/227",children:"Ruby Weekly"})]})}),`
2`,(0,e.jsx)(s.p,{children:"A feature for an internal Ruby project here at Quick Left necessitated parsing the domain from a URL. This seems like a problem for which there must already exist a solution, but it surprisingly turns out that there is nothing readily available for this seemingly simple task."}),`
3`,(0,e.jsxs)(s.p,{children:["When I first started searching, I came across the ",(0,e.jsx)(s.a,{href:"https://github.com/weppos/publicsuffix-ruby",children:"PublicSuffix gem"}),". At first glance, this appeared to be promising: pass it a host, and be given a domain name. Let's give it a try."]}),`
4`,(0,e.jsx)(e.Fragment,{children:(0,e.jsxs)(s.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"ruby",children:[(0,e.jsxs)(s.div,{className:"code-block-header",children:[(0,e.jsx)(s.span,{className:"font-mono text-xs text-gray-600",children:"ruby"}),(0,e.jsx)(s.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`PublicSuffix.parse('www.google.com').domain
5# => "google.com"
6
7PublicSuffix.parse('www.example.co.uk').domain
8# => "example.co.uk"
9
10PublicSuffix.parse('withgoogle.com').domain
11# => PublicSuffix::DomainNotAllowed: \`withgoogle.com' is not allowed according to Registry policy`})]}),(0,e.jsxs)(s.code,{children:[(0,e.jsxs)(s.span,{className:"line",children:[(0,e.jsx)(s.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"PublicSuffix"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"."}),(0,e.jsx)(s.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"parse"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,e.jsx)(s.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"'www.google.com'"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:")."}),(0,e.jsx)(s.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"domain"})]}),`
12`,(0,e.jsx)(s.span,{className:"line",children:(0,e.jsx)(s.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:'# => "google.com"'})}),`
13`,(0,e.jsx)(s.span,{className:"line"}),`
14`,(0,e.jsxs)(s.span,{className:"line",children:[(0,e.jsx)(s.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"PublicSuffix"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"."}),(0,e.jsx)(s.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"parse"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,e.jsx)(s.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"'www.example.co.uk'"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:")."}),(0,e.jsx)(s.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"domain"})]}),`
15`,(0,e.jsx)(s.span,{className:"line",children:(0,e.jsx)(s.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:'# => "example.co.uk"'})}),`
16`,(0,e.jsx)(s.span,{className:"line"}),`
17`,(0,e.jsxs)(s.span,{className:"line",children:[(0,e.jsx)(s.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"PublicSuffix"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"."}),(0,e.jsx)(s.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"parse"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,e.jsx)(s.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"'withgoogle.com'"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:")."}),(0,e.jsx)(s.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"domain"})]}),`
18`,(0,e.jsx)(s.span,{className:"line",children:(0,e.jsx)(s.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"# => PublicSuffix::DomainNotAllowed: `withgoogle.com' is not allowed according to Registry policy"})})]})]})}),`
19`,(0,e.jsxs)(s.p,{children:["As it turns out, the PublicSuffix gem is solving a similar problem, but not the one I'm striving to address. According to ",(0,e.jsx)(s.a,{href:"https://publicsuffix.org/",children:"PublicSuffix.org"}),", the Public Suffix List is an initiative to maintain suffixes under which users can register names. In other words, sites like Blogger and WordPress, which allow users to register subdomains, are considered public suffixes regardless of the registered domain name. Given the context, the PublicSuffix gem's implementation makes sense; it just does not suit our needs."]}),`
20`,(0,e.jsxs)(s.p,{children:[`From that, I set out to solve the problem for our constraints. It's a relatively simple problem, but it's not as simple as taking the ".com" off "example.com". The reasons is that some domain extensions that allow registrations have tw
20o or more levels, such as ".co.uk" or ".com.au". Given the possibilities regarding extensions, the best approach appears to be a whitelist of valid extensions. From this whitelist, we can deduce that when parsing a host, the next level down from a whitelisted extension can be considered the registered domain. With that approach in mind, I created the `,(0,e.jsx)(s.a,{href:"https://github.com/mhuggins/dominatrix",children:"Dominatrix gem"}),"."]}),`
21`,(0,e.jsx)(s.p,{children:"Let's take the examples from above and plug them into the Dominatrix gem."}),`
22`,(0,e.jsx)(e.Fragment,{children:(0,e.jsxs)(s.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"ruby",children:[(0,e.jsxs)(s.div,{className:"code-block-header",children:[(0,e.jsx)(s.span,{className:"font-mono text-xs text-gray-600",children:"ruby"}),(0,e.jsx)(s.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`Dominatrix.parse('http://www.google.com')
23# => "google.com"
24
25Dominatrix.parse('http://www.example.co.uk')
26# => "example.co.uk"
27
28Dominatrix.parse('http://withgoogle.com')
29# => "withgoogle.com"`})]}),(0,e.jsxs)(s.code,{children:[(0,e.jsxs)(s.span,{className:"line",children:[(0,e.jsx)(s.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"Dominatrix"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"."}),(0,e.jsx)(s.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"parse"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,e.jsx)(s.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"'http://www.google.com'"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:")"})]}),`
30`,(0,e.jsx)(s.span,{className:"line",children:(0,e.jsx)(s.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:'# => "google.com"'})}),`
31`,(0,e.jsx)(s.span,{className:"line"}),`
32`,(0,e.jsxs)(s.span,{className:"line",children:[(0,e.jsx)(s.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"Dominatrix"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"."}),(0,e.jsx)(s.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"parse"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,e.jsx)(s.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"'http://www.example.co.uk'"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:")"})]}),`
33`,(0,e.jsx)(s.span,{className:"line",children:(0,e.jsx)(s.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:'# => "example.co.uk"'})}),`
34`,(0,e.jsx)(s.span,{className:"line"}),`
35`,(0,e.jsxs)(s.span,{className:"line",children:[(0,e.jsx)(s.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"Dominatrix"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"."}),(0,e.jsx)(s.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"parse"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,e.jsx)(s.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"'http://withgoogle.com'"}),(0,e.jsx)(s.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:")"})]}),`
36`,(0,e.jsx)(s.span,{className:"line",children:(0,e.jsx)(s.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:'# => "withgoogle.com"'})})]})]})}),`
37`,(0,e.jsx)(s.p,{children:"We're now able to extract domain names. It's worth noting that this gem does not validate whether the domain is legitimately registered via whois; it simply parses the string to compare against the extension whitelist."}),`
38`,(0,e.jsxs)(s.p,{children:["As always, I'm happy to support new features, accept bug fixes, and generally merge meaningful pull requests on ",(0,e.jsx)(s.a,{href:"https://github.com/mhuggins/dominatrix",children:"the project"}),"!"]})]})}function o(i={}){const{wrapper:s}=i.components||{};return s?(0,e.jsx)(s,{...i,children:(0,e.jsx)(a,{...i})}):a(i)}export{o as default,t as frontmatter};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.