Statement invalid when attempting to implement password_reset Railscast - ruby-on-rails-3

I'm attempting to implement Railscast #274 in my app so I can provide a password reset option. I got to the point where I can type my email into a form for password resets and receive an email with a link to reset the password. Things start to go wrong when I enter my new password and attempt to save it. I ended up with an Action Controller:Exception caught. Here is what my log showed after I sent myself an email with the password_reset link:
Started GET "/password_resets/new" for 127.0.0.1 at 2012-01-26 00:50:42 -0500
Processing by PasswordResetsController#new as HTML
Clicking the password_reset link:
Started GET "/password_resets/qlslPnuOhdyMCNseMnV3bA/edit" for 127.0.0.1 at 2012-01-26 00:51:08 -0500
Processing by PasswordResetsController#edit as HTML
Parameters: {"id"=>"qlslPnuOhdyMCNseMnV3bA"}
Adding a new :password and :password_confirmation yields the error:
Started POST "/password_resets/qlslPnuOhdyMCNseMnV3bA" for 127.0.0.1 at 2012-01-26 00:53:08 -0500
Processing by PasswordResetsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2egfT2lr35FhuVPWDB72vcS2zPlqC75tcyctRp61ZHw=", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "commit"=>"Update Password", "id"=>"qlslPnuOhdyMCNseMnV3bA"}
Started GET "/profiles/qlslPnuOhdyMCNseMnV3bA" for 127.0.0.1 at 2012-01-26 00:53:09 -0500
Processing by ProfilesController#show as HTML
Parameters: {"id"=>"qlslPnuOhdyMCNseMnV3bA"}
Profile Load (0.9ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" = 'qlslPnuOhdyMCNseMnV3bA' LIMIT 1
PGError: ERROR: invalid input syntax for integer: "qlslPnuOhdyMCNseMnV3bA"
LINE 1: ...ofiles".* FROM "profiles" WHERE "profiles"."id" = 'qlslPnuOh...
^
: SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" = 'qlslPnuOhdyMCNseMnV3bA' LIMIT 1
Completed in 57ms
ActiveRecord::StatementInvalid (PGError: ERROR: invalid input syntax for integer: "qlslPnuOhdyMCNseMnV3bA"
LINE 1: ...ofiles".* FROM "profiles" WHERE "profiles"."id" = 'qlslPnuOh...
^
: SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" = 'qlslPnuOhdyMCNseMnV3bA' LIMIT 1):
app/controllers/profiles_controller.rb:41:in `show'
In profiles_controller.rb:41 in show:
def show
#profile = Profile.find(params[:id])
#user = User.find(#profile.user_id)
..
end
Before doing this I dumped my database, ran rake:db create, then rake:db migrate before re-seeded the db. Could this be because I haven't run a script to give existing users a password_reset_token?
UPDATE: Including password_resets_controller.rb:
class PasswordResetsController < ApplicationController
def new
end
def create
#user = #User.find_by_email(params[:email])
if user
user.send_password_reset
redirect_to new_password_reset_path, :notice => "Check your email for password reset instructions."
else
redirect_to new_password_reset_path, :notice => "Sorry, we couldn't find that email. Please try again."
end
end
def edit
#user = User.find_by_password_reset_token!(params[:id])
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Your password reset link has expired."
elsif #user.update_attributes(params[:user])
redirect_to profile_path, :notice => "Great news: Your password has been reset."
else
render :edit
end
end
end

Looks like the problem is in your PasswordResetsController#update:
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Your password reset link has expired."
elsif #user.update_attributes(params[:user])
redirect_to profile_path, :notice => "Great news: Your password has been reset."
else
render :edit
end
end
The redirect_to profile_path in particular.
If you look at the logs, you'll see this sequence:
POST "/password_resets/qlslPnuOhdyMCNseMnV3bA"
GET "/profiles/qlslPnuOhdyMCNseMnV3bA"
and the routes should be /password_resets/:id and /profiles/:id. The /password_resets wants the 'qlslPnuOhdyMCNseMnV3bA' token but /profiles wants the user's numeric ID.
Going back to the controller we see this:
redirect_to profile_path, :notice => "Great news: Your password has been reset."
You don't tell profile_path which user to use so apparently it is grabbing params[:id] to build the incorrect /profiles/qlslPnuOhdyMCNseMnV3bA URL. Try telling profile_path which user to use with something like this:
redirect_to profile_path(#user), :notice => "Great news: Your password has been reset."

Related

Send email to other user not logged in with action mailer for rails

I am trying to send an email to another user that I am not logged in as and is on another Devise model called 'user2', they use the site differently than 'user'. When I create a new message I am able to send the current_user an email by doing:
UserMailer.new_message_sent_user(current_user).deliver
Then in UserMailer:
def new_message_sent_user(user)
#user = user
mail to: #user.email, subject: "New message sent to user2!"
end
However I also need to send an email to 'user2' but am having a hard time finding them by their id and passing their email in to action mailer. I do know I have a value in user2_id because it is saving successfully on the message create. In the new action I have a find:
#user2 = User2.find(:first,:conditions=>["id = ?", #post.user2_id])
Then in create I have
UserMailer.new_message_sent_user2(#user2).deliver
Then in UserMailer I have:
#send to advisor when he has sent a new message to an advisor
def new_message_sent_user2(user2)
#user2 = user2
mail to: #user2.email, subject: "You have received a message from user"
end
But I am getting an error, I think saying it has a nil email, so how can I find and set the user2 object to send the email?:
undefined method `email' for nil:NilClass
Any help would be greatly appreciated!
Update with the create action:
# POST /messages
# POST /messages.json
def create
#message = current_user.messages.build(params[:message])
respond_to do |format|
if #message.save
UserMailer.new_message_sent_user(current_user).deliver
UserMailer.new_message_sent_user2(#user2)
format.html { redirect_to users_path, notice: 'Your message has been sent!' }
format.json { render json: #message, status: :created, location: #message }
else
format.html { render action: "new" }
format.json { render json: #message.errors, status: :unprocessable_entity }
end
end
end
To answer your question based on the create action you are not creating the #user2 instance for that action. An instance variable does not carry from the new => create action, or edit => update. So all you need to add is
#user2 = User2.find(params[:user2_id]) #assuming you have the user2_id as part of the submitted form.
I don't know exactly where you are storing the :user2_id in relation to the user, but if it is a has_many/belongs to association it might be easier right after #user is set to set the user2 as well
#user2 = #user.user2 #this will return an array of associated user_2's
the final action might look like:
def create
#message = current_user.messages.build(params[:message])
#user2 = User2.find(params[:user2_id]) #or whatever selector
respond_to do |format|
if #message.save
UserMailer.new_message_sent_user(current_user).deliver
...
end
check #user2, it seems to be nil.

Rails 3 error in Safari only - ActiveRecord::RecordNotFound (Couldn't find User with auth_token = ):

In following Railscast #274 to get reset password working in my Rails 3 app, I am experiencing a weird issue in Safari. If I run my app in Heroku I get the following error when I go to my root:
ActiveRecord::RecordNotFound (Couldn't find User with auth_token = ):
app/controllers/application_controller.rb:39:in `lookup_user'
app/controllers/application_controller.rb:32:in `current_user'
app/controllers/application_controller.rb:54:in `logged_in?'
app/controllers/users_controller.rb:8:in `new'
If use Firefox and Chrome (in incognito mode) it works. In Safari, I found that if I get the error, I can make it go away by navigating to /logout. Then the page renders perfectly.
Here's my route for /logout and root:
match "/logout" => "sessions#destroy", :as => "logout"
root :to => "users#new"
Here's my destroy action in sessions_controller:
def destroy
reset_session
cookies.delete(:auth_token)
redirect_to root_path, :notice => "You successfully logged out"
end
My application_controller:
protected
def current_user
#current_user ||= lookup_user
end
def lookup_user
if session[:user_id]
User.find_by_id(session[:user_id])
elsif cookies[:auth_token]
User.find_by_auth_token!(cookies[:auth_token])
end
end
And lastly, here's my new action in users_controller:
def new
#user = User.new
#user.profile = Profile.new
if logged_in?
redirect_to profile_path(current_user)
end
end
What I've tried:
To alter the new action to delete cookies with the following:
def new
#user = User.new
#user.profile = Profile.new
if logged_in?
redirect_to profile_path(current_user)
elsif
cookies.delete(:auth_token)
end
end
The rake task below, as suggested in the Railscast comments:
namespace :user do
desc "Rebuild Auth-Tokens"
task :rebuild_auth_token => :environment do
User.transaction do
User.all.each { |u|
u.generate_token(:auth_token)
u.save!
}
end
end
end
(I ran this with `heroku run rake user:rebuild_auth_token`)
Neither seems to have worked. Can anyone help me figure this out?
Anytime you regenerate the user :auth_code's you will need to delete your cookies for that domain. In a production, you should not regenerate :auth_codes and you will never have this issue, unless users edit their cookies.
In addition I have posted a response on the railscast.com authentication (revised) solution so Ryan can take a look at it.
Good luck!

Displaying total votes count with thumbs_up

I am trying to display total votes cast on a post with thumbs_up gem but it does not seem to work.
Here is my code
def vote_up
begin
post = Post.find(params[:id])
current_user.vote_for(post)
redirect_to :back
flash[:sucess] = "You have voted successfully"
#votes_total = post.votes_for
rescue ActiveRecord::RecordInvalid
redirect_to :back
flash[:error] = "You have already voted for this one"
end
end
In the view :-
<%="Total votes = #{#votes_total}"%>
I get the flash message "You have voted successfully" but my votes count are not getting displayed.
This is what I have in my log files :-
[1m[36m (0.3ms)[0m [1mSELECT COUNT(*) FROM "votes" WHERE
"votes"."voteable_id" = 12 AND "votes"."voteable_type" = 'Post' AND
"votes"."vote" = 't'[0m 0
--- Update ---
Update my post controller with this code :-
def vote_up
begin
post = Post.find(params[:id])
current_user.vote_for(post)
#votes_total = post.votes_for
render :template => "home/index"
flash[:sucess] = "You have voted successfully"
rescue ActiveRecord::RecordInvalid
redirect_to :back
flash[:error] = "You have already voted for this one"
end
end
Help please.
It does not display because you are redirecting. When redirecting you are basically doing a new request and the instance variables of the former request won't be available anymore. Flash does work, because it uses the session. Solution: either set #votes_total in the action you are redirecting to or use render instead of redirect_to.
try changing
#votes_total
to the
#post.votes_for or corresponding equivalent
in the view.

Displaying error messages in rails

I seem to have trouble handling error messages. Here's my method:
def destroy
#user = User.find(current_user)
#authorization = Authorization.find(params[:id])
if #user.authorizations.count > 1
#authorization.destroy
redirect_to(user_path(current_user))
else
...
end
end
I don't want a user to delete their last authorization (say, Facebook) because then the user won't have any authorizations associated with it and the account is lost. I want to send an error message that says why the destroy method fails, but I'm just not sure how. Everything I've tried just doesn't work.
I've tried things like #user.errors.add => "error message"
But it just shows up blank. I think my problem is with using render. If I try, for example:
respond_to do |format|
#user.errors[:base] << "Sorry, you can't delete your only authorized service."
format.html render :template => "users/show"
end
I get a problem because rails starts looking for the partials in users/show inside the authorizations directory for some reason, presumably because I'm calling the authorizations controller from a users view.
Here's how I display the flash in my views:
def show_flash
[:notice, :errors, :alert].collect do |key|
msg = (flash[key].to_s + " (close)")
content_tag(:div, (content_tag(:a, msg, :class => "#{key}", :href => "#", :onclick => "$('messages').slideUp(); return false;")), :id => key, :class => "flash_#{key}") unless flash[key].blank?
end.join
end
I can get notices to appear just fine.
So, this seems to be working:
respond_to do |format|
format.html {redirect_to(#user, :alert => "Sorry, you can't delete your only authorized service.")}
end
But, this does not:
respond_to do |format|
format.html {redirect_to(#user, :errors => "Sorry, you can't delete your only authorized service.")}
end

functional testing issue

I have testing code as follow, why "test "should get create" do" always failed????
# called before every single test
def setup
logger.debug '2'
#new_user = User.create(:email => 'shrimpy#email.com',
:password => 'secret',
:password_confirmation => 'secret')
logger.debug "==========> #{#new_user.id}"
end
# called after every single test
def teardown
#new_user.delete
end
test "should get create" do
logger.debug '1'
get :create, :email => "shrimpy#email.com", :password => "secret"
assert_response :success # <------why it always failed here?
assert_redirected_to user_path(#new_user)
end
Console output:
Finished in 3.834383 seconds.
1) Failure:
test_should_get_create(SessionsControllerTest) [test/functional/sessions_controller_test.rb:24]:
Expected block to return true value.
1 tests, 1 assertions, 1 failures, 0 errors, 0 skips
test.log:
2
SQL (2.0ms) SELECT 1 FROM "users" WHERE ("users"."email" = 'shrimpy#email.com') LIMIT 1
SQL (0.0ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
AREL (6.0ms) INSERT INTO "users" ("email", "hashed_password", "salt", "created_at", "updated_at") VALUES ('shrimpy#email.com', 'b4e991c44d9738effa3
98e97d7ed1e6ccad19c90ce2e911344a21bf9c82f915f', '258003960.04544115923816805', '2011-04-21 07:04:00.891929', '2011-04-21 07:04:00.891929')
==========> 980190963
1
Processing by SessionsController#create as HTML
Parameters: {"email"=>"shrimpy#email.com", "password"=>"[FILTERED]"}
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."email" = 'shrimpy#email.com' LIMIT 1
Redirected to http://test.host/users/980190963
Completed 302 Found in 166ms
AREL (0.0ms) DELETE FROM "users" WHERE "users"."id" = 980190963
This is the function i am testing:
def create
if user = User.authenticate(params[:email], params[:password])
session[:user_id] = user.id
redirect_to user_path(user), :notice => 'You have successfully login'
else
redirect_to login_url, :alert => "Invalid email/password combination"
end
end
Because the response isn't success, the response is a redirect.
Understanding this requires a little understanding of HTTP, and a little understanding of what assert_response :success and redirect_to do. All that assert_response :success is testing is the HTTP response code, it's testing to see whether it's a success code. You get a success code when you render a page back to the browser. When you redirect you do not get a success code, you get a redirect code instead.
So if you want to check the response you could use assert_response :redirect - although this is redundant when you already have the assert_redirected_to
Read more here in the Rails guide for testing