louche 0.0.1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 95dae9aded657e8a50393dff79f4aa990a85a85a
4
- data.tar.gz: 77e8c4cd58abda5885700f4ab51958a4909ba985
3
+ metadata.gz: 4fb1ff2a90ab738a9cc157e1941ad7f81a8766db
4
+ data.tar.gz: 5cfc8efcf777ef57f988c788715ad197549ba682
5
5
  SHA512:
6
- metadata.gz: a12afab71e59a21419e1e36d4110ec9acf7c66bb6fcf013d1b4b886b33c618c2fa722a1a2e6ccc7bba45faf2dfccc23082e5b0a4257918496b654b6830c2f9c8
7
- data.tar.gz: b3bc2a87ea079190ef8bd1c003a113efcbbb23a13da9ff57c7fdc60b58b420b7eed6b2d2ddade2099e29fea1be95ecd9e93a477cf1fee1975f2b9cd4daf5f0e0
6
+ metadata.gz: b2485bba9066234d23c656ea1b50676737eaeda547f60e0c7b08d2e842e36baf05a8eb3efdc8a14c66e14ae94a8a1ad6447224e129c0f5991d02296e95ebd630
7
+ data.tar.gz: 1442e35ad3da1b2fa5029fcc13e3fffe7d2726f4bae934d0fb2b3083b18a338f5531bd2b3aabd53d737906c80254ff796ccb25b2de9ef49f2ee7a11503b988fe
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.travis.yml ADDED
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.0.0
5
+ - 1.9.3
6
+
7
+ gemfile:
8
+ - gemfiles/Gemfile.activemodel-4.1
9
+ - gemfiles/Gemfile.activemodel-4.0
10
+
11
+ script: "echo 'DO IT' && bundle exec rake spec"
12
+
13
+ notifications:
14
+ hipchat:
15
+ rooms:
16
+ secure: "TF6e2Z/HSTFH2g5j8kILrJvEMyYF6Lx5uFIFp/VbTHc6XzaZ1Ot5DjcZr8aychcObYKNVW01/DTCcJ9Acs1GOVuBVgUZbRWUhvlBVAOAElYLJhOTDveYUiARXIwnmXgOZmZRuBagJqqHKBzoIo8XTO3l68txs1tHZA+sSvNJoWQ="
17
+ template:
18
+ - '%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message} (<a href="%{build_url}">Build</a>/<a href="%{compare_url}">Changes</a>)'
19
+ format: 'html'
data/Gemfile CHANGED
@@ -1,4 +1,2 @@
1
1
  source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in louche.gemspec
4
2
  gemspec
data/README.md CHANGED
@@ -1,4 +1,13 @@
1
- # Louche
1
+ <p align="center">
2
+ <a href="https://github.com/mirego/microscope">
3
+ <img src="http://i.imgur.com/FhNZIjg.png" alt="Louche" />
4
+ </a>
5
+ <br />
6
+ Louche adds common validators for ActiveModel/ActiveRecord classes.
7
+ <br /><br />
8
+ <a href="https://rubygems.org/gems/louche"><img src="https://badge.fury.io/rb/louche.png" /></a>
9
+ <a href="https://travis-ci.org/mirego/louche"><img src="https://travis-ci.org/mirego/louche.png?branch=master" /></a>
10
+ </p>
2
11
 
3
12
  ---
4
13
 
@@ -14,10 +23,80 @@ gem 'louche'
14
23
 
15
24
  Louche provides a few validators to use in your ActiveModel/ActiveRecord classes:
16
25
 
