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 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