0
votes

In my Rails application I have this update action:

class UsersController < ApplicationController

  before_filter :authorized_user

  def update
    current_email = @user.email
    new_email = params[:user][:email].downcase.to_s
    if @user.update_attributes(params[:user])    
      if new_email != current_email
        @user.change_email(current_email, new_email)     
        flash[:success] = "Please click on the link that we've sent you."
      else
        flash[:success] = "User updated."
      end
      redirect_to edit_user_path(@user)
    else
      render :edit
    end
  end

  private

  def authorized_user
    @user = User.find(params[:id])
    redirect_to(root_path) unless current_user?(@user)
  end

end

class User < ActiveRecord::Base

  def change_email(old_email, new_email)
    self.email = old_email
    self.new_email = new_email.downcase    
    self.send_email_confirmation_link  
  end

end

Now, everything works perfectly when I test the update action manually in the browser.

The RSpec test that I wrote, however, does not work and I can't figure out why:

it "changes @user's new_email" do
  @user = create(:user, email: "[email protected]")
  put :update, id: @user, user: attributes_for(:user, email: "[email protected]")
  @user.reload
  expect(@user.new_email).to eq("[email protected]")
end

I keep getting the same error message:

1) UsersController user access PUT #update with valid attributes changes @user's new_email Failure/Error: expect(@user.new_email).to eq("[email protected]")

   expected: "[email protected]"
        got: nil

   (compared using ==)

Can anybody tell me what I am missing here?

Thanks for any help!

2
which are your user fields in DB?sites
email, new_email and othersTintin81

2 Answers

0
votes

There are some problems in your controller.

Firstly, you have not defined @user at all. That's the reason your result is nil

# Define it
@user = User.find(params[:id]) # if your url is POST /user/1/update

Secondly, better not to use email only, what if in the further you want to allow users to edit other attributes say birthday etc?

Thirdly, updating user attributes is a bit tricky than others, the user must have authority to do that. Considering one user manipulate the form attributes in browser and post to another user's update, what will happen?

So you must check if the user can do that.

Combining together:

def update
  @user = User.find(params[:id])
  if @user != current_user
    return redirect_to(:back, alert: "not allowed to edit")
  end

  attrs = params[:user]

  if @user.update_attributes attrs
     //do something
  else
     render { action 'edit' }
  end
end
0
votes

I think you have a typo

expect(@user.email).to eq("[email protected]")

You are not saving user in change_email, and you are updating email in your controller. So new_email is not updated in your code.

To save your record use

def change_email(old_email, new_email)
  self.email = old_email
  self.new_email = new_email.downcase 
  save
  self.send_email_confirmation_link  
end

Callback

Could be something like this:

# controller
if @user.update_attributes[:user]
  redirect_to somewhere
else
  render :edit

# model
after_update :change_email

But there are more changes than these, this is just a schema.

anyway, maybe you should use a callback in your model instead of writing more code in your controller.