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 CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activevalidators (1.1.0)
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, :slug => true
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
  ----
@@ -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.1.0'
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
@@ -12,5 +12,8 @@ module ActiveModel
12
12
  autoload :RespondToValidator
13
13
  autoload :PhoneValidator
14
14
  autoload :SlugValidator
15
+ autoload :IpValidator
16
+ autoload :CreditCardValidator
17
+ autoload :DateValidator, 'date_validator'
15
18
  end
16
19
  end
@@ -0,0 +1,7 @@
1
+ module Models
2
+ class CreditCardValidatorModel
3
+ include ActiveModel::Validations
4
+ attr_accessor :card
5
+ validates :card, :credit_card => { :type => :any }, :if => :card
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Models
2
+ class IpValidatorModel
3
+ include ActiveModel::Validations
4
+ attr_accessor :ipv4, :ipv6
5
+ validates :ipv4, :ip => { :format => :v4 }, :if => :ipv4
6
+ validates :ipv6, :ip => { :format => :v6 }, :if => :ipv6
7
+ end
8
+ end
@@ -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
- - 1
7
+ - 2
8
8
  - 0
9
- version: 1.1.0
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-01 00:00:00 +01:00
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