17
- * `EmailValidator`
18
- * `URLValidator`
19
- * `PhoneNumberValidator`
20
- * `PostalCodeValidator`
26
+ ### `EmailValidator`
27
+
28
+ ```ruby
29
+ class User < ActiveRecord::Base
30
+ validates :email, email: true
31
+ end
32
+
33
+ User.new(email: 'foo@example.com').valid? # => true
34
+ User.new(email: 'foo@example').valid? # => false
35
+ ```
36
+
37
+ ### `URLValidator`
38
+
39
+ ```ruby
40
+ class User < ActiveRecord::Base
41
+ validates :website, url: true
42
+ end
43
+
44
+ User.new(website: 'http://example.com').valid? # => true
45
+ User.new(website: 'example.$$$').valid? # => false
46
+ ```
47
+
48
+ ### `PhoneNumberValidator`
49
+
50
+ ```ruby
51
+ class User < ActiveRecord::Base
52
+ validates :phone_number, phone_number: true
53
+ end
54
+
55
+ user = User.new(phone_number: '514 555-2525')
56
+ user.valid? # => true
57
+ user.phone_number # => '5145552525'
58
+
59
+ user = User.new(phone_number: '555-2525')
60
+ user.valid? # => false
61
+ user.phone_number # '5552525'
62
+ ```
63
+
64
+ ### `PostalCodeValidator`
65
+
66
+ ```ruby
67
+ class User < ActiveRecord::Base
68
+ validates :postal_code, postal_code: true
69
+ end
70
+
71
+ user = User.new(postal_code: 'G0R 2T0')
72
+ user.valid? # => true
73
+ user.postal_code # => 'G0R2T0'
74
+
75
+ user = User.new(postal_code: 'L -0- L')
76
+ user.valid? # => false
77
+ user.postal_code # => 'L0L'
78
+ ```
79
+
80
+ ### `ArrayValidator`
81
+
82
+ ```ruby
83
+ class Tag < Struct.new(:name)
84
+ def valid?
85
+ name.present?
86
+ end
87
+ end
88
+
89
+ class User < ActiveRecord::Base
90
+ validates :tags, array: true
91
+
92
+ def tags=(tags)
93
+ super tags.map { |tag| Tag.new(tag) }
94
+ end
95
+ end
96
+
97
+ User.new(tags: ['food', 'beer', 'code']).valid? # => true
98
+ User.new(tags: ['food', '', 'code']).valid? # => false
99
+ ```
21
100
 
22
101
  ## License
23
102
 
data/Rakefile CHANGED
@@ -1,2 +1,11 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler'
2
+ require 'rake'
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
2
5
 
6
+ task default: :spec
7
+
8
+ desc 'Run all specs'
9
+ RSpec::Core::RakeTask.new(:spec) do |task|
10
+ task.pattern = 'spec/**/*_spec.rb'
11
+ end
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '../'
4
+
5
+ gem 'activemodel', '~> 4.0.0'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '../'
4
+
5
+ gem 'activemodel', '~> 4.1.0'
data/lib/louche.rb CHANGED
@@ -1,4 +1,13 @@
1
1
  require 'louche/version'
2
2
 
3
+ require 'active_model'
4
+ require 'active_support/core_ext/hash'
5
+
6
+ require 'louche/validators/array_validator'
7
+ require 'louche/validators/email_validator'
8
+ require 'louche/validators/phone_number_validator'
9
+ require 'louche/validators/postal_code_validator'
10
+ require 'louche/validators/url_validator'
11
+
3
12
  module Louche
4
13
  end
