I'm trying to setup logic in a rails 4.2.0 app where a person has to confirm their user account before they can login to the site / rails app. Basically, I have a sign up form where a person can input an email / password and their signed up. During this process an email is sent to their address with a confirmation token that should provide a link for them to confirm their account. I'm not exactly sure how to use the confirmation token so it changes a boolean value in the DB from false to true. I'll post what I have implemented so far.
users_controller.rb
def create
#user = User.new(user_params)
if #user.save
# send confirmation email after user has been created.
#user.send_confirmation
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
def confirm
#user = User.find_by_confirmation_token!(params[:id])
if #user.update_attributes(confirmed: true)
redirect_to login_path
end
end
confirmation.text.erb
To confirm your account, click the URL below.
<%= user_url(#user.confirmation_token) %>
<%= url_for(controller: 'users', action: 'confirm') %>
If you did not request your account creation, just ignore this email and your account will not be created.
routes.rb
Rails.application.routes.draw do
resources :articles do
resources :comments
end
get 'resume' => 'resume#index'
get 'signup' => 'users#new'
get 'login' =>'sessions#new'
get 'logout' => 'sessions#destroy'
# the below route led to a rails routing error
# get 'confirm' => 'users/:confirmation_token#confirm'
resources :users
resources :sessions
resources :password_resets
# route to hopefully get confirmation link working :-/
match '/users/:confirmation_token', :to => 'users#confirm', via: [:post, :get]
# test route
match 'users/foo', :to => 'users#foo', via: [:post, :get]
root "articles#index"
# Added below route for correct "resumé" spelling
get 'resumé', :to =>"resume#index"
# get 'about#index'
get 'about' => 'about#index'
get 'contact' => 'contact#contact'
resources :about
resources :contact
match ':controller(/:action(/:id))(.:format)', via: [:post, :get]
I ended up separating the confirmation logic into it's own controller, i.e. away from the users_controller.rb This allowed me to add the following line to my routes.rb
resources :confirmations
which allowed me to edit the confirmation.text.erb and put the following link in the email message,
<%= edit_confirmation_url(#user.confirmation_token) %>
thus when a person receives an email to confirm their account, it routes to the edit action of the confirmation controller, which the edit action calls the update action, and confirms the account. The controller looks like the following,
confirmations_controller.rb
class ConfirmationsController < ApplicationController
def new
end
def edit
#user = User.find_by_confirmation_token!(params[:id])
update
end
def update
# #user = User.find_by_confirmation_token!(params[:id])
if #user.confirmation_sent_at < 2.hours.ago
redirect_to new_confirmation_path, :alert => "Confirmation has expired."
# elseif #user.update_attributes(params[:user])
elsif #user.update_attributes(confirmed: true)
redirect_to root_url, :notice => "Your account has been confirmed."
else
render :new
end
end
end
Related
I recently watched the railscast episode #250 Authentication from Scratch (revised) and I have the signup / login / logout actions working. However I am working on creating an action to delete a user from the database, but I am currently experiencing some errors when I try to delete a user.
The users_controller.rb delete / destroy actions look like the following,
def delete
# the below line calls the destroy method / action
self.destroy
end
def destroy
session[:user_id] = nil
# #user = User.find(params[:id])
#user.destroy
# User.find(parmas[:id]).destroy
# the below line didn't delete the current user :(
# #user = User.destroy
redirect_to :controller=>'users', :action => 'new'
end
The error message I'm getting in the browser when I try to delete a user looks like the following.
The page that contains the delete link looks like the following, index.html.erb
<h1>Welcome
<% if current_user %>
<%= current_user.email %>
<% end %>
</h1>
<p>You have <%= current_user.credit %> credits.</p>
<!-- http://stackoverflow.com/questions/5607155/ -->
<%= link_to('Delete your account', :controller => 'users', :action => 'destroy') %>
routes.rb
Rails.application.routes.draw do
# the below generated route is not necessary
# get 'sessions/new'
# delete user route
#get 'delete' => 'users#delete'
# shortened routes, per railscast comment
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
get 'logout' => 'sessions#destroy'
# get 'signup', to: 'users#new', :as 'signup'
# get 'login', to: 'sessions#new', :as 'login'
# get 'logout', to: 'sessions#destroy', :as 'logout'
resources :users
resources :sessions
root to: 'users#new'
# get 'users/new'
# the below line specifies JSON as the default API format
namespace :api, defaults: {format: 'json'} do
namespace :v1 do
resources :users
end
end
It stands to reason you're getting a NoMethodError, since you've never set the #user variable, that line is commented out:
def destroy
session[:user_id] = nil
# #user = User.find(params[:id]) <-- Commenting out this line was your problem
#user.destroy
Changing to
def destroy
session[:user_id] = nil
#user = User.find(params[:id])
#user.destroy
You should be good to go.
EDIT: one thing you'd probably want to do is change from using the old style of link_to, specifying the controller and action, and change to the new style, using route helpers. In this case, you'd use, i believe, link_to 'Delete your account', current_user, :method => :delete, but you can check by running rake routes, where it will list the helpers available based on your routes.rb file.
Well, I think you should make things a bit simpler and start from the dummiest thing, that works. First of all, if you use your controller as a resource, there would not be a delete action there, only destroy.
def destroy
User.find(params[:id]).destroy
session[:user_id] = nil
redirect_to new_user_path
end
P.S. once again, I assume that you have set resources :users in your routes.rb.
If you have a bunch of get|post|put|delete routes instead, just make sure you point the redirect correctly.
I am new to rails and I am trying to add a email confirmation upon register. I currently get this error.
(Bonus points for any verbose and easily understood answer.)
Routing Error
No route matches {:action=>"edit", :controller=>"email_activations", :id=>false}
config/routes.rb
LootApp::Application.routes.draw do
get "password_resets/new"
get "sessions/new"
resources :users
resources :sessions
resources :password_resets
resources :email_activations
root to: 'static_pages#home'
app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
def registration_confirmation(user)
#user = user
mail(:to => user.email, :subject => "registered", :from => "alain#private.com")
end
end
app/controllers/email_activations_controller.rb
class EmailActivationsController < ApplicationController
def edit
#user = User.find_by_email_activation_token!(params[:id])
#user.email_activation_token = true
redirect_to root_url, :notice => "Email has been verified."
end
end
app/views/user_mailer/registration_confirmation.html.haml
Confirm your email address please!
= edit_email_activation_url(#user.email_activation_token)
resources keyword in rails routes is a magical keyword that creates 7 restful routes by default
edit is one of those
check these docs link
http://guides.rubyonrails.org/routing.html#crud-verbs-and-actions
edit expects to edit a record so requires a id to find the record for editing
in your case
you can just add a custom action in users controller
like
in UsersController
def accept_invitation
#user = User.find_by_email_activation_token!(params[:token])
#user.email_activation_token = true
redirect_to root_url, :notice => "Email has been verified."
end
in routes.rb
resources :users do
collection do
get :accept_invitation
end
end
in app/views/user_mailer/registration_confirmation.html.haml
accept_invitation_users_url({:token=>#user.email_activation_token})
Check out how to add custom routes here
http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
My question has to do with mapping to controllers/actions using named routes. I am trying to map '/profile' to 'customers#show'. My routes file looks like this:
root :to => 'pages#home'
## named routes
match "profile" => "customers#show", :as => 'profile'
match 'signin' => 'sessions#new', :as => 'signin'
match 'signout' => 'sessions#destroy', :as => 'signout'
resources :customers do
member do
get 'add_card'
post 'submit_card'
end
end
resources :payments, :only => [:show, :new]
delete 'payments/delete_recurring_payment'
post 'payments/submit_non_recurring'
post 'payments/submit_recurring'
resources :sessions, :only => [:create, :destroy, :new]
Running 'rake routes' gives me this:
root / pages#home
profile /profile(.:format) customers#show
signin /signin(.:format) sessions#new
signout /signout(.:format) sessions#destroy
add_card_customer GET /customers/:id/add_card(.:format) customers#add_card
submit_card_customer POST /customers/:id/submit_card(.:format) customers#submit_card
customers GET /customers(.:format) customers#index
POST /customers(.:format) customers#create
new_customer GET /customers/new(.:format) customers#new
edit_customer GET /customers/:id/edit(.:format) customers#edit
customer GET /customers/:id(.:format) customers#show
PUT /customers/:id(.:format) customers#update
DELETE /customers/:id(.:format) customers#destroy
new_payment GET /payments/new(.:format) payments#new
payment GET /payments/:id(.:format) payments#show
Here is where I'm stumped. When I go to localhost:3000/profile I get a routing error saying this:
No route matches {:action=>"edit", :controller=>"customers"}
This seems odd because there is indeed a route to 'customers#edit' due to my declaring customers as a resource.
However, when I go to 'localhost:3000/signin' I get routed to 'customers#show' which is where I want '/profile' to route to.
It seems like my routes are 'one off' in my routes file but I have no idea why. Any help would be much appreciated.
Thanks
UPDATE 1: Adding my Customers Controller
class CustomersController < ApplicationController
layout "payments_layout"
def show
#customer = current_user
get_account_info(#customer)
get_payment_history(#customer, 10)
end
def new
#title = 'Create an account'
#customer = Customer.new
end
def edit
#customer = current_user
get_account_info(#customer)
end
def update
#customer = current_user
if #customer.update(params[:customer])
redirect_to #customer
else
#card_message = "Use this form to add a credit card to your account. You must have a credit card associated with your account in
in order to make payments on our system."
get_account_info(#customer)
render 'edit'
end
end
def create
#customer = Customer.new(params[:customer])
if #customer.save_and_get_stripe_id
sign_in(#customer)
redirect_to #customer
else
#title = 'Create an account'
render 'new'
end
end
def add_card
#customer = current_user
get_account_info(#customer)
#card_message = "Use this form to add a credit card to your account. You must have a credit card associated with your account in
in order to make payments on our system."
end
def submit_card
#customer = current_user
res = #customer.add_or_update_card(params)
if res
redirect_to #customer
else
#error = res
customer.get_account_info(#customer)
render 'add_card'
end
end
end
Check your views.
Do you have a link to edit your user profile?
It seems like you actually route to the right controller and action but have some links that rake can't route, e.g. you don't pass the ID to your edit_customer_path link.
I'm trying to implement a login form, but my routing seems to be wrong.
When the authentication fails, the app redirects to subdomain.domain.com/login but it should only render the login page again (only subdomain.domain.com without "/login").
views/login/index.html
<%= form_tag(check_login_path, :method => "post") do %>
... form fields
<% end %>
routes.rb
constraints(Subdomain) do
match '/' => 'login#index', :as => :login
match '/login' => 'login#check', :as => :check_login
match '/dashboard' => 'dashboard#index', :as => :dashboard
end
login_controller.rb
class LoginController < ApplicationController
def index
# some index logic
end
def check
# authenticate with mite.yo.lk account login
Mite.account = params[:domain]
Mite.authenticate(params[:email], params[:password])
if Mite.validate
redirect_to dashboard_path
else
flash[:error] = "not valid"
render :template => 'login/index'
end
end
end
In your routes, you are routing '/login' to the check action, and then in that action you are rendering a template for a failed validation. Rendering does not change the URL, which is why you end up with the url '/login'.
If you want the user to see the root url after a failed login attempt, then you'll need to change the render to a redirect_to :index:
if Mite.validate
redirect_to dashboard_path
else
flash[:error] = "not valid"
redirect_to :index
end
Alternatively, you could replace your check_login path to use the root url with a POST request, and make your login path route to the root only for a GET request, like so:
constraints(Subdomain) do
get '/' => 'login#index', :as => :login
post '/' => 'login#check', :as => :check_login
match '/dashboard' => 'dashboard#index', :as => :dashboard
end
If you do this, then you should be able to leave your current controller code as is and get the result you want.
When I login on my page I automatic go to the route: http://localhost:3000/sessions/user
And I get this error:
Routing Error
No route matches "/sessions/user"
I have created a controller named sessions_controller.rb in users folder here it is:
class Users::SessionsController < Devise::SessionsController
def new
redirect_to root_url, :notice => "You have been logged out."
end
def create
user = User.authenticate(params[:login], params[:encrypted_password])
if user
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in successfully."
else
flash.now[:alert] = "Invalid login or password."
render :action => 'new'
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "You have been logged out."
end
end
My route file:
Densidste::Application.routes.draw do
match 'user/edit' => 'users#edit', :as => :edit_current_user
devise_for :users, :controllers => { :sessions => "users/sessions" } do
get "login", :to => "devise/sessions#new"
get "opret", :to => 'users/users#new'
get "logud", :to => 'users/users#destroy'
end
resources :sessions
resources :users
devise_for :users, :controllers => { :sessions => "users/sessions" }
resources :aktivs
resources :taggingposts
resources :tags
resources :kommentares
resources :posts
end
(Old question but I ran into the same issue when setting up Devise, so hope this helps others)
Removing resources :sessions from the routes file should solve the problem.
For those who experiencing this issue with Devise 2.0 and Rails 3.2.1 and checked all the observations made by #Micah Alcorn but still facing the problem — restart your web server. Worked for me.
You don't have a root_url defined. It is still pointing to the static public/index.html. (edited out by Ryan Bigg)
devise_for :users is stated twice.
resources :users is unnecessary unless you have a RESTful controller handling destroy and index actions outside of devise.
Do you, in fact, have a "users" controller for that first edit action too? That should probably be in a custom Users::RegistrationsController < Devise::RegistrationsController.