activevalidators 1.1.0 → 1.2.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/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
|