user1086832
user1086832

Reputation: 11

RoR tutorial, chapter 7 - rspec tests failing: NoMethodError 'encrypt'

Well I've searched all over the place and it seems that nobody else has the issue with this 'encrypt' method causing their tests to fail, though it seems plenty others have had some difficulty with chapter 7. Without further adieu,

Here's the link to Hartl's Chapter 7

My code in the user model file, and the corresponding spec file, appear to be completely exact to what he has written and I still cannot get the tests to pass. The errors?

Failures:
1) User should create a new instance given valid attributes
Failure/Error: User.Create!(@attr)
NoMethodError: undefined method 'encrypt' for #<User:asdf>
#./app/models/user.rb:22:in 'has_password?'
#./app/models/user.rb:28:in 'encrypt_password'
#./spec/models/user_spec.rb:15:in 'block (2 levels) in <top (required)>'

2) User should not allow duplicate email addresses
Failure/Error: User.Create!(@attr)
NoMethodError: undefined method 'encrypt' for #<User:asdf>
#./app/models/user.rb:22:in 'has_password?'
#./app/models/user.rb:28:in 'encrypt_password'
#./spec/models/user_spec.rb:15:in 'block (2 levels) in <top (required)>'

3) User should reject email addresses identical up to case
Failure/Error: User.Create!(@attr)
NoMethodError: undefined method 'encrypt' for #<User:asdf>
#./app/models/user.rb:22:in 'has_password?'
#./app/models/user.rb:28:in 'encrypt_password'
#./spec/models/user_spec.rb:15:in 'block (2 levels) in <top (required)>'

...

7) User has_password? method should be false if passwords do not match
Failure/Error: User.Create!(@attr)
NoMethodError: undefined method 'encrypt' for #<User:asdf>
#./app/models/user.rb:22:in 'has_password?'
#./app/models/user.rb:28:in 'encrypt_password'
#./spec/models/user_spec.rb:15:in 'block (3 levels) in <top (required)>'

so i'm getting the same error messages for each test and I am going nuts trying to find out why!

Here's my user.rb:

require 'digest'
class User < ActiveRecord::Base
  attr_accessor :password
  attr_accessible :name, :email, :password, :password_confirmation

  email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

  validates :name,  :presence => true,
                    :length   => { :maximum => 50 }
  validates :email, :presence => true,
                    :format   => { :with => email_regex },
                    :uniqueness => { :case_sensitive => false }
#automatically create the virtual attribute for 'password_confirmation'
  validates :password, :presence => true,
                       :confirmation => true,
                       :length => { :within => 6..40 }

  before_save :encrypt_password

  #returns true if the users password matches the submitted one
  def has_password?(submitted_password)
  encrypted_password == encrypt(submitted_password)
  end

  private

  def encrypt_password
    self.salt = make_salt unless has_password?(password)
    self.encrypted_password = encrypt(password)
  end

  def encrypt_string
    secure_hash("#{salt}--#{string}")
  end

  def make_salt
    secure_hash("#{Time.now.utc}--#{password}")
  end

  def secure_hash(string)
    Digest::SHA2.hexdigest(string)
  end
end

and my user_spec.rb file:

require 'spec_helper'
require 'digest'

describe User do
  before(:each) do
@attr = {
  :name => "User Name",
  :email => "[email protected]",
  :password => "password",
  :password_confirmation => "password"
}
  end

  it "should create a new instance given valid attributes" do
User.create!(@attr)
  end

  it "should require a name" do
no_name_user = User.new(@attr.merge(:name => ""))
no_name_user.should_not be_valid
  end

  it "should require an email" do
no_email_user = User.new(@attr.merge(:email => ""))
no_email_user.should_not be_valid
  end

  it "should reject names that are too long" do
long_name = "a" * 51
long_name_user = User.new(@attr.merge(:name => long_name))
long_name_user.should_not be_valid
  end

  it "should accept valid email addresses" do
addresses = %w[[email protected] [email protected] [email protected]]
addresses.each do |address|
  valid_email_user = User.new(@attr.merge(:email => address))
  valid_email_user.should be_valid
end
  end

  it "should reject invalid email addresses" do
addresses = %w[user@foo,com user_at_foo.org example.user@foo.]
    addresses.each do |address|
  invalid_email_user = User.new(@attr.merge(:email => address))
  invalid_email_user.should_not be_valid
    end
  end

  it "should not allow duplicate email addresses" do 
User.create!(@attr)
user_with_duplicate_email = User.new(@attr)
user_with_duplicate_email.should_not be_valid
  end

  it "should reject email addresses identical up to case" do
upcased_email = @attr[:email].upcase
User.create!(@attr.merge(:email => upcased_email))
user_with_duplicate_email = User.new(@attr)
user_with_duplicate_email.should_not be_valid
  end

  describe "password validations" do
it "should require a password" do
  User.new(@attr.merge(:password => "", :password_confirmation => ""))
  should_not be_valid
end

it "should require password to match the password confirmation" do
  User.new(@attr.merge(:password_confirmation => "invalid"))
  should_not be_valid
end

it "should reject short passwords" do
  short = "a" * 5
  hash = @attr.merge(:password => short, :password_confirmation => short)
  User.new(hash).should_not be_valid
end

it "should reject long passwords" do
  long = "a" * 41
  hash = @attr.merge(:password => long, :password_confirmation => long)
  User.new(hash).should_not be_valid
    end
  end

  describe "password encryption" do

before(:each) do
  @user = User.create!(@attr)
end

it "should have an encrypted password attribute" do
  @user.should respond_to(:encrypted_password)
end

it "should not allow a blank encrypted password" do
  @user.encrypted_password.should_not be_blank
    end
  end

  describe "has_password? method" do

    before(:each) do
  @attr = User.create!(@attr)
    end

  it "should be true if the passwords match" do
    @user.has_password?(@attr[:password]).should be_true
  end

  it "should be false if the passwords don't match" do
        @user.has_password?("invalid").should be_false
      end 
  end
end

Any help would be greatly appreciated. I've poured over other's problems, my code, and changed various aspects to try and get the tests to work, all to no avail. I hope it's not something really stupid I'm still not seeing.

Upvotes: 1

Views: 1112

Answers (1)

iwasrobbed
iwasrobbed

Reputation: 46703

Your error is here:

  def encrypt_string
    secure_hash("#{salt}--#{string}")
  end

You are calling encrypt in the following encrypt_password method but your method above is named encrypt_string:

  def encrypt_password
    self.salt = make_salt unless has_password?(password)
    self.encrypted_password = encrypt(password)
  end

Just change encrypt_string to encrypt in the method definition and you should be good to go.

Upvotes: 3

Related Questions