josephndenton
josephndenton

Reputation: 327

Getting "ActiveRecord::UnknownAttributeError: unknown attribute: email_confirmation" Error with rspec

I am running into this error when running my tests. I have checked to make sure all the email_confirmations are spelled correctly and (unless I am crazy) they are. I'm a bit of a Rails noob, so it could be something simple.

User Model

class User < ActiveRecord::Base
  attr_accessible :email, :email_confirmation, :first_name, :last_name,
                  :password, :password_confirmation
  has_secure_password

  before_save { |user| user.email = email.downcase }

  validates :first_name, presence: true, length: { maximum: 25 }
  validates :last_name, presence: true, length: { maximum: 25 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
  validates :email_confirmation, presence: true
  validates :password, presence: true, length: { maximum: 6 }
  validates :password_confirmation, presence: true
end

Rspec tests

require 'spec_helper'

describe User do
  before { @user = User.new(email: "[email protected]",
                            first_name: "John", last_name: "Smith",
                            password: "foobar", password_confirmation: "foobar",
                            email_confirmation: "[email protected]") }

  subject { @user }

  it { should respond_to(:first_name) }
  it { should respond_to(:last_name) }
  it { should respond_to(:email) }
  it { should respond_to(:email_confirmation) }
  it { should respond_to(:password_digest) }
  it { should respond_to(:password) }
  it { should respond_to(:password_confirmation) }
  it { should respond_to(:authenticate) }

  it { should be_valid }

  describe "when first name is not present" do
    before { @user.first_name = " " }
    it { should_not be_valid }
  end

  describe "when last name is not present" do
    before { @user.last_name = " " }
    it { should_not be_valid }
  end

  describe "when email is not present" do
    before { @user.email = @user.email_confirmation = " " }
    it { should_not be_valid }
  end

  describe "when password is not present" do
    before { @user.password = @user.password_confirmation = " " }
    it { should_not be_valid }
  end

  describe "when first_name is too long" do
    before { @user.first_name = "a" * 26 }
    it { should_not be_valid }
  end

  describe "when last_name is too long" do
    before { @user.last_name = "a" * 26 }
    it { should_not be_valid }
  end

  describe "when email format is invalid" do
    it "should be invalid" do
      addresses = %w[user@foo,com user_at_foo.org example.user@foo.
                             foo@bar_baz.com foo@bar+baz.com]
      addresses.each do |invalid_address|
        @user.email = invalid_address
        @user.should_not be_valid
     end      
    end
  end

  describe "when email format is valid" do
    it "should be valid" do
      addresses = %w[[email protected] [email protected] [email protected] [email protected]]
      addresses.each do |valid_address|
        @user.email = valid_address
        @user.should be_valid
      end      
    end
  end

  describe "when email address is already taken" do
    before do
      user_with_same_email = @user.dup
      user_with_same_email.email = @user.email.upcase
      user_with_same_email.save
    end

    it { should_not be_valid }
  end

  describe "when password doesn't match confirmation" do
    before { @user.password_confirmation = "mismatch" }
    it { should_not be_valid }
  end

  describe "when email doesn't match confirmation" do
    before { @user.email_confirmation = "[email protected]" }
    it { should_not be_valid }
  end

  describe "when password confirmation is nil" do
    before { @user.password_confirmation = nil }
    it { should_not be_valid }
  end

  describe "when email confirmation is nil" do
    before { @user.email_confirmation = nil }
    it { should_not be_valid }
  end

  describe "with a password that's too short" do
    before { @user.password = @user.password_confirmation = "a" * 5 }
    it { should be_invalid }
  end

  describe "return value of authenticate method" do
    before { @user.save }
    let(:found_user) { User.find_by_email(@user.email) }

    describe "with valid password" do
      it { should == found_user.authenticate(@user.password) }
    end

    describe "with invalid password" do
      let(:user_for_invalid_password) { found_user.authenticate("invalid") }

      it { should_not == user_for_invalid_password }
      specify { user_for_invalid_password.should be_false }
    end
  end
end

schema.rb

ActiveRecord::Schema.define(:version => 20130417021135) do

  create_table "users", :force => true do |t|
    t.string   "first_name"
    t.string   "last_name"
    t.string   "email"
    t.datetime "created_at",      :null => false
    t.datetime "updated_at",      :null => false
    t.string   "password_digest"
  end

  add_index "users", ["email"], :name => "index_users_on_email", :unique => true

end

Upvotes: 27

Views: 67102

Answers (6)

stevec
stevec

Reputation: 52308

I had this error plenty of times when a table didn't have the column, but I had strange cause this time. For some reason, my migrations were perfect, but (weirdly) schema.rb hadn't updated, so rake db:migrate db:seed didn't create the column. I have no idea why that happened.

TL;DR, if your migrations are up to date, check schema.rb and make sure it is too

Upvotes: 0

koios
koios

Reputation: 349

I had the same issue and this worked like magic. Add this line at the end of your migration statements for each model you would update. Resets all the cached information about columns, which will cause them to be reloaded on the next request.

    <ModelName>.reset_column_information

Reference : https://apidock.com/rails/ActiveRecord/Base/reset_column_information/class

Upvotes: 7

agarcher
agarcher

Reputation: 403

Having just spent a ton of time debugging my own instance of this, I thought I would chime in with a third possibility.

I had done a migration correctly and verified it by inspecting my ActiveRecord in the rails console. I had tried recreating my db from the schema many times and I had tried re-running the migration many times, all to no avail.

The problem, in my case, is that I was seeing the problem when running my unit tests, not at runtime. The issue is that my test database had gotten out of sync in my migration/rollback testing. The solution was quite simple. All I had to do was reset the test database with:

rake db:test:prepare

Upvotes: 30

odecio.hero
odecio.hero

Reputation: 37

I have the same message error and I fix ordering the params as same order of columns definition in database:

CONTROLLER

def create
    worktime = Worktime.create(name: params[:name], workhours: params[:workhours], organization: @organization, workdays: params[:workdays])

    render json: worktime
end

DATABASE

Table: worktimes
Columns:
id  int(11) AI PK
name    varchar(255)
workhours   text
organization_id int(11)
workdays    text

Upvotes: 0

Donato
Donato

Reputation: 2777

I understand that the answer above is marked correct and solves the OP's problem. But there is another cause for this error that goes unheeded in a number of stackoverflow posts on this topic. This error can occur in a polymorphic many to many when you forget to use the as: option to a has_many. For example:

class AProfile < ActiveRecord::Base
  has_many :profile_students
  has_many :students, through: :profile_students
end

class BProfile < ActiveRecord::Base
  has_many :profile_students
  has_many :students, through: :profile_students
end

class ProfileStudent < ActiveRecord::Base
  belongs_to :profile, polymorphic: :true
  belongs_to :student
end

class Student < ActiveRecord::Base
  has_many :profile_students
  has_many :aprofiles, through: :profile_students
  has_many :bprofiles, through: :profile_students
end

This will give you this error:

Getting “ActiveRecord::UnknownAttributeError: unknown attribute: profile_id

when you try to do the following:

a = AProfile.new
a.students << Student.new

The solution is to add the :as option to AProfile and BProfile:

class AProfile < ActiveRecord::Base
  has_many :profile_students, as: :profile
  has_many :students, through: :profile_students
end

class BProfile < ActiveRecord::Base
  has_many :profile_students, as: :profile
  has_many :students, through: :profile_students
end

Upvotes: 11

Stuart M
Stuart M

Reputation: 11588

You're getting UnknownAttributeError because you don't have a column in your users table called email_confirmation. By default, ActiveRecord will look for DB columns named the same as the attributes you use to construct the model, but this line is trying to construct a User with an attribute the database doesn't know about:

  before { @user = User.new(email: "[email protected]",
                        first_name: "John", last_name: "Smith",
                        password: "foobar", password_confirmation: "foobar",
                        email_confirmation: "[email protected]") }

Are you really intending to save the email confirmation in the database, or are you just wanting to check that it matches email before saving it? I assume the latter, and Rails actually has built-in support for doing just that:

class User < ActiveRecord::Base
  validates :email, :confirmation => true
  validates :email_confirmation, :presence => true
end

See more details on the Rails Guide to Validations, or the validates_confirmation_of API docs. (And you'll probably need to do the same thing for :password_confirmation.)

Upvotes: 31

Related Questions