I'll just try to outline this as high level as I can. I am trying to access http://localhost:3000/login
The error is:
No route matches {:controller=>"user_sessions"}
And it's erroring on this line in the new.html.erb file below:
<%= form_for(#user_session) do |f| %>
The route in routes.rb is:
match 'login' => 'user_sessions#new', :as => :login
The user_sessions_controller.rb is:
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
flash[:notice] = "Successfully logged in."
redirect_to root_path
else
render :action => 'new'
end
end
def destroy
#user_session = UserSession.find
#user_session.destroy
flash[:notice] = "Successfully logged out."
redirect_to root_path
end
end
And the actual view for the login page is as follows (new.html.erb):
<h1>Login</h1>
<%= form_for(#user_session) do |f| %>
<% if #user_session.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user_session.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user_session.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :login %><br />
<%= f.text_field :login %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>
Thank you.
Using form_for(#user_session) alone will try to build out the path using a resource you have defined in your routes.rb. Which you currently don't have (I'm assuming, as you didn't mention it. Please correct if I'm wrong.).
A few ways to go..
Add a resource and limit to the ones you need
resources :user_sessions, :only => [:create, :destroy]
These will use the default routing namings, but you can custom that up as you need.
Match out the routes you need.
match 'login' => 'user_sessions#create', :as => :post_login, :via => :post
View
= form_for(#user_session), :url => post_login_path do |f|
...
Related
I am trying to use bcrpyt with has_secure_password but instead of password_digest attribute, I am using key_digest. as the documentation of has_secure_password allows us to use any XXX_digest.( https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password )
I am using it as a key will be generated from the system and the user doesn't need to enter any password. but for this example, I am just trying the key to be encrpted and saved in the database. If I change key to password, it works. I am not sure why key is not working
I am getting NoMethodError in Users#new and undefined method "key" for #<User:0x000000000d0beb38>
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
private
## Strong Parameters
def user_params
params.require(:user).permit(:email, :key_digest, :key)
end
end
users/new.html.erb
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :key %><br />
<%= f.number_field :key %>
</div>
<div class="actions"><%= f.submit "Sign Up" %></div>
<% end %>
20210605052011_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :email
t.string :key_digest
t.timestamps
end
end
end
routes.rb
Rails.application.routes.draw do
root to: 'static#home'
get 'static/home'
get 'static/faq'
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
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
In your User model, haven't field key, so error show. Change your code as below to save with hash password!
Edit your app/views/users/new.html.erb to:
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.number_field :password %>
</div>
<div class="actions"><%= f.submit "Sign Up" %></div>
<% end %>
With app/controllers/users_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
private
## Strong Parameters
def user_params
params.require(:user).permit(:email, :password)
end
end
Last, don't forget install bcrypt gem and set this content app/models/user.rb to:
class User < ApplicationRecord
has_secure_password
alias_attribute :password_digest, :key_digest
end
Any things will fine! : D
I was following this tutorial http://www.sitepoint.com/nested-comments-rails/ to implement nested comments for an image board. It worked fine until I made "comments" belong to "boards" and then had to nest my routes.
Here are my routes:
Rails.application.routes.draw do
root "boards#index"
devise_for :users do
get '/users/sign_out' => 'devise/sessions#destroy'
end
resources :boards do
resources :comments
get '/comments/new/(:parent_id)', to: 'comments#new', as: :new_comment
get '/comments/(:parent_id)', to: 'comments#destroy', as: :delete_comment
get '/comments/edit/(:parent_id)', to: 'comments#edit', as: :edit_comment
end
end
Here is my form:
<%= form_for [#board, #comment] do |f| %>
<% if #comment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#comment.errors.count, "error") %> prohibited this comment from being saved:</h2>
<ul>
<% #comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :parent_id %>
<div class="form-group">
<% if #comment.parent_id == nil %>
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
<% else %>
<% nil %>
<% end %>
</div>
<div class="form-group">
<%= f.radio_button(:user_id, current_user.id) %>
<%= f.label(:user_id, "I want to post as myself") %>
<%= f.radio_button(:user_id, nil) %>
<%= f.label(:user_id, "I want to post anonymously") %>
</div>
<div class="form-group">
<%= f.label :content %>
<%= f.text_area :content, class: 'form-control', required: true %>
</div>
<div class="form-group">
<%= f.label :image %>
<%= f.file_field :image %>
</div>
<%= f.submit class: 'btn btn-primary' %>
<% end %>
And here is my controller:
class CommentsController < ApplicationController
def index
#comments = Comment.hash_tree
end
def new
#comment = Comment.new(parent_id: params[:parent_id])
end
def edit
#comment = Comment.find(params[:parent_id])
end
def create
if params[:comment][:parent_id].to_i > 0
parent = Comment.find_by_id(params[:comment].delete(:parent_id))
#comment = parent.children.build(comment_params)
else
#comment = Comment.new(comment_params)
end
if #comment.save
redirect_to root_url
else
render 'new'
end
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
redirect_to #comment
else
render 'edit'
end
end
def make_parent
#comment.parent_id = nil
#comment.save
end
def destroy
#comment = Comment.find(params[:parent_id])
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url }
end
authorize! :destroy, #comment
end
private
def comment_params
params.require(:comment).permit(:title, :content, :user_id, :image)
end
end
I've tried setting a custom route in the form, this gets the form to at least appear, however when you hit the submit button it returns 'No route matches [POST] "/boards/1/comments/new"'. If I got to the controller and then change the corresponding "get" to a "post" then it causes the form to just reappear after pressing submit and nothing is added to the database. I've also experimented with shallow nesting my routes as per my instructors advice but this didn't work.
Your association between boards and comments must be:
board.rb
has_many :comments
comment.rb
belongs_to :user
routes.rb
resources :boards do
resources :comments, only: [:new, :edit, :destroy]
end
this will create a route
new_board_comment GET /boards/:board_id/comments/new(.:format) comments#new
edit_board_comment GET /boards/:board_id/comments/:id/edit(.:format) comments#edit
board_comment DELETE /boards/:board_id/comments/:id(.:format) comments#destroy
So, I got an interesting problem. I am attempting to override and redirect a devise sign_up and sign_in request to their appropriate profile pages but I am running into an error. The profiles and URL work by accessing it with console, link_to or by typing it out. For some reason the devise method won't route to it. I'm not sure why. Anyways, upvotes for all that contribute or solve. Thanks!
Routes:
root :to => 'pages#index'
get "pages/index"
devise_for :users, :path => 'accounts', :controllers => { :registrations => "registrations" }
get 'users/:id/profile' => 'profiles#show', :as => 'current_profile'
get 'users/:id/profile/edit' => 'profiles#edit', :as => 'edit_current_profile'
put 'users/:id/profile' => 'profiles#update'
resources :users do
resources :profiles
end
Registration Controller:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
edit_current_profile_path(resource)
end
def after_sign_in_path_for(resource)
current_profile_path(resource)
end
end
Profile Controller
def show
#user = current_user
#profile = #user.profile
respond_to do |format|
format.html
end
end
Error message:
Routing Error
No route matches {:controller=>"profiles", :action=>"show"}
Try running rake routes for more information on available routes.
View: edit.html.erb
<%= form_for([#profile.user, #profile]) do |f| %>
<% if #profile.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#profile.errors.count, "error") %> prohibited this profile from being saved:</h2>
<ul>
<% #profile.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :real_name %><br />
<%= f.text_field :real_name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You should give an attribute to your route helpers. Try it out
def after_sign_up_path_for(resource)
edit_current_profile_path(resource)
end
def after_sign_in_path_for(resource)
current_profile_path(resource)
end
Sorry for the novice question but I am trying to get to grips with RoR. I have a very basic sign up and login process in place but am having some difficulty getting the routing correct. I am also unsure whether I am actually being logged out successfully when I push my logout button because it isn't then displaying the login button as it should.
My setup is as follows on Rails 3.1:
Sessions Controller
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password!"
render "signup"
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged Out!"
end
end
User Controller
class UserController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new (params[:user])
if #user.save
redirect_to root_url, :notice => "Signed Up!"
else
render "user/new"
end
end
end
User Model
class User < ActiveRecord::Base
has_secure_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email, :on => :create
end
Sessions/new.html.erb
<h1>Log In</h1>
<%= form_tag login_path do %>
<div class="field">
<%= label_tag :email %>
<%= text_field_tag :email, params[:email] %>
</div>
<div class ="field">
<%= label_tag :password %>
<%= password_field_tag :password %>
</div>
<div class="actions"><%= submit_tag "Log in" %></div>
<%end%>
User/new.html.erb
<% if session[:user_id] %>
<!-- user is logged in -->
<%= link_to logout_path %>
<% else %>
<!-- user is not logged in -->
<%= link_to login_path %>
<% end %>
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class = "field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class = "field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class = "field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<div class="actions"><%= f.submit %></div>
<% end %>
Finally my Routes file
MadeByV2::Application.routes.draw do
controller :user do
get "signup" => "user#new"
end
resources :users
controller :sessions do
get "login" => "sessions#new"
post "login" => "sessions#create"
delete "logout" => "sessions#destroy"
end
root :to => "user#new"
end
Sorry for the extensive use of code in this post but I figure it's best to give a well rounded view of everything so people can see where I am going wrong.
Any help you can offer really would be much appreciated because I don't seem to be getting it myself
Thanks,
Tom
Your code looks fine, but your routes look a little strange.
I'd try something like this:
resources :users
resources :sessions
match 'login' => 'sessions#new', :as => :login
match 'logout' => 'sessions#destroy', :as => :logout
Then I think everything you've currently got should work.
Please see UPDATES at the bottom of the question...
For reference, this problem evolved out of some fixes I made based on a previous problem I was having here: Associating Two Models in Rails (user and profile)
I'm building an app that has a user model and a profile model.
I want to associate these models such that:
- After the user creates an account, he is automatically sent to the "create profile" page, and the profile he creates is connected to only that particular user.
- Only the user who owns the profile can edit it.
I generated the user model using nifty_generators. When the user hits submit for the account creation, I redirect him to the "new profile" view to create a profile. I did this by editing the redirect path in the user controller. The user controller looks like this:
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
flash[:notice] = "Thank you for signing up! You are now logged in."
redirect_to new_user_profile_path(:user_id => #user)
else
render :action => 'new'
end
end
When I click 'submit' to create a new user, it now sends me to the following url, which seems right: localhost:3000/users/6/profile/new. But it throws the following exception:
NoMethodError in
ProfilesController#new You have a
nil object when you didn't expect it!
The error occurred while evaluating
nil.build
The trace indicates that the problem is in the profiles controller, in the New method. The profiles controller looks like this:
def index
#user = User.find(params[:user_id])
#profile = #user.profile(:order => "created_at DESC")
end
def show
#user = User.find(params[:user_id])
#profile = #user.profile.find(params[:id])
end
def new
#user = User.find(params[:user_id])
#profile = #user.profile.build
end
def edit
#user = User.find(params[:user_id])
#profile = #user.profile.find(params[:id])
end
def create
#user = User.find(params[:user_id])
#profile = #user.profile.build(params[:profile])
if #profile.save
flash[:notice] = 'Profile was successfully created.'
redirect_to(#profile)
else
flash[:notice] = 'Error. Something went wrong.'
render :action => "new"
end
end
Additionally, the app also throws an exception when I try to view the index page of the profiles (there are currently no profiles because I can't get past the user creation step to create one). This is the exception:
ActiveRecord::RecordNotFound in ProfilesController#index
Couldn't find User without an ID
This is what the log is telling me:
Processing ProfilesController#index
[GET] Parameters:
{"action"=>"index",
"controller"=>"profiles"}
ActiveRecord::RecordNotFound (Couldn't
find User without an ID):
app/controllers/profiles_controller.rb:5:in
`index'
To give you the rest of the details on the app, the models have the following associations:
Profile belongs _to :user
User has _one :profile
I have this in the routes.rb file: map.resources :users, :has_one => :profile
In the view for the new profile page that's throwing the first exception listed above, I have this:
<% form_for([#user, #profile]) do |f| %>
<%= f.error_messages %>
....
<% end %>
In the view for the profile index that's throwing the second exception explained above, I have this:
<% #profiles.each do |profile| %>
<div class="post">
<div class="left">
<p>Store: </p>
<p>Category: </p>
</div>
<div class="right">
<p><%=h profile.name %></p>
<p><%=h profile.category %></p>
</div>
<div class="bottom">
<p><%= link_to 'Go to profile', user_profile_path(#user, profile) %></p>
<p><%= link_to 'Edit', edit_user_profile_path(#user, profile) %></p>
<p><%= link_to 'Destroy', user_profile_path(#user, profile), :confirm => 'Are you sure?', :method => :delete %></p>
</div>
I've spent hours trying to track down the problem myself as a learning exercise, but at this point I have no idea how to fix this. Appreciate the help!
UPDATE:
jdl, per your request:
profiles/new.html.erb:
<% form_for([#user, #profile]) do |f| %>
<%= f.error_messages %>
<div class="left">
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>required
</p>
<p>
<%= f.label :category %><br />
<%= f.text_field :category %>required
</p>
<p>
<%= f.label :address1 %><br />
<%= f.text_field :address1 %>
</p>
<p>
<%= f.label :address2 %><br />
<%= f.text_field :address2 %>
</p>
<p>
<%= f.label :city %><br />
<%= f.text_field :city %>
</p>
<p>
<%= f.label :state %><br />
<%= f.text_field :state %>
</p>
<p>
<%= f.label :zip %><br />
<%= f.text_field :zip %>required
</p>
<p>
<%= f.label :phone %><br />
<%= f.text_field :phone %>
</p>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
</div>
<div class="right">
<p>
<%= f.label :website %><br />
<%= f.text_field :website %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description %>
</p>
</div>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
routes.rb:
ActionController::Routing::Routes.draw do |map|
map.signup 'signup', :controller => 'users', :action => 'new'
map.logout 'logout', :controller => 'sessions', :action => 'destroy'
map.login 'login', :controller => 'sessions', :action => 'new'
map.resources :sessions
map.resources :users, :has_one => :profile
map.root :controller => "home"
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
PROFILES CONTROLLER (as of 8/20/09, 8pm EST)
class ProfilesController < ApplicationController
def index
#users = User.all(:order => "created_at DESC")
end
def show
#user = User.find(params[:user_id])
end
def new
#user.profile = Profile.new
end
def edit
#user = User.find(params[:user_id])
#profile = #user.profile.find(params[:id])
end
def create
#user = User.find(params[:user_id])
#profile = #user.profile.build(params[:profile])
if #profile.save
flash[:notice] = 'Profile was successfully created.'
redirect_to(#profile)
else
flash[:notice] = 'Error. Something went wrong.'
render :action => "new"
end
end
def update
#profile = Profile.find(params[:id])
if #profile.update_attributes(params[:profile])
flash[:notice] = 'Profile was successfully updated.'
redirect_to(#profile)
else
render :action => "edit"
end
end
def destroy
#profile = Profile.find(params[:id])
#profile.destroy
redirect_to(profiles_url)
end
end
Cody, the index page below is throwing the following exception:
NoMethodError in Profiles#index
Showing app/views/profiles/index.html.erb where line #14 raised:
undefined method `name' for #
<div id="posts">
<% #users.each do |profile| %>
<div class="post">
<div class="left">
<p>Store: </p>
<p>Category: </p>
</div>
<div class="right">
<p><%=h profile.name %></p>
<p><%=h profile.category %></p>
</div>
<div class="bottom">
<p><%= link_to 'Go to profile', user_profile_path(#user, profile) %></p>
<p><%= link_to 'Edit', edit_user_profile_path(#user, profile) %></p>
<p><%= link_to 'Destroy', user_profile_path(#user, profile), :confirm => 'Are you sure?', :method => :delete %></p>
</div>
</div>
The error is telling you that in this line:
#profile = #user.profile.build
#user.profile is nil.
Since you haven't created the profile yet, this makes sense. Instead, go for something like this.
#profile = Profile.new(:user_id => #user.id)
Re: The index page throwing an exception.
You are defining #users in your controller, and then referencing #user in your path helpers. Also, iterating over #users should give you User objects, not Profile objects.
It looks like you're trying to use the Profile#index action to show a list of users. That's kind-of OK, in a not-quite-pure-REST way. However, I would expect to see something more like this.
<% #users.each do |user| -%>
<% unless user.profile.blank? -%>
<%= h user.profile.name %>
<%= h user.profile.category %>
<%= link_to 'Go to profile', user_profile_path(user, user.profile) %>
<% end -%>
<% end -%>
hmm.. I thought when User has many profiles, you can then use:
#user.profiles.build
#user.profiles.create
when User has one profile, you have to use:
#user.build_profile
#user.create_profile
Can't be sure, check API
#jdl was correct in that there is no #user.profile object in ProfilesController#new so calling #user.profile.build would throw a nil exception.
You can fix this by instantiating a new Profile object by
#user.profile = Profile.new
Later if #user gets saved then it will trigger a #user.profile and the foreign keys will be set appropriately.
Also in your ProfilesController, the #index and #show actions are kind of weird. Conventionally, the #index action returns a "list of objects" and #show is to display just one object, a specific one given an ID. However, your #index returns a very specific object because it does a User.find with a specific ID. Furthermore, since a user only has one Profile object it doesnt make sense to also load up its Profile object with an ORDER BY. There should only be one so no order is necessary. On top of that, its debatable whether you need to explicitly load the profile object as you can just access it via #user.profile and ActiveRecord will load it on demand. So your new #index looks something like
def index
#users = User.paginate(:all, :order = "created_at desc", :page => 1, :per_page => 10)
end
This assumes you have the WillPaginate plugin, but the gist is that you are loading a LIST of objects, not just one. In your view, if you are iterating over #users and call .profile on an element of that list then ActiveRecord will load the associated Profile on the fly.
Pretty same thing for #show - no need to explicitly load the profile.
def show
#user = User.find(params[:user_id])
end
Do one thing
In ProfileController provide session[:user_id] not params[:user_id]
def show <br>
#user = User.find(session[:user_id])<br>
#profile = #user.profile <br>
end <br>
I hope it works!