activevalidators 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ # Offical support for MRIs + REE
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - ruby-head
7
+ - ree
8
+ - rbx
9
+ - rbx-2.0
10
+ - jruby
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ActiveValidators
1
+ ActiveValidators [![Travis](http://travis-ci.org/cesario/activevalidators.png)](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
- begin
16
- require 'rspec/core/rake_task'
17
-
18
- desc "Run specs"
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 => :spec
59
+ task :default => :full
@@ -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 = '1.5.1'
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 "rspec"
17
- s.add_development_dependency "rspec-core"
18
- s.add_development_dependency "rspec-expectations"
19
- s.add_dependency 'activerecord' , '~> 3.0.0'
20
- s.add_dependency 'activemodel' , '~> 3.0.0'
21
- s.add_dependency 'mail' , '~> 2.2.15'
22
- s.add_dependency 'date_validator', '~> 0.6.1'
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
- @value = value
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
- @formats = TrackingNumberValidator.known_formats[carrier]
9
- raise "No known tracking number formats for carrier #{carrier}" unless @formats
10
- record.errors.add(attribute) unless matches_any?
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
- def self.known_formats
14
- @@known_formats ||= {
15
- # see https://www.ups.com/content/us/en/tracking/help/tracking/tnh.html
16
- :ups => ['1Z................', '............', 'T..........', '.........'],
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
- def matches_any?
21
- false if @formats.nil? or not @formats.respond_to?(:detect)
22
- @formats.detect { |format| @value.match(TrackingNumberValidator.regexp_from format) }
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
- private
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
- def self.regexp_from(format)
28
- Regexp.new "^"+(Regexp.escape format).gsub('\#','\d').gsub('\.','[a-zA-Z0-9]')+"$"
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
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+ module ActiveValidators
3
+ VERSION = '1.6.0'
4
+ end
@@ -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 File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
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.card = number
41
- subject.should be_valid
42
- subject.should have(0).errors
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
- before :each do
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
- it "generates an error message of type invalid" do
58
- subject.should_not be_valid
59
- subject.errors[:card].should include subject.errors.generate_message(:card, :invalid)
60
- end
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