activevalidators 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +10 -0
- data/README.md +10 -7
- data/Rakefile +24 -27
- data/activevalidators.gemspec +9 -9
- data/lib/active_model/validations/tracking_number_validator.rb +53 -16
- data/lib/active_validators/version.rb +4 -0
- data/test/test_helper.rb +29 -0
- data/{spec/validations/credit_card_spec.rb → test/validations/credit_card_test.rb} +14 -22
- data/test/validations/email_test.rb +42 -0
- data/test/validations/ip_test.rb +61 -0
- data/test/validations/password_test.rb +40 -0
- data/test/validations/phone_test.rb +76 -0
- data/test/validations/postal_code_test.rb +53 -0
- data/test/validations/respond_to_test.rb +33 -0
- data/test/validations/slug_test.rb +39 -0
- data/test/validations/tracking_number_test.rb +151 -0
- data/test/validations/twitter_test.rb +161 -0
- data/test/validations/url_test.rb +39 -0
- metadata +49 -71
- data/.bundle/config +0 -2
- data/autotest/discover.rb +0 -1
- data/spec/activevalidators_spec.rb +0 -39
- data/spec/spec_helper.rb +0 -15
- data/spec/validations/email_spec.rb +0 -44
- data/spec/validations/ip_spec.rb +0 -73
- data/spec/validations/password_spec.rb +0 -44
- data/spec/validations/phone_spec.rb +0 -80
- data/spec/validations/postal_code_spec.rb +0 -61
- data/spec/validations/respond_to_spec.rb +0 -37
- data/spec/validations/slug_spec.rb +0 -44
- data/spec/validations/tracking_number_spec.rb +0 -66
- data/spec/validations/twitter_spec.rb +0 -155
- data/spec/validations/url_spec.rb +0 -38
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
ActiveValidators
|
1
|
+
ActiveValidators [](http://travis-ci.org/cesario/activevalidators)
|
2
2
|
================
|
3
3
|
|
4
4
|
Collection of ActiveModel/ActiveRecord validations
|
@@ -49,17 +49,18 @@ In your models, the gem provides new validators like `email`, or `url`:
|
|
49
49
|
|
50
50
|
Exhaustive list of supported validators and their implementation:
|
51
51
|
|
52
|
-
* `email` : based on the `mail` gem
|
53
|
-
* `url` : based on a regular expression
|
54
|
-
* `phone` : based on a set of predefined masks
|
55
|
-
* `twitter` : based on a regular expression
|
56
|
-
* `slug` : based on `ActiveSupport::String#parameterize`
|
57
|
-
* `ip` : based on `Resolv::IPv[4|6]::Regex`
|
58
52
|
* `credit_card` : based on the `Luhn` algorithm
|
59
53
|
* `date` : based on the `DateValidator` gem
|
54
|
+
* `email` : based on the `mail` gem
|
55
|
+
* `ip` : based on `Resolv::IPv[4|6]::Regex`
|
60
56
|
* `password` : based on a set of regular expressions
|
57
|
+
* `phone` : based on a set of predefined masks
|
61
58
|
* `postal_code`: based on a set of predefined masks
|
59
|
+
* `respond_to`
|
60
|
+
* `slug` : based on `ActiveSupport::String#parameterize`
|
62
61
|
* `tracking_number`: based on a set of predefined masks
|
62
|
+
* `twitter` : based on a regular expression
|
63
|
+
* `url` : based on a regular expression
|
63
64
|
|
64
65
|
Todo
|
65
66
|
----
|
@@ -89,6 +90,8 @@ Contributors
|
|
89
90
|
* Garrett Bjerkhoel
|
90
91
|
* Renato Riccieri Santos Zannon
|
91
92
|
* Brian Moseley
|
93
|
+
* Travis Vachon
|
94
|
+
* Rob Zuber
|
92
95
|
|
93
96
|
Copyright
|
94
97
|
---------
|
data/Rakefile
CHANGED
@@ -1,8 +1,21 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
$:.unshift File.expand_path("../lib", __FILE__)
|
3
|
-
|
4
1
|
require 'rubygems'
|
5
2
|
require 'rubygems/specification'
|
3
|
+
|
4
|
+
require 'bundler'
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
require 'rake/testtask'
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << "test"
|
10
|
+
t.pattern = "test/**/*_test.rb"
|
11
|
+
t.verbose = true
|
12
|
+
t.warning = true
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'turn'
|
16
|
+
|
17
|
+
# -*- encoding: utf-8 -*-
|
18
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
6
19
|
require 'activevalidators'
|
7
20
|
|
8
21
|
def gemspec
|
@@ -12,31 +25,15 @@ def gemspec
|
|
12
25
|
end
|
13
26
|
end
|
14
27
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
RSpec::Core::RakeTask.new do |t|
|
20
|
-
t.rspec_opts = %w(-fs --color)
|
21
|
-
t.ruby_opts = %w(-w)
|
22
|
-
end
|
23
|
-
|
24
|
-
namespace :spec do
|
25
|
-
task :clean do
|
26
|
-
rm_rf 'tmp'
|
27
|
-
rm_rf 'pkg'
|
28
|
-
end
|
29
|
-
|
30
|
-
desc "Run the full spec suite"
|
31
|
-
task :full => ["clean", "spec"]
|
32
|
-
end
|
33
|
-
|
34
|
-
rescue LoadError
|
35
|
-
task :spec do
|
36
|
-
abort "Run `gem install rspec` to be able to run specs"
|
37
|
-
end
|
28
|
+
desc "Clean the current directory"
|
29
|
+
task :clean do
|
30
|
+
rm_rf 'tmp'
|
31
|
+
rm_rf 'pkg'
|
38
32
|
end
|
39
33
|
|
34
|
+
desc "Run the full spec suite"
|
35
|
+
task :full => ["clean", "test"]
|
36
|
+
|
40
37
|
desc "install the gem locally"
|
41
38
|
task :install => :package do
|
42
39
|
sh %{gem install pkg/#{gemspec.name}-#{gemspec.version}}
|
@@ -59,4 +56,4 @@ task :install => :gem do
|
|
59
56
|
sh "gem install pkg/#{gemspec.full_name}.gem"
|
60
57
|
end
|
61
58
|
|
62
|
-
task :default => :
|
59
|
+
task :default => :full
|
data/activevalidators.gemspec
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
lib = File.expand_path('../lib/', __FILE__)
|
3
3
|
$:.unshift lib unless $:.include?(lib)
|
4
|
+
require 'active_validators/version'
|
4
5
|
|
5
6
|
Gem::Specification.new do |s|
|
6
7
|
s.name = "activevalidators"
|
7
|
-
s.version =
|
8
|
+
s.version = ActiveValidators::VERSION
|
8
9
|
s.platform = Gem::Platform::RUBY
|
9
10
|
s.authors = ["Franck Verrot", "Paco Guzmán", "Oriol Gual", "Garrett Bjerkhoel", "Renato Riccieri Santos Zannon", "Brian Moseley"]
|
10
11
|
s.email = ["franck@verrot.fr"]
|
@@ -13,14 +14,13 @@ Gem::Specification.new do |s|
|
|
13
14
|
s.description = %q{ActiveValidators is a collection of ActiveModel/ActiveRecord validations}
|
14
15
|
|
15
16
|
s.add_development_dependency "bundler"
|
16
|
-
s.add_development_dependency "
|
17
|
-
s.add_development_dependency "
|
18
|
-
s.
|
19
|
-
s.add_dependency 'activerecord' , '
|
20
|
-
s.add_dependency 'activemodel' , '
|
21
|
-
s.add_dependency 'mail'
|
22
|
-
s.add_dependency 'date_validator'
|
23
|
-
s.add_dependency 'date_validator', '~> 0.6.1'
|
17
|
+
s.add_development_dependency "minitest"
|
18
|
+
s.add_development_dependency "turn"
|
19
|
+
s.add_dependency 'rake' , '>= 0.8.7'
|
20
|
+
s.add_dependency 'activerecord' , '>= 3.0.0'
|
21
|
+
s.add_dependency 'activemodel' , '>= 3.0.0'
|
22
|
+
s.add_dependency 'mail'
|
23
|
+
s.add_dependency 'date_validator'
|
24
24
|
|
25
25
|
s.files = `git ls-files`.split("\n")
|
26
26
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -2,30 +2,67 @@ module ActiveModel
|
|
2
2
|
module Validations
|
3
3
|
class TrackingNumberValidator < EachValidator
|
4
4
|
def validate_each(record, attribute, value)
|
5
|
-
|
6
|
-
carrier = options[:carrier]
|
5
|
+
carrier = options[:carrier] || (options[:carrier_field] && record.send(options[:carrier_field]))
|
7
6
|
raise "Carrier option required" unless carrier
|
8
|
-
|
9
|
-
raise "
|
10
|
-
record.errors.add(attribute) unless
|
7
|
+
method = "valid_#{carrier.to_s}?"
|
8
|
+
raise "Tracking number validation not supported for carrier #{carrier}" unless self.respond_to?(method)
|
9
|
+
record.errors.add(attribute) unless self.send(method, value)
|
11
10
|
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
# UPS:
|
13
|
+
# ups tracking codes are validated solely on their format
|
14
|
+
# see https://www.ups.com/content/us/en/tracking/help/tracking/tnh.html
|
15
|
+
UPS_REGEXES = [ /^1Z[a-zA-Z0-9]{16}$/, /^[a-zA-Z0-9]{12}$/, /^[a-zA-Z0-9]{9}$/, /^T[a-zA-Z0-9]{10}$/ ]
|
16
|
+
def valid_ups?(value)
|
17
|
+
!!UPS_REGEXES.detect { |fmt| value.match(fmt) }
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
# USPS:
|
21
|
+
# usps tracking codes are validated based on format (one of USS228 or USS39)
|
22
|
+
# and a check digit (using either of the USPS's MOD10 or MOD11 algorithms)
|
23
|
+
# see USPS Publications:
|
24
|
+
# - #91 (05/2008) pp. 38
|
25
|
+
# - #97 (05/2002) pp. 62-63
|
26
|
+
# - #109 (09/2007) pp. 19-21
|
27
|
+
def valid_usps?(value)
|
28
|
+
uss228?(value) || uss39?(value)
|
23
29
|
end
|
24
30
|
|
25
|
-
|
31
|
+
USS128_REGEX = /^(\d{19,21})(\d)$/
|
32
|
+
def uss228?(value)
|
33
|
+
m = value.match(USS128_REGEX)
|
34
|
+
m.present? && (m[2].to_i == usps_mod10(m[1]))
|
35
|
+
end
|
36
|
+
|
37
|
+
USS39_REGEX = /^[a-zA-Z0-9]{2}(\d{8})(\d)US$/
|
38
|
+
def uss39?(value)
|
39
|
+
m = value.match(USS39_REGEX)
|
40
|
+
# it appears to be valid for a USS39 barcode's checkdigit to be calculated with either the usps mod 10
|
41
|
+
# algorithm or the usps mod 11.
|
42
|
+
m.present? && (m[2].to_i == usps_mod10(m[1]) || m[2].to_i == usps_mod11(m[1]))
|
43
|
+
end
|
44
|
+
|
45
|
+
MOD10_WEIGHTS = [3,1]
|
46
|
+
def usps_mod10(chars)
|
47
|
+
10 - weighted_sum(chars.reverse, MOD10_WEIGHTS) % 10
|
48
|
+
end
|
49
|
+
|
50
|
+
MOD11_WEIGHTS = [8,6,4,2,3,5,9,7]
|
51
|
+
def usps_mod11(chars)
|
52
|
+
mod = weighted_sum(chars, MOD11_WEIGHTS) % 11
|
53
|
+
case mod
|
54
|
+
when 0 then 5
|
55
|
+
when 1 then 0
|
56
|
+
else 11 - mod
|
57
|
+
end
|
58
|
+
end
|
26
59
|
|
27
|
-
|
28
|
-
|
60
|
+
# takes a string containing digits and calculates a checksum using the provided weight array
|
61
|
+
# cycles the weight array if it's not long enough
|
62
|
+
def weighted_sum(value, weights)
|
63
|
+
digits = value.split('').map { |d| d.to_i }
|
64
|
+
weights = weights.cycle.take(digits.count) if weights.count < digits.count
|
65
|
+
digits.zip(weights).inject(0) { |s,p| s + p[0] * p[1] }
|
29
66
|
end
|
30
67
|
end
|
31
68
|
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
# silence warnings
|
4
|
+
old_w, $-w = $-w, false
|
5
|
+
|
6
|
+
begin; require 'turn'; rescue LoadError; end
|
7
|
+
require 'minitest/spec'
|
8
|
+
require 'minitest/mock'
|
9
|
+
require 'minitest/autorun'
|
10
|
+
|
11
|
+
# unsilence warnings
|
12
|
+
$-w = old_w
|
13
|
+
|
14
|
+
require 'activevalidators'
|
15
|
+
|
16
|
+
%w(models).each do |directory|
|
17
|
+
Dir["#{File.dirname(__FILE__)}/#{directory}/*.rb"].each {|f| require f}
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestRecord
|
21
|
+
include ActiveModel::Validations
|
22
|
+
attr_accessor :ip, :url, :slug, :responder, :global_condition,
|
23
|
+
:local_condition, :phone, :email, :card, :password, :twitter_username,
|
24
|
+
:postal_code, :carrier, :tracking_number
|
25
|
+
|
26
|
+
def initialize(attrs = {})
|
27
|
+
attrs.each_pair { |k,v| send("#{k}=", v) }
|
28
|
+
end
|
29
|
+
end
|
@@ -1,13 +1,6 @@
|
|
1
|
-
require
|
1
|
+
require 'test_helper.rb'
|
2
2
|
|
3
3
|
describe "Credit Card Validation" do
|
4
|
-
before(:each) do
|
5
|
-
TestRecord.reset_callbacks(:validate)
|
6
|
-
TestRecord.validates :card, :credit_card => { :type => :any }
|
7
|
-
end
|
8
|
-
|
9
|
-
subject { TestRecord.new }
|
10
|
-
|
11
4
|
# Here are some valid credit cards
|
12
5
|
VALID_CARDS =
|
13
6
|
{
|
@@ -37,26 +30,25 @@ describe "Credit Card Validation" do
|
|
37
30
|
|
38
31
|
VALID_CARDS.each_pair do |card, number|
|
39
32
|
it "accepts #{card} valid cards" do
|
40
|
-
subject
|
41
|
-
subject.
|
42
|
-
subject.
|
33
|
+
subject = build_card_record :card => number
|
34
|
+
subject.valid?.must_equal true
|
35
|
+
subject.errors.size.must_equal 0
|
43
36
|
end
|
44
37
|
end
|
45
38
|
|
46
39
|
describe "for invalid cards" do
|
40
|
+
it "rejects invalid cards and generates an error message of type invalid" do
|
41
|
+
subject = build_card_record :card => '99999'
|
42
|
+
subject.valid?.must_equal false
|
43
|
+
subject.errors.size.must_equal 1
|
47
44
|
|
48
|
-
|
49
|
-
subject.card = '99999'
|
50
|
-
end
|
51
|
-
|
52
|
-
it "rejects invalid cards" do
|
53
|
-
subject.should_not be_valid
|
54
|
-
subject.should have(1).error
|
45
|
+
subject.errors[:card].include?(subject.errors.generate_message(:card, :invalid)).must_equal true
|
55
46
|
end
|
47
|
+
end
|
56
48
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
49
|
+
def build_card_record(attrs = {})
|
50
|
+
TestRecord.reset_callbacks(:validate)
|
51
|
+
TestRecord.validates :card, :credit_card => { :type => :any }
|
52
|
+
TestRecord.new attrs
|
61
53
|
end
|
62
54
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper.rb'
|
2
|
+
|
3
|
+
describe "Email Validation" do
|
4
|
+
it "accepts valid emails" do
|
5
|
+
subject = build_email_record :email => 'franck@verrot.fr'
|
6
|
+
subject.valid?.must_equal true
|
7
|
+
subject.errors.size.must_equal 0
|
8
|
+
end
|
9
|
+
|
10
|
+
it "accepts complete emails" do
|
11
|
+
subject = build_email_record :email => 'Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>'
|
12
|
+
subject.valid?.must_equal true
|
13
|
+
subject.errors.size.must_equal 0
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "for invalid emails" do
|
17
|
+
it "rejects invalid emails" do
|
18
|
+
subject = build_email_record :email => 'franck.fr'
|
19
|
+
subject.valid?.must_equal false
|
20
|
+
subject.errors.size.must_equal 1
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'rejects local emails' do
|
24
|
+
subject = build_email_record :email => 'franck.fr'
|
25
|
+
subject.email = 'franck'
|
26
|
+
subject.valid?.must_equal false
|
27
|
+
subject.errors.size.must_equal 1
|
28
|
+
end
|
29
|
+
|
30
|
+
it "generates an error message of type invalid" do
|
31
|
+
subject = build_email_record :email => 'franck.fr'
|
32
|
+
subject.valid?.must_equal false
|
33
|
+
subject.errors[:email].include?(subject.errors.generate_message(:email, :invalid)).must_equal true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_email_record(attrs = {})
|
38
|
+
TestRecord.reset_callbacks(:validate)
|
39
|
+
TestRecord.validates :email, :email => true
|
40
|
+
TestRecord.new attrs
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'test_helper.rb'
|
2
|
+
|
3
|
+
describe "IP Validation" do
|
4
|
+
describe "IPv4 Validation" do
|
5
|
+
it "accepts valid IPs" do
|
6
|
+
subject = build_ip_record :v4, :ip => '192.168.1.1'
|
7
|
+
subject.valid?.must_equal true
|
8
|
+
subject.errors.size.must_equal 0
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "for invalid IPs" do
|
12
|
+
it "rejects invalid IPs" do
|
13
|
+
subject = build_ip_record :v4, :ip => '267.34.56.3'
|
14
|
+
subject.valid?.must_equal false
|
15
|
+
subject.errors.size.must_equal 1
|
16
|
+
end
|
17
|
+
|
18
|
+
it "generates an error message of type invalid" do
|
19
|
+
subject = build_ip_record :v4, :ip => '267.34.56.3'
|
20
|
+
subject.valid?.must_equal false
|
21
|
+
subject.errors[:ip].include?(subject.errors.generate_message(:ip, :invalid)).must_equal true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "IPv6 Validation" do
|
27
|
+
it "accepts valid IPs" do
|
28
|
+
subject = build_ip_record :v6, :ip => '::1'
|
29
|
+
subject.valid?.must_equal true
|
30
|
+
subject.errors.size.must_equal 0
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "for invalid IPs" do
|
34
|
+
it "rejects invalid IPs" do
|
35
|
+
subject = build_ip_record :v6, :ip => '192.168.1.1'
|
36
|
+
subject.valid?.must_equal false
|
37
|
+
subject.errors.size.must_equal 1
|
38
|
+
end
|
39
|
+
|
40
|
+
it "generates an error message of type invalid" do
|
41
|
+
subject = build_ip_record :v6, :ip => '192.168.1.1'
|
42
|
+
subject.valid?.must_equal false
|
43
|
+
subject.errors[:ip].include?(subject.errors.generate_message(:ip, :invalid)).must_equal true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "checks validity of the arguments" do
|
49
|
+
[3, "foo", 1..6].each do |wrong_argument|
|
50
|
+
assert_raises(ArgumentError,"Unknown IP validator format #{wrong_argument.inspect}") do
|
51
|
+
TestRecord.validates :ip, :ip => { :format => wrong_argument }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_ip_record(version, attrs = {})
|
57
|
+
TestRecord.reset_callbacks(:validate)
|
58
|
+
TestRecord.validates :ip, :ip => { :format => version }
|
59
|
+
TestRecord.new attrs
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper.rb'
|
2
|
+
|
3
|
+
describe "Password Validation" do
|
4
|
+
STRENGTHS = {
|
5
|
+
:weak => { :valid => 'sixchr', :invalid => 'foo' },
|
6
|
+
:medium => { :valid => 'chrs123', :invalid => 'sixchr' },
|
7
|
+
:strong => { :valid => 'HQSij2323#$%', :invalid => 'chrs123' }
|
8
|
+
}
|
9
|
+
|
10
|
+
STRENGTHS.each_pair do |strength, passwords|
|
11
|
+
describe "#{strength} mode" do
|
12
|
+
describe "valid passwords" do
|
13
|
+
it "accepts a #{strength} password like #{passwords[:valid]}" do
|
14
|
+
subject = build_password_record strength, :password => passwords[:valid]
|
15
|
+
subject.valid?.must_equal true
|
16
|
+
subject.errors.size.must_equal 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "invalid passwords" do
|
21
|
+
it "rejects invalid passwords like #{passwords[:invalid]}" do
|
22
|
+
subject = build_password_record strength, :password => passwords[:invalid]
|
23
|
+
subject.valid?.must_equal false
|
24
|
+
subject.errors.size.must_equal 1
|
25
|
+
end
|
26
|
+
|
27
|
+
it "generates an error message of type invalid" do
|
28
|
+
subject = build_password_record strength, :password => passwords[:invalid]
|
29
|
+
subject.valid?.must_equal false
|
30
|
+
subject.errors[:password].include?(subject.errors.generate_message(:password, :invalid)).must_equal true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
def build_password_record(strength, attrs = {})
|
36
|
+
TestRecord.reset_callbacks(:validate)
|
37
|
+
TestRecord.validates :password, :password => { :strength => strength }
|
38
|
+
TestRecord.new attrs
|
39
|
+
end
|
40
|
+
end
|