Skip to content
This repository has been archived by the owner on Sep 25, 2020. It is now read-only.

Commit

Permalink
Added domain_validator gem
Browse files Browse the repository at this point in the history
  • Loading branch information
avokhmin committed Aug 21, 2015
1 parent 9eb205e commit 3fe39fd
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -33,3 +33,5 @@ build/

# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc

Gemfile.lock
13 changes: 13 additions & 0 deletions .travis.yml
@@ -0,0 +1,13 @@
script: 'rspec spec'
notifications:
email:
- avokhmin@gmail.com
- be9@be9.ru
- dave@recurser.com
branches:
only:
- master
rvm:
- 2.0.0
- 2.1.0
- 2.2.2
4 changes: 4 additions & 0 deletions Gemfile
@@ -0,0 +1,4 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in validator.gemspec
gemspec
1 change: 0 additions & 1 deletion LICENSE
Expand Up @@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

51 changes: 50 additions & 1 deletion README.md
@@ -1 +1,50 @@
# domain_validator
Domain Validator [![Travis](https://secure.travis-ci.org/Shuttlerock/domain_validator.png)](http://travis-ci.org/Shuttlerock/domain_validator) [![Code Climate](https://codeclimate.com/github/Shuttlerock/domain_validator/badges/gpa.svg)](https://codeclimate.com/github/Shuttlerock/domain_validator)
============================

This is a ActiveModel validator for domains.

Installation
------------
gem install domain-validator

Usage
-------

In your models, the gem provides new :domain validator

class Model < ActiveRecord::Base
validates :domain_name, domain: true
end


Domain Validator
----------------

validates :domain_name, domain: true

validates :domain_name, domain: { message: 'custom message' }

Localization Tricks
-------------------
To customize error message, you can use { message: "your custom message" } or simple use Rails localization en.yml file, for instance:

en:
errors:
messages:
domain:
long: "your custom length error message"
activemodel:
errors:
messages:
domain:
invalid: "custom error message only for activemodel"
models:
your_model:
domain:
invalid: "custom error message for YourDomain model"


Copyright
---------

Copyright 2015 Shuttlerock Ltd. See LICENSE for details.
23 changes: 23 additions & 0 deletions Rakefile
@@ -0,0 +1,23 @@
#!/usr/bin/env rake

require 'rspec/core/rake_task'
require File.dirname(__FILE__) + '/lib/domain_validator/version'

task :default => :test

task :build => :test do
system 'gem build domain_validator.gemspec'
end

task :release => :build do
# tag and push
system "git tag v#{DomainValidator::VERSION}"
system "git push origin --tags"
# push the gem
system "gem push domain-validator-#{DomainValidator::VERSION}.gem"
end

RSpec::Core::RakeTask.new(:test) do |t|
t.pattern = 'spec/**/*_spec.rb'
t.fail_on_error = true # be explicit
end
22 changes: 22 additions & 0 deletions domain_validator.gemspec
@@ -0,0 +1,22 @@
# encoding: utf-8
require File.join(File.dirname(__FILE__), 'lib/domain_validator/version')

Gem::Specification.new do |s|
s.name = 'domain-validator'
s.license = 'MIT'
s.version = DomainValidator::VERSION
s.authors = ['Alexey Vokhmin', 'Dave Perrett', 'Oleg Dashevskii']
s.email = ['avokhmin@gmail.com', 'dave@recurser.com', 'be9@be9.ru']
s.homepage = 'https://github.com/Shuttlerock/domain_validator'
s.summary = 'ActiveModel validations for domains with fully localization support.'
s.description = 'ActiveModel validations for domains with fully localization support.'

s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]

s.add_runtime_dependency 'activemodel', '>= 3.0.0'
s.add_development_dependency 'activesupport', '>= 3.0.0'
s.add_development_dependency 'rspec'
end
56 changes: 56 additions & 0 deletions lib/active_model/validations/domain_validator.rb
@@ -0,0 +1,56 @@
require 'uri'

module ActiveModel
module Validations

# See: https://gist.github.com/rietta/3836366
#
# Domain Validator by Frank Rietta
# (C) 2012 Rietta Inc. All Rights Reserved.
# Licensed under terms of the BSD License.
#
# To use in a validation, add something like this to your model:
#
# validates :name, domain: true
#
class DomainValidator < ActiveModel::EachValidator

def validate_each(record, attribute, value)
return if value.blank?

# The shortest possible domain name something like Google's g.cn, which is four characters long
# The longest possible domain name, as per RFC 1035, RFC 1123, and RFC 2181 is 253 characters
if value.length < 4
record.errors.add(attribute, :'domain.short', options)
elsif value.length > 253
record.errors.add(attribute, :'domain.long', options)
elsif value !~ /\A([a-z0-9]([a-z0-9\-]{0,61}[a-z0-9])?\.)+[a-z]{2,6}\z/
record.errors.add(attribute, :'domain.invalid', options)
elsif value.include?('://')
record.errors.add(attribute, :'domain.protocol', options)
elsif value.include?('/')
record.errors.add(attribute, :'domain.slashes', options)
elsif !value.include?('.')
record.errors.add(attribute, :'domain.dots', options)
else

# Finally, see if the URI parser recognizes this as a valid URL when given a protocol;
# remember, we have already rejected any value that had a protocol specified already
valid = begin
URI.parse('http://' + value).kind_of?(URI::HTTP)
rescue URI::InvalidURIError
false
end

unless valid
record.errors.add(attribute, :'domain.invalid', options)
end

end

end # validate_each

end # DomainValidator

end
end
10 changes: 10 additions & 0 deletions lib/domain_validator.rb
@@ -0,0 +1,10 @@
require 'active_model'
require 'active_support/i18n'
require 'active_support/inflector'

require 'active_model/validations/domain_validator'

module DomainValidator
end

I18n.load_path << File.dirname(__FILE__) + '/domain_validator/locale/en.yml'
10 changes: 10 additions & 0 deletions lib/domain_validator/locale/en.yml
@@ -0,0 +1,10 @@
en:
errors:
messages:
domain:
short: "is invalid as a bare domain name because it is too short"
long: "is invalid as a bare domain name because it is too long"
protocol: "is invalid as a bare domain name because it includes a protocol seperator (://)"
slashes: "is invalid as a bare domain name because it includes forward slashes (/)"
dots: "is invalid as a bare domain name because it doesn't contain any dots (.)"
invalid: "is an invalid domain"
3 changes: 3 additions & 0 deletions lib/domain_validator/version.rb
@@ -0,0 +1,3 @@
module DomainValidator
VERSION = '0.0.2'
end
120 changes: 120 additions & 0 deletions spec/active_model/validations/domain_validator_spec.rb
@@ -0,0 +1,120 @@
require 'spec_helper'
require 'test_classes/domain'

module ActiveModel
module Validations
describe DomainValidator do
let(:domain) { TestDomain.new }

describe "validations" do
# for blank domain
it { expect(domain).to be_valid }

describe 'valid' do

it {
domain.domain_name = 'this-should-be-valid.com'
expect(domain).to be_valid
}

it "could start with letter" do
domain.domain_name = "correct.com"
expect(domain).to be_valid
end

it "could start with number" do
domain.domain_name = "4correct.com"
expect(domain).to be_valid
end

it "should be valid for domain with full length 253 and max length per label 63" do
domain.domain_name = "#{'a'*63}.#{'b'*63}.#{'c'*63}.#{'d'*57}.com"
expect(domain).to be_valid
end

it {
domain.domain_name = "#{'w'*63}.com"
expect(domain).to be_valid
}

it "should be valid with TLD [.com, .com.ua, .co.uk, .travel, .info, etc...]" do
%w(.com .com.ua .co.uk .travel .info).each do |tld|
domain.domain_name = "domain#{tld}"
expect(domain).to be_valid
end
end

it "should be valid if TLD length is between 2 and 6" do
%w(ua museum).each do |tld|
domain.domain_name = "domain.#{tld}"
expect(domain).to be_valid
end
end

it "should be valid for custom length and label length" do
domain.domain_name = "valid-domain.com"
expect(domain).to be_valid
end

it "should be valid with unknown TLD" do
domain.domain_name = "valid-domain.abcabc"
expect(domain).to be_valid
end

end

describe 'invalid' do
it "should be invalid if domain length is too short" do
domain.domain_name = 'a.b'
expect(domain).to_not be_valid
expect(domain.errors[:domain_name]).to include I18n.t(:'errors.messages.domain.short')
end

it 'should be invalid if domain length is too long' do
[
"not-valid-because-of-length-#{'w'*230}.com",
"not-valid-because-of-length-of-label#{'w'*230}.com"
].each do |domain_name|
domain.domain_name = domain_name
expect(domain).to_not be_valid
expect(domain.errors[:domain_name]).to include I18n.t(:'errors.messages.domain.long')
end
end

it 'should be invalid if label length is too long' do
domain.domain_name = "#{'w'*64}.com"
expect(domain).to_not be_valid
expect(domain.errors[:domain_name]).to include I18n.t(:'errors.messages.domain.invalid')
end

it "should be invalid if consists of special symbols (&, _, {, ], *, etc)" do
domain.domain_name = "&_{]*.com"
expect(domain).to_not be_valid
expect(domain.errors[:domain_name]).to include I18n.t(:'errors.messages.domain.invalid')
end

it "should not start with special symbol" do
domain.domain_name = "_incorrect.com"
expect(domain).to_not be_valid
expect(domain.errors[:domain_name]).to include I18n.t(:'errors.messages.domain.invalid')
end

it "should not be valid with TLD length more than 6" do
domain.domain_name = "domain.abcabcd"
expect(domain).to_not be_valid
expect(domain.errors[:domain_name]).to include I18n.t(:'errors.messages.domain.invalid')
end

it "should not be valid if TLD consists of numbers or special symbols (&, _, {, ], *, etc)" do
%w(2 & _ { ] *).each do |tld|
domain.domain_name = "domain.a#{tld}"
expect(domain).to_not be_valid
expect(domain.errors[:domain_name]).to include I18n.t(:'errors.messages.domain.invalid')
end
end

end
end
end
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
@@ -0,0 +1,2 @@
require 'rspec'
require_relative '../lib/domain_validator'
7 changes: 7 additions & 0 deletions spec/test_classes/domain.rb
@@ -0,0 +1,7 @@
class TestDomain
include ActiveModel::Validations

validates :domain_name, domain: true

attr_accessor :domain_name
end

0 comments on commit 3fe39fd

Please sign in to comment.