@@ -0,0 +1,18 @@
1
+ class ArrayValidator < ActiveModel::EachValidator
2
+ def initialize(options)
3
+ options.reverse_merge!(message: :invalid_array)
4
+ super
5
+ end
6
+
7
+ def validate_each(record, attribute, value)
8
+ if value.is_a?(Array)
9
+ add_error(record, attribute, value) if value.empty? || !value.map.all?(&:valid?)
10
+ else
11
+ add_error(record, attribute, value)
12
+ end
13
+ end
14
+
15
+ def add_error(record, attribute, value)
16
+ record.errors.add(attribute, options.fetch(:message), value: value)
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ class EmailValidator < ActiveModel::EachValidator
2
+ def initialize(options)
3
+ options.reverse_merge!(message: :invalid_email)
4
+ options.reverse_merge!(regex: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i)
5
+ super
6
+ end
7
+
8
+ def validate_each(record, attribute, value)
9
+ unless value =~ options.fetch(:regex)
10
+ record.errors.add(attribute, options.fetch(:message), value: value)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ class PhoneNumberValidator < ActiveModel::EachValidator
2
+ def initialize(options)
3
+ options.reverse_merge!(message: :invalid_phone_number)
4
+ options.reverse_merge!(regex: /\d{10,}/)
5
+ options.reverse_merge!(cleanup_regex: /[^\d]/)
6
+ super
7
+ end
8
+
9
+ def validate_each(record, attribute, value)
10
+ value = PhoneNumberValidator.format_number(value, options.fetch(:cleanup_regex))
11
+ record.send(:"#{attribute}=", value)
12
+
13
+ unless value =~ options.fetch(:regex)
14
+ record.errors.add(attribute, options.fetch(:message), value: value)
15
+ end
16
+ end
17
+
18
+ def self.format_number(number, regex)
19
+ number.try(:gsub, regex, '')
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ class PostalCodeValidator < ActiveModel::EachValidator
2
+ def initialize(options)
3
+ options.reverse_merge!(message: :invalid_postal_code)
4
+ options.reverse_merge!(regex: /^[a-z]\d[a-z]\d[a-z]\d$/i)
5
+ options.reverse_merge!(cleanup_regex: /[^a-z0-9]/i)
6
+ super
7
+ end
8
+
9
+ def validate_each(record, attribute, value)
10
+ value = PostalCodeValidator.format_code(value, options.fetch(:cleanup_regex))
11
+ record.send(:"#{attribute}=", value)
12
+
13
+ unless value =~ options.fetch(:regex)
14
+ record.errors.add(attribute, options.fetch(:message), value: value)
15
+ end
16
+ end
17
+
18
+ def self.format_code(postal_code, regex)
19
+ postal_code.try(:gsub, regex, '')
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ class URLValidator < ActiveModel::EachValidator
2
+ def initialize(options)
3
+ options.reverse_merge!(schemes: %w(http https))
4
+ options.reverse_merge!(message: :invalid_url)
5
+ super
6
+ end
7
+
8
+ def validate_each(record, attribute, value)
9
+ unless self.class.valid_url?(value, options)
10
+ record.errors.add(attribute, options.fetch(:message), value: value)
11
+ end
12
+ end
13
+
14
+ def self.valid_url?(url, options)
15
+ schemes = [*options.fetch(:schemes)].map(&:to_s)
16
+
17
+ if URI::regexp(schemes).match(url)
18
+ begin
19
+ URI.parse(url)
20
+ rescue URI::InvalidURIError
21
+ false
22
+ end
23
+ else
24
+ false
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module Louche
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1'
3
3
  end
data/louche.gemspec CHANGED
@@ -18,6 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_development_dependency "bundler", '~> 1.6'
21
+ spec.add_dependency 'activemodel', '>= 4.0.0'
22
+
23
+ spec.add_development_dependency 'rspec', '~> 3.0.0.beta2'
24
+ spec.add_development_dependency 'bundler', '~> 1.6'
22
25
  spec.add_development_dependency 'rake'
23
26
  end
