2

How can define custom validator that permits first name or last name to be null but not both

My Profile class:

class Profile < ActiveRecord::Base
     belongs_to :user

     validates :first_name, allow_nil: true
     validates :last_name, allow_nil: true

     validate :first_xor_last


    def first_xor_last
     if (first_name.nil? and last_name.nil?)
        errors[:base] << ("Specify a first or a last.")
      end
    end

I tries create by self first_xor_last function but does not work.

I recieve this rspec test:

context "rq11" do
       context "Validators:" do
         it "does not allow a User without a username" do
           expect(User.new(:username=> "")).to_not be_valid
         end
         it "does not allow a Profile with a null first and last name" do
           profile = Profile.new(:first_name=>nil, :last_name=>nil, :gender=>"male")
           expect(Profile.new(:first_name=>nil, :last_name=>nil, :gender=>"male")).to_not be_valid
         end
         it "does not allow a Profile with a gender other than male or female " do
           expect(Profile.new(:first_name=>"first", :last_name=>"last", :gender=>"neutral")).to_not be_valid
         end
         it "does not allow a boy named Sue" do
           expect(Profile.new(:first_name=>"Sue", :last_name=>"last", :gender=>"male")).to_not be_valid
         end
       end
     end

I should pass it.

Thanks, Michael.

5
  • Unrelated, but I read your method name as meaning you can have a first name or last name but not both. Commented Nov 1, 2015 at 11:59
  • ok, thanks I will fix it. Commented Nov 1, 2015 at 12:03
  • First thing. The allow_nil: true is going against those tests. You should no be allowing them nil. Secondly, You can use the rails presence: true instead. Commented Nov 1, 2015 at 12:04
  • lcguida, Thanks, it's work!!!! Commented Nov 1, 2015 at 12:07
  • If you solved your problem, choose an answer as the correct one =) Commented Nov 1, 2015 at 18:36

3 Answers 3

1

First of all, allow_nill is not a valid validator. You should be using presence or absence.

Unless you really need a custom message for both fields at the same time, there's no need to use a custom validator, simply do like this:

class Profile < ActiveRecord::Base
  belongs_to :user

  validates :first_name, :last_name, presence: true

end

If you want to allow either one, you can use conditional validation:

class Profile < ActiveRecord::Base
  belongs_to :user

  validates :first_name, presence: true, unless: "last_name.present?"
  validates :last_name, presence: true, unless: "first_name.present?"

end   
Sign up to request clarification or add additional context in comments.

4 Comments

How does this handle the either condition? To my understanding, if first_name or last_name are missing, an error will show. The specs here stipulate that first_name or last_name can be missing so long as at least one is present.
it "does not allow a Profile with a null first and last name". From where are you taking that the specs are telling that either one can be missing?
"that permits first name or last name to be null but not both" - so you can have first_name + no last name, or last_name + no first name. This answer means you have to have both, which does not solve the problem.
Ok, agreed. That is wirtten. But also is written that he wants to make the supplied spec to pass, and those specs says otherwise.
0

Instead of:

errors[:base] << ("Specify a first or a last.")

do

errors.add(:base, "Specify a first or a last.")

EDIT:

The error message you get is not caused by your custom validation, but by two other validations, which seems not needed, just get rid of those two lines:

 validates :first_name, allow_nil: true
 validates :last_name, allow_nil: true

3 Comments

Any feedback? Errors? Messages?
Profile.all ArgumentError: You need to supply at least one validation from /home/mic3ael/.rvm/gems/ruby-2.2.2/gems/activemodel-4.2.3/lib/active_model/validations/validates.rb:109:in validates' from /home/mic3ael/Study/ruby/todolists/app/models/profile.rb:4:in <class:Profile>' from /home/mic3ael/Study/ruby/todolists/app/models/profile.rb:1:in `<top (required)>' Maybe this help
@MichaelHorojanski - Updated.
0
class Profile < ActiveRecord::Base
  belongs_to :user
  validate :presence_of_first_or_last_name

  def presence_of_first_or_last_name
    if (first_name.blank? and last_name.blank?)
      errors[:base] << ("Specify a first or a last.")
    end
  end
end

You can lose the two validates :first_name, allow_nil: true validations since they do absolutely nothing.

Instead of .nil? you might want to use the ActiveSupport method .blank? which checks not just for nil but also if the value is an empty string "" or consists of only whitespaces.

Also as David Newton pointed out XOR is eXclusive OR which would be first_name or last_name but not both. I try to name validators according to the general scheme of ActiveModel::Validations - a descriptive name which tells what kind of validation is performed.

1 Comment

"also if the value is an empty string" or consists of whitespace only.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.