Bazley
Bazley

Reputation: 2847

Setting attribute during initialization

The Model is:

class Persona < ActiveRecord::Base
  before_save :make_downcase_name
  after_initialize :make_downcase_name
  validates :name, presence: true, length: { maximum: 50 }
  validates :downcase_name, uniqueness: { case_sensitive: false }

  private

    def make_downcase_name
      if name != nil
        self.downcase_name ||= name.downcase
      end
    end
end

This test fails:

before do
  @persona = Persona.new(name: "Bazman")
end

subject { @persona }

describe "name with mixed case" do
  let(:mixed_case_name) { "TeRRy" }
  it "should be saved as all lower-case" do
    @persona.name = mixed_case_name
    @persona.save
    expect(@persona.reload.downcase_name).to eq mixed_case_name.downcase
  end
end

with the error message:

expected: "terry"

got: "bazman"

So that means in the test when @persona.save is called, the before_save callback is not changing TeRRy's downcase_name to terry, so the line

self.downcase_name ||= name.downcase

is failing, but I don't understand why.

Upvotes: 0

Views: 34

Answers (1)

fivedigit
fivedigit

Reputation: 18682

downcase_name is not updated to the lowercase version of the new name because of the way make_downcase_name works.

self.downcase_name ||= name.downcase

This line can be explained as: if downcase_name is falsey (nil or false), then set it to name.downcase. Otherwise, leave it alone.

There are two options here. Either your spec is right and downcase_name should be updated when a new name is assigned, or your model is right and downcase_name should never change once it has first been assigned.

Option 1: The spec is right

In this case, you need to fix your model:

def make_downcase_name
  return unless name

  self.downcase_name = name.downcase
end

That's it!

Option 2: The model is right

If this is true, you need to make sure you're creating a new Persona in your spec. First, let's do a small refactoring of your spec to declare variables using let rather than using an instance variable:

let(:persona) { Persona.new(name: 'Bazman' }
subject       { persona }

describe "name with mixed case" do
  let(:mixed_case_name) { "TeRRy" }

  it "should be saved as all lower-case" do
    persona.name = mixed_case_name
    persona.save
    expect(persona.reload.downcase_name).to eq mixed_case_name.downcase
  end
end

For the name with mixed case scenario, you can now easily create a new Persona instance:

let(:persona) { Persona.new(name: 'Bazman') }
subject       { persona }

describe "name with mixed case" do
  let(:mixed_case_name) { "TeRRy" }
  let(:persona)         { Persona.new }

  it "should be saved as all lower-case" do
    persona.name = mixed_case_name
    persona.save
    expect(persona.downcase_name).to eq mixed_case_name.downcase
  end
end

Upvotes: 1

Related Questions