Scro
Scro

Reputation: 1443

Rails 4 model custom validation with method - works in console but fails in spec

Attempting to write a custom model validation and having some trouble. I'm using a regular expression to confirm that a decimal amount is validated to be in the following format:

The regular expression has been tested on Rubular - I'm very confident with it. I've also defined the method in the ruby console, and it appears to be working fine there. I've followed the general instructions on the Rails Guides for Active Record Validations but I'm still getting validation issues that have me headscratching.

Here is the model validation for the column :bar -

class Foo < ActiveRecord::Base
  validate :bar_format

  def bar_format
    unless :bar =~ /^([0-4]{1}\.{1}\d{2})$/  || :bar == nil
      errors.add(:bar, "incorrect format")
    end
  end
end

The spec for Foo:

require 'rails_helper'

describe Foo, type: :model do
  let(:foo) { build(:foo) }

  it "has a valid factory" do
    expect(foo).to be_valid
  end

  describe "bar" do
    it "can be nil" do
      foo = create(:foo, bar: nil)
      expect(foo).to be_valid
    end

    it "accepts a decimal value with precision 3 and scale 2" do
      foo = create(:foo, bar: "3.50")
      expect(foo).to be_valid
    end

    it "does not accept a decimal value with precision 4 and scale 3" do
      expect(create(:foo, bar: "3.501")).not_to be_valid
    end
  end
end

All of these specs fail for validation on bar:

ActiveRecord::RecordInvalid:
       Validation failed: bar incorrect format

In the ruby console I've copied the method bar_format as follows:

irb(main):074:0> def bar_format(bar)
irb(main):075:1>  unless bar =~ /^([0-4]{1}\.{1}\d{2})$/ || bar == nil
irb(main):076:2>   puts "incorrect format"
irb(main):077:2>  end
irb(main):078:1> end
=> :bar_format
irb(main):079:0> bar_format("3.50")
=> nil
irb(main):080:0> bar_format("0.0")
incorrect format
=> nil
irb(main):081:0> bar_format("3.5")
incorrect format
=> nil
irb(main):082:0> bar_format("3.1234")
incorrect format
=> nil
irb(main):083:0> bar_format("3.00")
=> nil

The method returns nil for a correctly formatted entry, and it returns the error message for an incorrectly formatted entry.

Suspecting this has something to do with the validation logic, but as far as I can understand, validations look at the errors hash to determine if the item is valid or not. The logical structure of my validation matches the structure in the example on the Rails Guides, for custom methods.

* EDIT * Thanks Lazarus for suggesting that I remove the colon from the :bar so it's not a symbol in the method. After doing that, most of the tests pass. However: I'm getting a weird failure on two tests that I can't understand. The code for the tests:

   it "does not accept a decimal value with precision 4 and scale 3" do
      expect(create(:foo, bar: "3.501")).not_to be_valid
    end

    it "generates an error message for an incorrect decimal value" do
      foo = create(:foo, bar: "4.506")
      expect(scholarship.errors.count).to eq 1
    end

After turning the symbol :bar into a variable bar the other tests pass, but for these two I get:

Failure/Error: expect(create(:foo, bar: "3.501")).not_to be_valid
     ActiveRecord::RecordInvalid:
       Validation failed: 3 501 incorrect format

 Failure/Error: foo = create(:bar, min_gpa: "4.506")
     ActiveRecord::RecordInvalid:
       Validation failed: 4 506 incorrect format

Any ideas why it's turning the input "3.501" to 3 501 and "4.506" to 4 506?

Upvotes: 1

Views: 809

Answers (2)

Rafael Belo
Rafael Belo

Reputation: 36

Don't use symbol to refer an argument.

class Foo < ActiveRecord::Base
  validate :bar_format

  def bar_format
    unless bar =~ /^([0-4]{1}\.{1}\d{2})$/  || bar == nil
      errors.add(:bar, "incorrect format")
    end
  end
end

But if you want an regex for decimal like '1.0', '1.11', '1111.00' I advise you to use this regex:

/^\d+(\.\d{1,2})?$/

If you can to use regex for money, here is:

/^(\d{1,3}\,){0,}(\d{1,3}\.\d{1,2})$/

Good luck ^^

Upvotes: 0

Lazarus Lazaridis
Lazarus Lazaridis

Reputation: 6029

You use the symbol instead of the variable when checking against the regex or for nil.

unless :bar =~ /^([0-4]{1}\.{1}\d{2})$/  || :bar == nil
  errors.add(:bar, "incorrect format")
end

Remove the : from the :bar

* EDIT *

It's not the specs that are failing but the model's validations upon creation. You should use build instead of create

Upvotes: 2

Related Questions