@@ -0,0 +1,7 @@
1
+ $:.unshift File.expand_path('../lib', __FILE__)
2
+
3
+ require 'rspec'
4
+ require 'louche'
5
+
6
+ RSpec.configure do |config|
7
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArrayValidator do
4
+ describe :validate_each do
5
+ let(:validator) { described_class.new(options) }
6
+ let(:options) { { attributes: [attribute] } }
7
+ let(:record) { double('ActiveRecord::Base', errors: errors) }
8
+ let(:errors) { double('ActiveModel::Errors') }
9
+ let(:attribute) { :items }
10
+
11
+ shared_examples 'a successful validator' do
12
+ before { expect(errors).to_not receive(:add) }
13
+ specify { validator.validate_each(record, attribute, value) }
14
+ end
15
+
16
+ shared_examples 'an erroneous validator' do
17
+ before do
18
+ expect(errors).to receive(:add).with(attribute, :invalid_array, value: value)
19
+ end
20
+
21
+ specify { validator.validate_each(record, attribute, value) }
22
+ end
23
+
24
+ let(:valid_item) { double('ArrayItem', valid?: true) }
25
+ let(:invalid_item) { double('ArrayItem', valid?: false) }
26
+
27
+ context 'with an invalid value' do
28
+ context 'with non-Array valud' do
29
+ let(:value) { valid_item }
30
+ it_behaves_like 'an erroneous validator'
31
+ end
32
+
33
+ context 'with value containing invalid item' do
34
+ let(:value) { [valid_item, invalid_item] }
35
+ it_behaves_like 'an erroneous validator'
36
+ end
37
+
38
+ context 'with empty value' do
39
+ let(:value) { [] }
40
+ it_behaves_like 'an erroneous validator'
41
+ end
42
+ end
43
+
44
+ context 'with valid value' do
45
+ let(:value) { [valid_item, valid_item, valid_item] }
46
+ it_behaves_like 'a successful validator'
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe EmailValidator do
4
+ describe :validate_each do
5
+ let(:validator) { described_class.new(options) }
6
+ let(:options) { { attributes: [attribute] } }
7
+ let(:record) { double('ActiveRecord::Base', errors: errors) }
8
+ let(:errors) { double('ActiveModel::Errors') }
9
+ let(:attribute) { :contact_email }
10
+
11
+ shared_examples 'a successful validator' do
12
+ before { expect(errors).to_not receive(:add) }
13
+ specify { validator.validate_each(record, attribute, value) }
14
+ end
15
+
16
+ shared_examples 'an erroneous validator' do
17
+ before do
18
+ expect(errors).to receive(:add).with(attribute, :invalid_email, value: value)
19
+ end
20
+
21
+ specify { validator.validate_each(record, attribute, value) }
22
+ end
23
+
24
+ context 'with an invalid value' do
25
+ context 'with missing @' do
26
+ let(:value) { 'exomel.com' }
27
+ it_behaves_like 'an erroneous validator'
28
+ end
29
+
30
+ context 'with missing TLD' do
31
+ let(:value) { 'example@exomelcom' }
32
+ it_behaves_like 'an erroneous validator'
33
+ end
34
+ end
35
+
36
+ context 'with valid value' do
37
+ let(:value) { 'example@exomel.com' }
38
+ it_behaves_like 'a successful validator'
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe PhoneNumberValidator do
4
+ describe :validate_each do
5
+ let(:validator) { described_class.new(options) }
6
+ let(:options) { { attributes: [attribute] } }
7
+ let(:record) { double('ActiveRecord::Base', errors: errors) }
8
+ let(:errors) { double('ActiveModel::Errors') }
9
+ let(:attribute) { :phone }
10
+
11
+ before { allow(record).to receive(:phone=).with(expected_stored_value) }
12
+
13
+ shared_examples 'a successful validator' do
14
+ before { expect(errors).to_not receive(:add) }
15
+ specify { validator.validate_each(record, attribute, value) }
16
+ end
17
+
18
+ shared_examples 'an erroneous validator' do
19
+ before do
20
+ expect(errors).to receive(:add).with(attribute, :invalid_phone_number, value: expected_stored_value)
21
+ end
22
+
23
+ specify { validator.validate_each(record, attribute, value) }
24
+ end
25
+
26
+ context 'with an invalid value' do
27
+ context 'with missing digits' do
28
+ let(:value) { '555-2525' }
29
+ let(:expected_stored_value) { '5552525' }
30
+ it_behaves_like 'an erroneous validator'
31
+ end
32
+
33
+ context 'containing invalid characters' do
34
+ let(:value) { '514 555-252A' }
35
+ let(:expected_stored_value) { '514555252' }
36
+ it_behaves_like 'an erroneous validator'
37
+ end
38
+ end
39
+
40
+ context 'with valid value' do
41
+ context 'with spaces and dashes' do
42
+ let(:value) { '514 555-2525' }
43
+ let(:expected_stored_value) { '5145552525' }
44
+
45
+ it_behaves_like 'a successful validator'
46
+ end
47
+
48
+ context 'without spaces' do
49
+ let(:value) { '5145552525' }
50
+ let(:expected_stored_value) { '5145552525' }
51
+
52
+ it_behaves_like 'a successful validator'
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe PostalCodeValidator do
4
+ describe :validate_each do
5
+ let(:validator) { described_class.new(options) }
6
+ let(:options) { { attributes: [attribute] } }
7
+ let(:record) { double('ActiveRecord::Base', errors: errors) }
8
+ let(:errors) { double('ActiveModel::Errors') }
9
+ let(:attribute) { :postal_code }
10
+
11
+ before { allow(record).to receive(:postal_code=).with(expected_stored_value) }
12
+
13
+ shared_examples 'a successful validator' do
14
+ before { expect(errors).to_not receive(:add) }
15
+ specify { validator.validate_each(record, attribute, value) }
16
+ end
17
+
18
+ shared_examples 'an erroneous validator' do
19
+ before do
20
+ expect(errors).to receive(:add).with(attribute, :invalid_postal_code, value: expected_stored_value)
21
+ end
22
+
23
+ specify { validator.validate_each(record, attribute, value) }
24
+ end
25
+
26
+ context 'with an invalid value' do
27
+ context 'with missing characters' do
28
+ let(:value) { 'G0R 2T' }
29
+ let(:expected_stored_value) { 'G0R2T' }
30
+
31
+ it_behaves_like 'an erroneous validator'
32
+ end
33
+
34
+ context 'with invalid characters' do
35
+ let(:value) { 'GOR2T$' }
36
+ let(:expected_stored_value) { 'GOR2T' }
37
+
38
+ it_behaves_like 'an erroneous validator'
39
+ end
40
+ end
41
+
42
+ context 'with valid value' do
43
+ context 'with spaces' do
44
+ let(:value) { ' G 0R 2T 0 ' }
45
+ let(:expected_stored_value) { 'G0R2T0' }
46
+
47
+ it_behaves_like 'a successful validator'
48
+ end
49
+
50
+ context 'without spaces' do
51
+ let(:value) { 'G0R2T0' }
52
+ let(:expected_stored_value) { 'G0R2T0' }
53
+
54
+ it_behaves_like 'a successful validator'
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe URLValidator do
4
+ describe :validate_each do
5
+ let(:validator) { described_class.new(options) }
6
+ let(:options) { { attributes: [attribute] } }
7
+ let(:record) { double('ActiveRecord::Base', errors: errors) }
8
+ let(:errors) { double('ActiveModel::Errors') }
9
+ let(:attribute) { :website_url }
10
+
11
+ shared_examples 'a successful validator' do
12
+ before { expect(errors).to_not receive(:add) }
13
+ specify { validator.validate_each(record, attribute, value) }
14
+ end
15
+
16
+ shared_examples 'an erroneous validator' do
17
+ before do
18
+ expect(errors).to receive(:add).with(attribute, :invalid_url, value: value)
19
+ end
20
+
21
+ specify { validator.validate_each(record, attribute, value) }
22
+ end
23
+
24
+ context 'with an invalid value' do
25
+ context 'with missing protocol' do
26
+ let(:value) { 'exomel.com' }
27
+ it_behaves_like 'an erroneous validator'
28
+ end
29
+
30
+ context 'containing invalid characters' do
31
+ let(:value) { 'http://www.$%.com' }
32
+ it_behaves_like 'an erroneous validator'
33
+ end
34
+ end
35
+
36
+ context 'with valid value' do
37
+ let(:value) { 'http://exomel.com' }
38
+ it_behaves_like 'a successful validator'
39
+ end
40
+ end
41
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: louche
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: '0.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rémi Prévost
@@ -10,6 +10,34 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2014-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.0.beta2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.0.beta2
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: bundler
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -46,13 +74,28 @@ extensions: []
46
74
  extra_rdoc_files: []
