This is my controller file:
class OrdersController < ApplicationController
...
def create
#order = current_user.current_cart.order #get a current order
if #order.nil?
#order = current_user.current_cart.build_order #if one does not exist, create it
end
#order.update_attributes!(...) #update the attributes
render :new
end
...
end
I get a "undefined method 'order_path'" error whenever I try to save/update the order model.
This is the simplified version of the view:
<%= form_for #order do |f| %>
....
<% end %>
Whenever #order = Order.new it works, and if I do #order = current_user.current_cart.order, it works. But, as soon as I save something or update something before rendering a template, it gives me the error. I would like to save the model.
In the routes the order is a simple resources :orders.
I have same error. When routes.rb
resources :orders, only: [:index, :create, :edit] do
# ...
end
Solution
add :update
resources :orders, only: [:index, :create, :edit, :update] do
# ...
end
Related
I'm trying to add a validation to my Rails app in order to display an error message if the user goes to the wrong id. The project has reviews, if I go to http://localhost:3000/reviews/:id that doesn't exist the app crashes, I'd like to prevent the runtime error by displaying a message.
In the model, I got this validation:
class Review < ApplicationRecord
validates :id, presence: true
end
Then, in the reviews/show.html.erb file, I'm trying this:
<% if #review.valid? %>
<div class='review-header'>
....
</div>
<% else %>
<% #review.errors.objects.first.full_message %>
<% end %>
This is also the Reviews Controller:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
before_action :authorize!, only: [:edit, :destroy]
def index
if params[:search]
#reviews = Review.where("title like ?", "%#{params[:search]}%")
else
#reviews = Review.all
end
end
def new
#review = Review.new
#comment = Comment.new
#comment.review_id = #review.id
#We need to declare the comments in the new action.
end
def create
#review = current_user.reviews.new(review_params)
if #review.save
redirect_to review_path(#review)
else
render 'new'
end
end
def show
#comment = Comment.new
#We also need to declare the new comment in the show action.
end
def edit
end
def update
if #review.update(review_params)
redirect_to review_path(#review)
else
render 'edit'
end
end
def destroy
#review.destroy
redirect_to reviews_path
end
private
def set_review
#review = Review.find_by(id: params[:id])
end
def review_params
params.require(:review).permit(:title, :content, :category_id, :search)
end
def authorize!
authorize #review #authorize method using the Pundit gem
end
end
However, my project keep crashing rather than showing a message. If there's any way I can make this work? Thanks.
The whole setup of the question is actually broken.
You don't need to add a model validation for the id since ids are automatically generated by the database when you insert records. On most databases primary keys are also non-nullable. Adding the validation will actually break the model as will prevent you from saving records without manually assigning an id (bad idea).
Its also not the models job to verify that a record can be found in the controller. Instead your controller should use find so that it bails early if the record cannot be found:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
before_action :authorize!, only: [:edit, :destroy]
private
def set_review
#review = Review.find(params[:id])
end
end
This halts execution of the method and other callbacks and prevents the NoMethodErrors that are bound to occur. There is no sense in continuing to process a request if the record that its supposed to CRUD doesn't exist.
By default Rails will handle an uncaught ActiveRecord::RecordNotFound exception by rendering a static HTML page located at public/404.html and returning a 404 status code. If you want to customize this on the controller level use rescue_from:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
before_action :authorize!, only: [:edit, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :not_found
private
def set_review
#review = Review.find(params[:id])
end
def not_found
# renders app/reviews/not_found.html.erb
render :not_found,
status: :not_found
end
end
Note that this should be done in different view. If you add a <% if #review.present? %> to your reviews/show.html.erb view you should get your Rails licence revoked as the views one and only job is to display the review.
You can also configure the responses on the application level with config.exceptions_app.
The problem is that if the ID does not correspond to a review in the database, the #review object will be nil, and your line if #review.valid? will throw an error.
You need a different test, something like
<% if #review.present? %>
<div class='review-header'>
....
</div>
<% else %>
Review does not exist.
<% end %>
I have a user profile controller called "userinfo" and it's corresponding view. The userinfo index is the root path. In the homepage(which is the userinfo index), I have a link that takes you to the user profile page. It is giving me this error when I go to the home page:
My routes are:
My userinfos_controller:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#userinfors = Userinfo.find(params[:id])
end
def show
#myvideo = Video.last
end
def new
#userinformation = current_user.userinfos.build
end
def create
#userinformation = current_user.userinfos.build(userinfo_params)
if #userinformation.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#userinformation.destroy
redirect_to userinfo_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major)
end
def find_userinfo
#userinformation = Userinfo.find(params[:id])
end
end
and my view is:
<%= link_to 'profile', userinfors_path(#userinfors) %>
My routes.rb file:
Rails.application.routes.draw do
devise_for :users
resources :userinfos do
resources :videos
end
resources :pages
get '/application/decide' => 'application#decide'
root 'userinfos#index'
get '/userinfos/:id', to: 'userinfos#show', as: 'userinfors'
end
Thanks for any help!
ok, there are multiple errors and you are not following conventions of rails, index is not for what you have used.
Index is used to list all the users and show for a particular one with id passed in params.
Your index path is, as you can see, /userinfos which is correct and it doesn't have any id with it but you are trying to find user with params[:id] which is nil and hence the error.
Lets try out this:
def index
#userinfors = Userinfo.all #pagination is recommended
end
In your index view,
<% #userinfors.each do |userinfor| %>
<%= link_to "#{userinfor.name}'s profile", userinfo_path(userinfor) %>
<% end %>
It should work now.
Please read routing and action controller to get the idea and understand the magic behind rails routing and mvc architecture..
I am getting a routing error when I attempt to create a new db entry or update a current one.
ERROR: No route matches [POST] "/pubs"
Routes.rb:
resources :people, except: [:show] do
resources :pubs, except: [:create, :new, :edit, :destroy]
end
resources :articles
resources :pubs, except: [:create, :new, :edit, :destroy]
namespace :sekret do
resources :people do
resources :pubs
end
end
sekret/pubs_controller
class Sekret::PubsController < SekretController
def index
#pubs = Pub.all
end
def show
#pub = Pub.find(params[:id])
end
def new
#person = Person.find(params[:person_id])
#pub = #person.pubs.new
end
def create
#pub = Pub.new(pub_params)
if #pub.save
flash[:notice] = "Article created successfully!"
redirect_to sekret_person_pub_path(#pub)
else
render :new, status: :unprocessable_entity
end
end
def edit
#pub = Pub.find(params[:id])
end
def update
#pub = Pub.find(params[:id])
if #pub.update(pub_params)
redirect_to sekret_person_pub_path(#pub)
else
render :edit, status: :unprocessable_entity
end
end
def destroy
pub = Pub.find(params[:id])
pub.destroy
redirect_to sekret_people_path
end
private
def pub_params
params.require(:pub).permit(
:pubmed_id, :journal, :pages, :date, :type, :link, :authors,
:title, :notes, :auth_id, :person_id)
end
end
After going through all of this setup, when I allow the non-namespace pubs to resolve edit, update, etc, the update process goes through without a hitch. Once I limit these functions to within the password protected namespace I get the routing error. After parsing through the routes I can see that sekret_person_pub_path is listed there. I think I am missing something somewhere.
Rake Routes:
pubs#index
pub GET /pubs/:id(.:format) pubs#show
PATCH /pubs/:id(.:format) pubs#update
PUT /pubs/:id(.:format) pubs#update
sekret_person_pubs GET /sekret/people/:person_id/pubs(.:format) sekret/pubs#index
POST /sekret/people/:person_id/pubs(.:format) sekret/pubs#create
new_sekret_person_pub GET /sekret/people/:person_id/pubs/new(.:format) sekret/pubs#new
edit_sekret_person_pub GET /sekret/people/:person_id/pubs/:id/edit(.:format) sekret/pubs#edit
sekret_person_pub GET /sekret/people/:person_id/pubs/:id(.:format) sekret/pubs#show
PATCH /sekret/people/:person_id/pubs/:id(.:format) sekret/pubs#update
PUT /sekret/people/:person_id/pubs/:id(.:format) sekret/pubs#update
DELETE /sekret/people/:person_id/pubs/:id(.:format) sekret/pubs#destroy
sekret_people GET /sekret/people(.:format)
By using resources :pubs, except: [:create, :new, :edit, :destroy], you are preventing the route generation from providing POST /pubs.
The namespace and nested resources will generate a URL POST sekret/people/:person_id/pubs.
In your controller, you should create the Pub as an associated object.
def create
person = Person.find(params[:person_id])
#pub = person.pubs.new(pub_params)
if #pub.save
flash[:notice] = "Article created successfully!"
redirect_to sekret_person_pub_path(#pub)
else
render :new, status: :unprocessable_entity
end
end
If you want to restrict access the create method, you could use an authorization library such as Pundit in which case you would setup a policy to restrict who can do what.
https://github.com/elabs/pundit
You are missing out on the routes because rails form don't use the correct routes when namespacing so you'll have to specify them manually
<%= form for #pub, url: sekret_person_pubs_path do |f| %>
to let the form knows which route to post, if you do not specify the url, rails will use url: person_pubs_path behind the scenes
Edit: forgot to add _path
I have a link on my #index view:
<%= link_to 'Export Calendar (ICS)', { controller: :tickets, action: :ics_export, format: :ics }, class: "class-needed right" %>
routes.rb that pertains to this:
resources :tickets
get 'tickets/calendar' => 'tickets#ics_export'
post 'tickets' => 'tickets#index'
patch 'tickets/:id/close' => 'tickets#close', as: 'close_ticket'
post 'tickets/:id' => 'ticket_comments#create'
My TicketsController that pertains:
before_action :set_ticket, only: [:show, :edit, :destroy, :update, :close]
def show
#ticket_comment = TicketComment.new
end
def ics_export
tickets = Ticket.all
respond_to do |format|
format.html
format.ics do
cal = Icalendar::Calendar.new
tickets.each do |ticket|
event = Icalendar::Event.new
event.dtstart = ticket.start
event.description = ticket.summary
cal.add_event(event)
end
cal.publish
render :text => cal.to_ical
end
end
end
private
def set_ticket
#ticket = Ticket.find(params[:id])
end
And when I click the link, it takes me to /tickets/calendar.ics which is correct but I get the following error:
ActiveRecord::RecordNotFound in TicketsController#show
Couldn't find Ticket with 'id'=calendar
Extracted source (around line #83):
private
def set_ticket
#ticket = Ticket.find(params[:id])
end
The #ticket = Ticket.find(params[:id]) is highlighted. Which make sense that it is failing to call a ticket with an id of calendar.
Request has parameters:
{"id"=>"calendar",
"format"=>"ics"}
How do I fix this error? Why is it calling the show action?
There is a footnote in the canonical Rails Routing from the Outside In to the effect:
Rails routes are matched in the order they are specified, so if you have a resources :photos above a get 'photos/poll' the show action's route for the resources line will be matched before the get line. To fix this, move the get line above the resources line so that it is matched first.
As commented, the fix is to specify get 'tickets/calendar' => ... ahead of resources :tickets. If the order of routes is in question, you can run rake routes, which, to the best of my knowledge, should render your routes in the order they are checked.
Hey I am trying to namespace a bunch of controllers that I only want admins to be able to access. For example, I want routes like admin/products or admin/categories, but when I call any of those controllers located in my controllers/admin folder, I get the following error message
superclass mismatch for class CategoriesController
If I restart the server right after, I get this
Unable to autoload constant Admin::CategoriesController
and
Circular dependency detected while autoloading constant Admin::CategoriesController
These are my routes
Rails.application.routes.draw do
root 'pages#home'
devise_for :admins
namespace :admin do
resources :categories, :except => [:new, :show]
resources :products
end
resources :products
resources :carts, :only => [:show]
resources :line_items, :only => [:create, :destroy]
# Shop controller
get 'shop/index
# Admin controller
get 'admin/index'
This is my categories controller
class CategoriesController < ApplicationController
before_filter :authenticate_admin!
def index
#categories = Category.all
#category = Category.new
end
def create
category = Category.new(categories_params)
if category.save
flash[:notice] = "You have added a new category"
redirect_to categories_path
else
flash[:error] = "An error occured"
render "index"
end
end
def edit
#category = Category.find(params[:id])
end
def update
#category = Category.find(params[:id])
if #category.update(categories_params)
flash[:notice] = "Succesfully updated #{#category[:name].titleize}"
redirect_to categories_path
else
flash[:error] = "An error occured trying to update #{#category[:name].titleize}"
render "edit"
end
end
def destroy
#category = Category.find(params[:id])
if #category.destroy
flash[:notice] = "You succesfully removed #{#category.name}"
else
flash[:error] = "An error occured trying to remove #{#category.name}"
end
redirect_to categories_path
end
private
def categories_params
params.require(:category).permit(:name)
end
end
This has been troubling me for some time now so any help will be greatly appreciated
You forgot to namespace the Controller class :
class Admin::CategoriesController < ApplicationController
which is why you receive Unable to autoload constant Admin::CategoriesController error as Rails is looking for namespaced class Admin::CategoriesController and what you have is CategoriesController