activevalidators 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +5 -1
- data/README.md +20 -1
- data/activevalidators.gemspec +2 -2
- data/lib/active_model/validations/credit_card_validator.rb +57 -0
- data/lib/active_model/validations/ip_validator.rb +13 -0
- data/lib/activevalidators.rb +3 -0
- data/spec/models/credit_card_validator_model.rb +7 -0
- data/spec/models/ip_validator_model.rb +8 -0
- data/spec/specs/credit_card_spec.rb +56 -0
- data/spec/specs/date_validator_spec.rb +124 -0
- data/spec/specs/ip_spec.rb +57 -0
- metadata +30 -3
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
activevalidators (1.
|
4
|
+
activevalidators (1.2.0)
|
5
5
|
activemodel (>= 3.0.0)
|
6
6
|
activerecord (>= 3.0.0)
|
7
|
+
date_validator (= 0.5.9)
|
7
8
|
mail
|
8
9
|
|
9
10
|
GEM
|
@@ -21,6 +22,8 @@ GEM
|
|
21
22
|
activesupport (3.0.3)
|
22
23
|
arel (2.0.4)
|
23
24
|
builder (2.1.2)
|
25
|
+
date_validator (0.5.9)
|
26
|
+
activemodel (>= 3.0.0)
|
24
27
|
diff-lcs (1.1.2)
|
25
28
|
i18n (0.4.2)
|
26
29
|
mail (2.2.10)
|
@@ -50,6 +53,7 @@ DEPENDENCIES
|
|
50
53
|
activerecord (>= 3.0.0)
|
51
54
|
activevalidators!
|
52
55
|
bundler
|
56
|
+
date_validator (= 0.5.9)
|
53
57
|
mail
|
54
58
|
rspec
|
55
59
|
rspec-core
|
data/README.md
CHANGED
@@ -23,15 +23,34 @@ In your models, the gem provides new validators like `email`, or `url`:
|
|
23
23
|
end
|
24
24
|
|
25
25
|
class Article
|
26
|
-
validates :slug,
|
26
|
+
validates :slug, :slug => true
|
27
|
+
validates :expiration_date,
|
28
|
+
:date => {
|
29
|
+
:after => lambda { Time.now },
|
30
|
+
:before => lambda { Time.now + 1.year }
|
31
|
+
}
|
27
32
|
end
|
28
33
|
|
34
|
+
class Device
|
35
|
+
validates :ipv6, :ip => { :format => :v6 }
|
36
|
+
validates :ipv4, :ip => { :format => :v4 }
|
37
|
+
end
|
38
|
+
|
39
|
+
class Account
|
40
|
+
validates :visa_card, :credit_card => { :type => :visa }
|
41
|
+
validates :credit_card, :credit_card => { :type => :all }
|
42
|
+
end
|
43
|
+
|
44
|
+
|
29
45
|
Exhaustive list of supported validators and their implementation:
|
30
46
|
|
31
47
|
* `email` : based on the `mail` gem
|
32
48
|
* `url` : based on a regular expression
|
33
49
|
* `phone` : based on a regular expression
|
34
50
|
* `slug` : based on `ActiveSupport::String#parameterize`
|
51
|
+
* `ip` : based on `Resolv::IPv[4|6]::Regex`
|
52
|
+
* `credit_card` : based on the `Luhnacy` gem
|
53
|
+
* `date` : based on the `DateValidator` gem
|
35
54
|
|
36
55
|
Todo
|
37
56
|
----
|
data/activevalidators.gemspec
CHANGED
@@ -4,7 +4,7 @@ $:.unshift lib unless $:.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "activevalidators"
|
7
|
-
s.version = '1.
|
7
|
+
s.version = '1.2.0'
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Franck Verrot"]
|
10
10
|
s.email = ["franck@verrot.fr"]
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.add_dependency 'activerecord', '>= 3.0.0'
|
20
20
|
s.add_dependency 'activemodel', '>= 3.0.0'
|
21
21
|
s.add_dependency 'mail'
|
22
|
-
|
22
|
+
s.add_dependency 'date_validator', '0.5.9'
|
23
23
|
|
24
24
|
s.files = `git ls-files`.split("\n")
|
25
25
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Validations
|
3
|
+
|
4
|
+
class CreditCardValidator < EachValidator
|
5
|
+
def validate_each(record, attribute, value)
|
6
|
+
record.errors.add(attribute) unless Luhn.valid?(options[:type], sanitize_card(value))
|
7
|
+
end
|
8
|
+
|
9
|
+
def sanitize_card(value)
|
10
|
+
value.tr('- ','')
|
11
|
+
end
|
12
|
+
|
13
|
+
class Luhn
|
14
|
+
def self.valid?(card_type,number)
|
15
|
+
if card_type == :any
|
16
|
+
self.luhn_valid?(number)
|
17
|
+
else
|
18
|
+
self.send("#{card_type.to_s}?", number)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.mastercard?(number)
|
23
|
+
self.luhn_valid?(number) and !(number !~ /^5[1-5].{14}/)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.visa?(number)
|
27
|
+
self.luhn_valid?(number) and !(number !~ /^4.{15}/)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.amex?(number)
|
31
|
+
self.luhn_valid?(number) and !(number !~ /^3[47].{13}/)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.luhn_valid?(s)
|
35
|
+
return false unless s && s.is_a?(String)
|
36
|
+
return false if s.empty?
|
37
|
+
value = s.gsub(/\D/, '')
|
38
|
+
return false if value.empty?
|
39
|
+
value.
|
40
|
+
reverse.
|
41
|
+
each_char.
|
42
|
+
collect(&:to_i).
|
43
|
+
each_with_index.
|
44
|
+
inject(0) {| num, (i, index) |
|
45
|
+
num + if (index + 1) % 2 == 0
|
46
|
+
i*=2; ((i > 9) ? (i % 10) + 1 : i)
|
47
|
+
else
|
48
|
+
i
|
49
|
+
end
|
50
|
+
} % 10 == 0
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Validations
|
3
|
+
require 'resolv'
|
4
|
+
class IpValidator < EachValidator
|
5
|
+
def validate_each(record, attribute, value)
|
6
|
+
r = Resolv.const_get("IP#{options[:format]}")
|
7
|
+
raise "Unknown IP validator format #{options[:format].inspect}" if r.nil?
|
8
|
+
record.errors.add(attribute) unless !(r::Regex !~ value)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/activevalidators.rb
CHANGED
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
|
2
|
+
|
3
|
+
# Here are some valid credit cards
|
4
|
+
CARDS = {
|
5
|
+
#American Express
|
6
|
+
:amex => '3400 0000 0000 009',
|
7
|
+
#Carte Blanche
|
8
|
+
:carte_blanche => '3000 0000 0000 04',
|
9
|
+
#Discover
|
10
|
+
:discover => '6011 0000 0000 0004',
|
11
|
+
#Diners Club
|
12
|
+
:diners_club => '3852 0000 0232 37',
|
13
|
+
#enRoute
|
14
|
+
:en_route => '2014 0000 0000 009',
|
15
|
+
#JCB
|
16
|
+
:jcb => '2131 0000 0000 0008',
|
17
|
+
#MasterCard
|
18
|
+
:master_card => '5500 0000 0000 0004',
|
19
|
+
#Solo
|
20
|
+
:solo => '6334 0000 0000 0004',
|
21
|
+
#Switch
|
22
|
+
:switch => '4903 0100 0000 0009',
|
23
|
+
#Visa
|
24
|
+
:visa => '4111 1111 1111 1111',
|
25
|
+
#Laser
|
26
|
+
:laser => '6304 1000 0000 0008'
|
27
|
+
}
|
28
|
+
|
29
|
+
describe "Credit Card Validation" do
|
30
|
+
CARDS.each_pair do |card, number|
|
31
|
+
it "accepts #{card} valid cards" do
|
32
|
+
model = Models::CreditCardValidatorModel.new
|
33
|
+
model.card = number
|
34
|
+
model.valid?.should be(true)
|
35
|
+
model.should have(0).errors
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "for invalid cards" do
|
40
|
+
let(:model) do
|
41
|
+
Models::CreditCardValidatorModel.new.tap do |m|
|
42
|
+
m.card = '99999'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "rejects invalid cards" do
|
47
|
+
model.valid?.should be(false)
|
48
|
+
model.should have(1).errors
|
49
|
+
end
|
50
|
+
|
51
|
+
it "generates an error message of type invalid" do
|
52
|
+
model.valid?.should be(false)
|
53
|
+
model.errors[:card].should == [model.errors.generate_message(:card, :invalid)]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#Copied from DateValidator v0.5.9
|
2
|
+
#The translation stuff has been removed though
|
3
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
|
4
|
+
|
5
|
+
require 'active_support/time' # For testing Date and TimeWithZone objects
|
6
|
+
|
7
|
+
class TestRecord
|
8
|
+
include ActiveModel::Validations
|
9
|
+
attr_accessor :expiration_date
|
10
|
+
|
11
|
+
def initialize(expiration_date)
|
12
|
+
@expiration_date = expiration_date
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "DateValidator" do
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
TestRecord.reset_callbacks(:validate)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "checks validity of the arguments" do
|
23
|
+
[3, "foo", 1..6].each do |wrong_argument|
|
24
|
+
expect {
|
25
|
+
TestRecord.validates :expiration_date, :date => {:before => wrong_argument}
|
26
|
+
}.to raise_error(ArgumentError, ":before must be a time, a date, a time_with_zone, a symbol or a proc")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "complains if provided with no options" do
|
31
|
+
TestRecord.validates :expiration_date, :date => {:before => Time.now}
|
32
|
+
model = TestRecord.new(nil)
|
33
|
+
model.should_not be_valid
|
34
|
+
end
|
35
|
+
|
36
|
+
[:valid,:invalid].each do |should_be|
|
37
|
+
|
38
|
+
_context = should_be == :valid ? 'when value validates correctly' : 'when value does not match validation requirements'
|
39
|
+
|
40
|
+
context _context do
|
41
|
+
|
42
|
+
[:after, :before, :after_or_equal_to, :before_or_equal_to].each do |check|
|
43
|
+
|
44
|
+
now = Time.now.to_datetime
|
45
|
+
|
46
|
+
model_date = case check
|
47
|
+
when :after then should_be == :valid ? now + 21000 : now - 1
|
48
|
+
when :before then should_be == :valid ? now - 21000 : now + 1
|
49
|
+
when :after_or_equal_to then should_be == :valid ? now : now - 21000
|
50
|
+
when :before_or_equal_to then should_be == :valid ? now : now + 21000
|
51
|
+
end
|
52
|
+
|
53
|
+
# it "ensures that an attribute is #{should_be} when #{should_be == :valid ? 'respecting' : 'offending' } the #{check} check" do
|
54
|
+
#TestRecord.validates :expiration_date, :date => {:"#{check}" => Time.now}
|
55
|
+
#model = TestRecord.new(model_date)
|
56
|
+
#should_be == :valid ? model.should(be_valid, "an attribute should be valid when respecting the #{check} check") : model.should_not(be_valid, "an attribute should be invalidwhen offending the #{check} check")
|
57
|
+
#end
|
58
|
+
|
59
|
+
if _context == 'when value does not match validation requirements'
|
60
|
+
|
61
|
+
it "yields a default error message indicating that value must be #{check} validation requirements" do
|
62
|
+
TestRecord.validates :expiration_date, :date => {:"#{check}" => Time.now}
|
63
|
+
model = TestRecord.new(model_date)
|
64
|
+
model.should_not be_valid
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
if _context == 'when value does not match validation requirements'
|
72
|
+
|
73
|
+
it "allows for a custom validation message" do
|
74
|
+
TestRecord.validates :expiration_date, :date => {:before_or_equal_to => Time.now, :message => 'must be after Christmas'}
|
75
|
+
model = TestRecord.new(Time.now + 21000)
|
76
|
+
model.should_not be_valid
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
extra_types = [:proc, :symbol]
|
86
|
+
extra_types.push(:date) if defined?(Date) and defined?(DateTime)
|
87
|
+
extra_types.push(:time_with_zone) if defined?(ActiveSupport::TimeWithZone)
|
88
|
+
|
89
|
+
extra_types.each do |type|
|
90
|
+
it "accepts a #{type} as an argument to a check" do
|
91
|
+
case type
|
92
|
+
when :proc then
|
93
|
+
expect {
|
94
|
+
TestRecord.validates :expiration_date, :date => {:after => Proc.new{Time.now + 21000}}
|
95
|
+
}.to_not raise_error
|
96
|
+
when :symbol then
|
97
|
+
expect {
|
98
|
+
TestRecord.send(:define_method, :min_date, lambda { Time.now + 21000 })
|
99
|
+
TestRecord.validates :expiration_date, :date => {:after => :min_date}
|
100
|
+
}.to_not raise_error
|
101
|
+
when :date then
|
102
|
+
expect {
|
103
|
+
TestRecord.validates :expiration_date, :date => {:after => Time.now.to_date}
|
104
|
+
}.to_not raise_error
|
105
|
+
when :time_with_zone then
|
106
|
+
expect {
|
107
|
+
Time.zone = "Hawaii"
|
108
|
+
TestRecord.validates :expiration_date, :date => {:before => Time.zone.parse((Time.now + 21000).to_s)}
|
109
|
+
}.to_not raise_error
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it "gracefully handles an unexpected result from a proc argument evaluation" do
|
115
|
+
TestRecord.validates :expiration_date, :date => {:after => Proc.new{ nil }}
|
116
|
+
TestRecord.new(Time.now).should_not be_valid
|
117
|
+
end
|
118
|
+
|
119
|
+
it "gracefully handles an unexpected result from a symbol argument evaluation" do
|
120
|
+
TestRecord.send(:define_method, :min_date, lambda { nil })
|
121
|
+
TestRecord.validates :expiration_date, :date => {:after => :min_date}
|
122
|
+
TestRecord.new(Time.now).should_not be_valid
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe "IP Validation" do
|
4
|
+
describe "IPv4 Validation" do
|
5
|
+
it "accepts valid IPs" do
|
6
|
+
model = Models::IpValidatorModel.new
|
7
|
+
model.ipv4 = '192.168.1.1'
|
8
|
+
model.valid?.should be(true)
|
9
|
+
model.should have(0).errors
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "for invalid IPs" do
|
13
|
+
let(:model) do
|
14
|
+
Models::IpValidatorModel.new.tap do |m|
|
15
|
+
m.ipv4 = '267.34.56.3'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "rejects invalid IPs" do
|
20
|
+
model.valid?.should be(false)
|
21
|
+
model.should have(1).errors
|
22
|
+
end
|
23
|
+
|
24
|
+
it "generates an error message of type invalid" do
|
25
|
+
model.valid?.should be(false)
|
26
|
+
model.errors[:ipv4].should == [model.errors.generate_message(:ipv4, :invalid)]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "IPv6 Validation" do
|
32
|
+
it "accepts valid IPs" do
|
33
|
+
model = Models::IpValidatorModel.new
|
34
|
+
model.ipv6 = '::1'
|
35
|
+
model.valid?.should be(true)
|
36
|
+
model.should have(0).errors
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "for invalid IPs" do
|
40
|
+
let(:model) do
|
41
|
+
Models::IpValidatorModel.new.tap do |m|
|
42
|
+
m.ipv6 = '192.168.1.1'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "rejects invalid IPs" do
|
47
|
+
model.valid?.should be(false)
|
48
|
+
model.should have(1).errors
|
49
|
+
end
|
50
|
+
|
51
|
+
it "generates an error message of type invalid" do
|
52
|
+
model.valid?.should be(false)
|
53
|
+
model.errors[:ipv6].should == [model.errors.generate_message(:ipv6, :invalid)]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 1.
|
9
|
+
version: 1.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Franck Verrot
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-12-
|
17
|
+
date: 2010-12-04 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -112,6 +112,21 @@ dependencies:
|
|
112
112
|
version: "0"
|
113
113
|
type: :runtime
|
114
114
|
version_requirements: *id007
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: date_validator
|
117
|
+
prerelease: false
|
118
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - "="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
segments:
|
124
|
+
- 0
|
125
|
+
- 5
|
126
|
+
- 9
|
127
|
+
version: 0.5.9
|
128
|
+
type: :runtime
|
129
|
+
version_requirements: *id008
|
115
130
|
description: ActiveValidators is a collection of ActiveModel/ActiveRecord validations
|
116
131
|
email:
|
117
132
|
- franck@verrot.fr
|
@@ -131,19 +146,26 @@ files:
|
|
131
146
|
- Rakefile
|
132
147
|
- activevalidators.gemspec
|
133
148
|
- autotest/discover.rb
|
149
|
+
- lib/active_model/validations/credit_card_validator.rb
|
134
150
|
- lib/active_model/validations/email_validator.rb
|
151
|
+
- lib/active_model/validations/ip_validator.rb
|
135
152
|
- lib/active_model/validations/phone_validator.rb
|
136
153
|
- lib/active_model/validations/respond_to_validator.rb
|
137
154
|
- lib/active_model/validations/slug_validator.rb
|
138
155
|
- lib/active_model/validations/url_validator.rb
|
139
156
|
- lib/activevalidators.rb
|
157
|
+
- spec/models/credit_card_validator_model.rb
|
140
158
|
- spec/models/email_validator_model.rb
|
159
|
+
- spec/models/ip_validator_model.rb
|
141
160
|
- spec/models/phone_validator_model.rb
|
142
161
|
- spec/models/respond_to_validator_model.rb
|
143
162
|
- spec/models/slug_validator_model.rb
|
144
163
|
- spec/models/url_validator_model.rb
|
145
164
|
- spec/spec_helper.rb
|
165
|
+
- spec/specs/credit_card_spec.rb
|
166
|
+
- spec/specs/date_validator_spec.rb
|
146
167
|
- spec/specs/email_spec.rb
|
168
|
+
- spec/specs/ip_spec.rb
|
147
169
|
- spec/specs/phone_spec.rb
|
148
170
|
- spec/specs/respond_to_spec.rb
|
149
171
|
- spec/specs/slug_spec.rb
|
@@ -181,13 +203,18 @@ signing_key:
|
|
181
203
|
specification_version: 3
|
182
204
|
summary: Collection of ActiveModel/ActiveRecord validations
|
183
205
|
test_files:
|
206
|
+
- spec/models/credit_card_validator_model.rb
|
184
207
|
- spec/models/email_validator_model.rb
|
208
|
+
- spec/models/ip_validator_model.rb
|
185
209
|
- spec/models/phone_validator_model.rb
|
186
210
|
- spec/models/respond_to_validator_model.rb
|
187
211
|
- spec/models/slug_validator_model.rb
|
188
212
|
- spec/models/url_validator_model.rb
|
189
213
|
- spec/spec_helper.rb
|
214
|
+
- spec/specs/credit_card_spec.rb
|
215
|
+
- spec/specs/date_validator_spec.rb
|
190
216
|
- spec/specs/email_spec.rb
|
217
|
+
- spec/specs/ip_spec.rb
|
191
218
|
- spec/specs/phone_spec.rb
|
192
219
|
- spec/specs/respond_to_spec.rb
|
193
220
|
- spec/specs/slug_spec.rb
|