47
75
  files:
48
76
  - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
49
79
  - Gemfile
50
80
  - LICENSE.md
51
81
  - README.md
52
82
  - Rakefile
83
+ - gemfiles/Gemfile.activemodel-4.0
84
+ - gemfiles/Gemfile.activemodel-4.1
53
85
  - lib/louche.rb
86
+ - lib/louche/validators/array_validator.rb
87
+ - lib/louche/validators/email_validator.rb
88
+ - lib/louche/validators/phone_number_validator.rb
89
+ - lib/louche/validators/postal_code_validator.rb
90
+ - lib/louche/validators/url_validator.rb
54
91
  - lib/louche/version.rb
55
92
  - louche.gemspec
93
+ - spec/spec_helper.rb
94
+ - spec/validators/array_validator_spec.rb
95
+ - spec/validators/email_validator_spec.rb
96
+ - spec/validators/phone_number_validator_spec.rb
97
+ - spec/validators/postal_code_validator_spec.rb
98
+ - spec/validators/url_validator_spec.rb
56
99
  homepage: https://github.com/mirego/louche
57
100
  licenses:
58
101
  - BSD-3 Clause
@@ -77,4 +120,10 @@ rubygems_version: 2.2.2
77
120
  signing_key:
78
121
  specification_version: 4
79
122
  summary: Louche provides common validators for ActiveModel/ActiveRecord classes
80
- test_files: []
123
+ test_files:
124
+ - spec/spec_helper.rb
125
+ - spec/validators/array_validator_spec.rb
126
+ - spec/validators/email_validator_spec.rb
127
+ - spec/validators/phone_number_validator_spec.rb
128
+ - spec/validators/postal_code_validator_spec.rb
129
+ - spec/validators/url_validator_spec.rb