Skip to content

Commit

Permalink
Add Mask feature
Browse files Browse the repository at this point in the history
  • Loading branch information
plribeiro3000 committed Apr 19, 2018
1 parent edae528 commit d5f8751
Show file tree
Hide file tree
Showing 24 changed files with 283 additions and 196 deletions.
13 changes: 13 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require: rubocop-rspec

Metrics/BlockLength:
Enabled: false

Metrics/LineLength:
Max: 125

RSpec/DescribedClass:
EnforcedStyle: explicit

Style/Documentation:
Enabled: false
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.9.3
2.2
20 changes: 7 additions & 13 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- jruby
- ree
- 2.2
- 2.3
- 2.4
- 2.5
gemfile:
- gemfiles/Gemfile.rails3
- gemfiles/Gemfile.rails4
matrix:
exclude:
- rvm: 1.8.7
gemfile: gemfiles/Gemfile.rails4
- rvm: 1.9.2
gemfile: gemfiles/Gemfile.rails4
- rvm: ree
gemfile: gemfiles/Gemfile.rails4
- gemfiles/Gemfile.rails5
before_script:
- bundle exec rubocop
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
source "http://rubygems.org"
# frozen_string_literal: true

source 'http://rubygems.org'

gemspec
File renamed without changes.
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Gem Version](https://badge.fury.io/rb/validates_cpf.png)](http://badge.fury.io/rb/validates_cpf) [![Build Status](https://secure.travis-ci.org/plribeiro3000/validates_cpf.png?branch=master)](http://travis-ci.org/plribeiro3000/validates_cpf) [![Dependency Status](https://gemnasium.com/plribeiro3000/validates_cpf.png)](https://gemnasium.com/plribeiro3000/validates_cpf) [![Coverage Status](https://coveralls.io/repos/plribeiro3000/validates_cpf/badge.png?branch=master)](https://coveralls.io/r/plribeiro3000/validates_cpf) [![Code Climate](https://codeclimate.com/github/plribeiro3000/validates_cpf.png)](https://codeclimate.com/github/plribeiro3000/validates_cpf)

Validates cpf and test it in a simple way
Validates cpf and test it in a simple way. Depends on ruby `>= 2.2`. For older ruby versions use the version `2` series.

## Installation

Expand All @@ -24,16 +24,32 @@ Just use as any other validator:

```ruby
class User < ActiveRecord::Base
validates :cpf, :cpf => true
validates :cpf, cpf: true
end
```

## Notes
To force the attribute to be masked pass option `mask`:

It will load a macher to test automatically if the gem is below shoulda-matchers.
```ruby
class User < ActiveRecord::Base
validates :cpf, cpf: { mask: true }
end
```

## Testing

Require the matcher:

## Mantainers
[@plribeiro3000](https://github.com/plribeiro3000)
```ruby
require 'validates_cpf/require_a_valid_matcher'
```

Use in your tests:

```ruby
it { is_expected.to require_a_valid_cpf } # It will test the attribute :cpf by default
it { is_expected.to require_a_valid_cpf(:id) }
```

## Contributing

Expand Down
10 changes: 6 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
require "bundler/gem_tasks"
require "rspec/core/rake_task"
# frozen_string_literal: true

require 'bundler/gem_tasks'
require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new

desc "Default Task"
task :default => [ :spec ]
desc 'Default Task'
task default: [:spec]
3 changes: 2 additions & 1 deletion gemfiles/Gemfile.rails3
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
source :rubygems

gem 'activesupport', '3.2.14'
gem 'activesupport', '< 4.0.0'
gem 'mime-types', '< 2.0'
gem 'test-unit', '~> 3.0'

gemspec :path => '../'
2 changes: 1 addition & 1 deletion gemfiles/Gemfile.rails4
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
source :rubygems

gem 'activesupport', '>= 4.0.0'
gem 'activesupport', '>= 4.0.0', '< 5.0.0'

gemspec :path => '../'
5 changes: 5 additions & 0 deletions gemfiles/Gemfile.rails5
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source :rubygems

gem 'activesupport', '>= 5.0.0'

gemspec :path => '../'
2 changes: 2 additions & 0 deletions lib/validates_cpf.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'validates_cpf/cpf_validator'
require 'validates_cpf/require_a_valid_cpf_matcher' if defined?(::Shoulda)

Expand Down
22 changes: 11 additions & 11 deletions lib/validates_cpf/cpf.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# frozen_string_literal: true

module ValidatesCpf
class Cpf
def initialize(number)
number =~ /^(\d{3}\.?\d{3}\.?\d{3})-?(\d{2})$/
@number = number
@pure_number = $1
@result = $2
@cleaned_number = @pure_number.nil? ? nil : @number.gsub(/[\.-]/, "")
@pure_number = Regexp.last_match(1)
@result = Regexp.last_match(2)
@cleaned_number = @pure_number.nil? ? nil : @number.gsub(/[\.-]/, '')
format_number! if @pure_number
end

Expand All @@ -15,31 +17,29 @@ def valid?
check_cpf
end

def number
@number
end
attr_reader :number

private

def check_cpf
return false if @cleaned_number.length != 11 or @cleaned_number.scan(/\d/).uniq.length == 1
return false if @cleaned_number.length != 11 || @cleaned_number.scan(/\d/).uniq.length == 1
@result == first_digit_verifier + second_digit_verifier
end

def first_digit_verifier
sum = multiply_and_sum([10, 9, 8, 7, 6, 5, 4, 3, 2], @pure_number)
digit_verifier(sum%11).to_s
digit_verifier(sum % 11).to_s
end

def second_digit_verifier
sum = multiply_and_sum([11, 10, 9, 8, 7, 6, 5, 4, 3, 2], @pure_number + first_digit_verifier)
digit_verifier(sum%11).to_s
digit_verifier(sum % 11).to_s
end

def multiply_and_sum(array, number)
multiplied = []
number.scan(/\d{1}/).each_with_index { |e, i| multiplied[i] = e.to_i * array[i] }
multiplied.inject { |s,e| s + e }
multiplied.inject { |s, e| s + e }
end

def digit_verifier(rest)
Expand All @@ -48,7 +48,7 @@ def digit_verifier(rest)

def format_number!
@cleaned_number =~ /(\d{3})(\d{3})(\d{3})(\d{2})/
@number = "#{$1}.#{$2}.#{$3}-#{$4}"
@number = "#{Regexp.last_match(1)}.#{Regexp.last_match(2)}.#{Regexp.last_match(3)}-#{Regexp.last_match(4)}"
end
end
end
10 changes: 9 additions & 1 deletion lib/validates_cpf/cpf_validator.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# frozen_string_literal: true

class CpfValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute, :invalid, options) unless ValidatesCpf::Cpf.new(value).valid?
cpf = ValidatesCpf::Cpf.new(value)

if cpf.valid?
record.send("#{attribute}=", cpf.number) if options[:mask]
else
record.errors.add(attribute, :invalid, options)
end
end
end
13 changes: 9 additions & 4 deletions lib/validates_cpf/require_a_valid_cpf_matcher.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require 'shoulda-matchers'
require 'active_support/core_ext/array/wrap'

module Shoulda
module Matchers
Expand All @@ -10,14 +11,18 @@ def require_a_valid_cpf(attr = :cpf)

class RequireAValidCpfMatcher < ValidationMatcher
def description
'require a valid CPF number'
'requires a valid CPF'
end

def failure_message
'does not require a valid CPF'
end

def matches?(subject)
@subject = subject
super(subject)
disallows_value_of('123456') && allows_value_of('897.546.112-20')
end
end
end
end
end
end
4 changes: 3 additions & 1 deletion lib/validates_cpf/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module ValidatesCpf
VERSION = '2.0.1'
VERSION = '2.0.1'.freeze
end
51 changes: 51 additions & 0 deletions spec/cpf_validator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe CpfValidator do
let(:user) { User.new }

context 'with invalid cpf' do
before do
user.cpf = '12345'
I18n.stub(:t).with(:'activerecord.errors.models.user.attributes.cpf.invalid',
default: :'activerecord.errors.messages.invalid').and_return('is invalid')
user.valid?
end

it 'invalidates the object' do
expect(user).not_to be_valid
end

it 'sets an error message' do
expect(user.errors[:cpf]).to match(['is invalid'])
end
end

context 'with valid cpf' do
before do
user.cpf = '11144477735'
user.valid?
end

it 'validates the object' do
expect(user).to be_valid
end

it 'does not set an error message' do
expect(user.errors[:cpf]).to be_blank
end
end

context 'with nil value' do
before { user.cpf = nil }

it 'validates the object' do
expect(user).to be_valid
end

it 'does not set an error message' do
expect(user.errors[:cpf]).to be_blank
end
end
end
3 changes: 0 additions & 3 deletions spec/fake_app/admin.rb

This file was deleted.

19 changes: 7 additions & 12 deletions spec/fake_app/user.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
class User
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
# frozen_string_literal: true

attr_accessor :cpf, :name
class User
include ActiveModel::Model

validates :cpf, :cpf => true
attr_accessor :cpf, :masked_cpf, :name

def initialize(attributes = {})
attributes.each do |key, value|
instance_variable_set("@#{key}", value)
end
end
end
validates :cpf, cpf: true
validates :masked_cpf, cpf: { mask: true }
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Shoulda::Matchers::ActiveModel::RequireAValidCpfMatcher do
subject { User.new }

it { is_expected.to require_a_valid_cpf(:cpf) }
it { is_expected.to require_a_valid_cpf }
it { is_expected.not_to require_a_valid_cpf(:name) }
end
21 changes: 19 additions & 2 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
# frozen_string_literal: true

require 'rspec'
require 'active_model'
require 'coveralls'
require 'jazz_fingers'
require 'shoulda-matchers'

JazzFingers.configure do |config|
config.colored_prompt = false
config.awesome_print = false
config.coolline = false
config.application_name = ValidatesCpf
end

require 'jazz_fingers/setup'

RSpec.configure do |config|
config.include Shoulda::Matchers::ActiveModel
end

Coveralls.wear!

Dir.glob(File.dirname(__FILE__) + '/../lib/**/*.rb').each { |file| require file }
Dir.glob(File.dirname(__FILE__) + '/fake_app/*.rb').each { |file| require file }
require File.expand_path('lib/validates_cpf')
require File.expand_path('spec/fake_app/user')

0 comments on commit d5f8751

Please sign in to comment.