Skip to content

Commit

Permalink
Merge pull request #55 from joecorcoran/confirmation-validation
Browse files Browse the repository at this point in the history
Move confirmation validation to confirmation field
  • Loading branch information
jamesmk committed May 12, 2015
2 parents accbe6d + f4b6010 commit c3e079e
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 20 deletions.
2 changes: 1 addition & 1 deletion app/assets/javascripts/judge.js
Expand Up @@ -350,7 +350,7 @@
// ActiveModel::Validations::ConfirmationValidator
confirmation: function(options, messages) {
var id = this.getAttribute('id'),
confId = id + '_confirmation',
confId = id.replace('_confirmation', ''),
confElem = root.document.getElementById(confId);
return closed(
this.value === confElem.value ? [] : [messages.confirmation]
Expand Down
3 changes: 2 additions & 1 deletion lib/judge.rb
Expand Up @@ -9,6 +9,7 @@
'form_builder',
'each_validator',
'validation',
'controller'
'controller',
'confirmation_validator'
]
files.each { |filename| require "judge/#{filename}" }
35 changes: 35 additions & 0 deletions lib/judge/confirmation_validator.rb
@@ -0,0 +1,35 @@
module Judge
class ConfirmationValidator

include Judge::EachValidator

attr_reader :object, :method, :amv

def initialize(object, method)
@object = object
@method = method
@amv = amv_from_original
end

def kind
@amv.kind if @amv.present?
end

def options
@amv.options if @amv.present?
end

private

def amv_from_original
original_amv = nil
original_method = method.to_s.gsub('_confirmation', '').to_sym
object.class.validators_on(original_method).each do |v|
original_amv = v if v.class.name['ConfirmationValidator']
end

original_amv
end

end
end
36 changes: 27 additions & 9 deletions lib/judge/validator_collection.rb
Expand Up @@ -4,12 +4,11 @@ class ValidatorCollection

include Enumerable

attr_reader :validators
attr_reader :validators, :object, :method

def initialize(object, method)
amvs = object.class.validators_on(method)
amvs = amvs.reject { |amv| reject?(amv) }
amvs = amvs.reject { |amv| unsupported_options?(amv) && reject?(amv) != false } if Judge.config.ignore_unsupported_validators?
@object = object
@method = method
@validators = amvs.map { |amv| Judge::Validator.new(object, method, amv) }
end

Expand All @@ -22,11 +21,26 @@ def each(&block)
def to_json
validators.map { |v| v.to_hash }.to_json
end

protected

UNSUPPORTED_OPTIONS = [:if, :on, :unless, :tokenizer, :scope, :case_sensitive]

# returns an array of ActiveModel::Validations
# starts with all Validations attached to method and removes one that are:
# ignored based on a config
# ConfirmationValidators, which are moved directly to the confirmation method
# unsupported by Judge
# if it's a confirmation field, an AM::V like class is added to handle the confirmation validations
def amvs
amvs = object.class.validators_on(method)
amvs = amvs.reject { |amv| reject?(amv) || amv.class.name['ConfirmationValidator'] }
amvs = amvs.reject { |amv| unsupported_options?(amv) && reject?(amv) != false } if Judge.config.ignore_unsupported_validators?
amvs << Judge::ConfirmationValidator.new(object, method) if is_confirmation?

amvs
end

def unsupported_options?(amv)
unsupported = !(amv.options.keys & UNSUPPORTED_OPTIONS).empty?
return false unless unsupported
Expand All @@ -36,15 +50,19 @@ def unsupported_options?(amv)
unsupported = amv.options.keys & UNSUPPORTED_OPTIONS
unsupported.length > 1 || unsupported != [:case_sensitive] || amv.options[:case_sensitive] == false
end

# decides whether to reject a validation based on the presence of the judge option.
# return values:
# true when :judge => :ignore is present in the options
# false when :judge => :force is present
# nil otherwise (e.g. when no :judge option or an unknown option is present)
def reject?(amv)
return unless [:force, :ignore].include?( amv.options[:judge] )
amv.options[:judge] == :ignore ? true : false
amv.options[:judge] == :ignore
end

def is_confirmation?
method.to_s['_confirmation']
end

end
Expand Down
34 changes: 34 additions & 0 deletions spec/confirmation_validator_spec.rb
@@ -0,0 +1,34 @@
require "spec_helper"

describe Judge::ConfirmationValidator do

let :password_confirmation do
user = FactoryGirl.build(:user)
Judge::ConfirmationValidator.new(user, :password_confirmation)
end

describe '#amv' do
it "should return a ConfirmationValidator" do
expect(password_confirmation.amv).to be_a(ActiveModel::Validations::ConfirmationValidator)
end

it "should be the ConfirmationValidator from :password" do
expect(password_confirmation.amv.attributes).to include(:password)
end
end

describe '#kind' do
it "should return the the original amv's kind (:confiramtion)" do
expect(password_confirmation.kind).to eq(:confirmation)
expect(password_confirmation.kind).to eq(password_confirmation.amv.kind)
end
end

describe '#options' do
it "should return the original amv's options (an empty hash)" do
expect(password_confirmation.options).to eq({})
expect(password_confirmation.options).to eq(password_confirmation.amv.options)
end
end

end
6 changes: 3 additions & 3 deletions spec/javascripts/judge-spec.js
Expand Up @@ -421,11 +421,11 @@ describe('judge', function() {
describe('confirmation', function() {
var confEl;
beforeEach(function() {
validator = _.bind(judge.eachValidators.confirmation, el);
el.id = 'pw';
el.id = 'pw_confirmation';
confEl = document.createElement('input');
confEl.id = 'pw_confirmation';
confEl.id = 'pw';
document.body.appendChild(confEl);
validator = _.bind(judge.eachValidators.confirmation, el);
});
afterEach(function() {
document.body.removeChild(confEl);
Expand Down
22 changes: 16 additions & 6 deletions spec/validator_collection_spec.rb
@@ -1,7 +1,7 @@
require "spec_helper"

describe Judge::ValidatorCollection do

let(:vc) { Judge::ValidatorCollection.new(FactoryGirl.build(:user), :name) }

it "contains validators" do
Expand All @@ -17,7 +17,7 @@
vc.should be_an Enumerable
vc.should respond_to :each
end

it "respects the global ignore_unsupported_validators configuration option" do
vc.validators.length.should eq 2
Judge::ValidatorCollection.new(FactoryGirl.build(:user), :country).validators.length.should eq 2
Expand All @@ -32,12 +32,22 @@

it "respects the per-validator judge configuration option" do
Judge::ValidatorCollection.new(FactoryGirl.build(:user), :team_id).validators.length.should eq 1
Judge::ValidatorCollection.new(FactoryGirl.build(:user), :discipline_id).validators.length.should eq 2
Judge::ValidatorCollection.new(FactoryGirl.build(:user), :time_zone).validators.length.should eq 1
Judge::ValidatorCollection.new(FactoryGirl.build(:user), :discipline_id).validators.length.should eq 2
Judge::ValidatorCollection.new(FactoryGirl.build(:user), :time_zone).validators.length.should eq 1
end

it "ignores unknown per-validator judge configuration options" do
Judge::ValidatorCollection.new(FactoryGirl.build(:user), :gender).validators.length.should eq 2
end


it "should remove confirmation validation from password" do
Judge::ValidatorCollection.new(FactoryGirl.build(:user), :password).validators.each do |validator|
validator.kind.should_not eq :confirmation
end
end

it "should add confirmation validation to password_confirmation" do
Judge::ValidatorCollection.new(FactoryGirl.create(:user), :password_confirmation).validators.length.should eq 1
end

end

0 comments on commit c3e079e

Please sign in to comment.