I am wondering what would be the best practise and approach to handle routing error for specific controller?
In my case i have 2 resources
resources :user
resources :special_user
I want that everything that throws routing error redirects back to that resources index, for example:
request to mydomain.com/users/blahblah, will redirect to mydomain.com/users
and same for special user mydomain.com/special_users/blahblah will redirect back to mydomain.com/special_users
What would be the best approach for this?
You can do:
resources :users do
# if you need to add new routes, add them before the catch all
get '*a', to: redirect('/users')
end
resources :special_users do
# if you need to add new routes, add them before the catch all
get '*a', to: redirect('/special_users')
end
An alternative is to do:
resources :special_users do
# if you need to add new routes, add them before the catch all
get '*a', action: :catch_all
end
and in your special_users controller, you defined the action:
def catch_all
# maybe set some flash message
redirect_to special_users_path
end
Thanks to #JiříPospíšil 's comment I realize you may have one more thing to check.
In your show actions (for users for instance):
def show
#user = User.find_by(id: params[:id])
if #user
# usual stuff here
else
# flash message?
redirect_to users_path
end
end
(I assumed you forgot to pluralize your resources)
Related
could one advise me how to get a url like this in rails
http://www.example.com/users/5/ian
i tried the below but unsure:
route file:
devise_for :users
resources :users do
resources :socials
end
get '/users/:id/:firstname', controller: 'users', action: 'show'
users_controller.rb
def show
#user = User.find(params[:id], params[:firstname])
end
If you are trying to achieve 'friendly urls' then I suggest using this:
You don't have to create a special route:
get '/users/:id', controller: 'users', action: 'show'
Instead you have your model overwrite the to_param method:
class User
...
def to_param
"#{id}-#{firstname.try(:parameterize)}"
end
...
end
The url helper calls to_param to build the urls. If you overwrite it this way, you will receive a url like this:
http://localhost:3000/users/1-artloe
The rails find method calls .to_i on the params[:id] which, thankfully, interprets strings as number until it arrives at a character that can't become a number.
Examples:
'123abcde'.to_i # 123
'123-asdf'.to_i # 123
'asdf-123'.to_i # 0
So except for overwriting to_param, you don't have to do anything.
Try replacing this
def show
#user = User.find_by_id_and_firstname(params[:id], params[:firstname])
end
If what you are trying accomplish is "friendly urls" you would do it by:
# GET /users/1
# GET /users/joe
def show
#user = User.find_by!('id = :x OR firstname = :x', x: params[:id])
end
However you must ensure that property you are using in URLs is URL safe and unique. Usually a separate username or slug field is used.
Nothing special is needed in terms of routes.
These gems provide "friendly urls":
stringex
friendly_id
In my Rails routes.rb file I'm wanting to do something like the following.
get '/:id' => 'pages#show'
get '/:id' => 'articles#show'
So that if a visitor types in
http://www.example.com/about-this-site
The pages controller in the above example would get first shot at handling it. Then if not, the next controller in line would get a shot.
REASONs for wanting to do this:
1) I'm trying to port my Wordpress site over without establishing new urls for all my pages and blog posts. As it stands, all of my blog post files and pages are accessed directly off the root uri '/' folder.
2) Because I'm not able to, it's a learning thing for me. But, I want to do it without a hack.
How about redirecting to the second controller from your first controller?
in PagesController
def show
unless Page.find_by(id: params[:id])
redirect_to controller: :articles, action: :show, id: params[:id]
end
end
in ArticlesController
def show
# Handle whatever logic here...
end
Edit
If you really don't want to redirect then you can consolidate the logic into a single action:
def show
if Page.find_by(id: params[:id])
render :show
elsif Article.find_by(id: params[:id])
render controller: :articles, action: :show
else
# Handle missing case, perhaps a 404?
end
end
However, I'd recommend using a redirect if possible. It's a cleaner solution and keeps your controller code isolated.
Hey guys i probably have a simple problem which annoys me for 2 hours now.
I try to set up a menu_item_icon which is linked to one of my controller actions.
So far every of these menu_items work. But there is one where I always get the failure message 'Controller_Action path not found' and I am wondering why this is happening.
Here are some code-snippets from
a) The definition of the controller_action itself
b) The route in the routes.rb
c) The menu_item_icon link on some of my views
a) Definition of action in controller sells_controller.rb
def manage_sell
#stored_sells = SaveSell.all
respond_to do |format|
format.html{render 'manage_sells',:layout=>false}
end
end
b) The route for action manage_sell in my routes.rb
resources :sells, :only=>[:show,:new,:create] do
[...]
get :manage_sell, :on=>:collection
[...]
end
c) menu_item_icon link inone of my views
[...]
=menu_item_icon('m_sells','Manage Sells'),sells_manage_sell_path
[...]
So what is going wrong?
The rake routes gives the name you have to use, so in your case you have to write manage_sell_sells_path.
I do have a comment regarding naming: I would prefer the simpler manage and then it would all make sense. If you define the route to be on a member, the path would be manage_sell_path.
So my guess is your route definition should be
resources :sells, only: [:show, :new, :create] do
get :manage, on: :member
end
as the naming now seems to imply you are "managing" a single sell.
I think it will be
manage_sell_sells_path
instead of
manage_sells_sell_path
Because your action is
manage_sell
and controller is
sells
Though there is no action named 'manage_sells' it raise no 'Controller_Action path not found' errors
I have three roles: Instuctor, Student, Admin and each have controllers with a "home" view.
so this works fine,
get "instructor/home", :to => "instructor#home"
get "student/home", :to => "student#home"
get "admin/home", :to => "admin#home"
I want to write a vanity url like below which will route based on the role of the user_id to the correct home page.
get "/:user_id/home", :to => "instructor#home" or "student#home" or "admin#home"
How do I accomplish this?
I'm providing an alternate approach as this SO question comes up near the top when searching for role based routing in Rails.
I recently needed to implement something similar but wanted to avoid having a large number of conditionals in the controller - this was compounded by the fact that each of my user roles required completely different data to be loaded and presented. I opted to move the deciding logic to the routing layer by using a Routing Constraint.
# app/constraints/role_route_constraint.rb
class RoleRouteConstraint
def initialize(&block)
#block = block || lambda { |user| true }
end
def matches?(request)
user = current_user(request)
user.present? && #block.call(user)
end
def current_user(request)
User.find_by_id(request.session[:user_id])
end
end
The most important part of the above code is the matches? method which will determine whether or not the route will match. The method is passed the request object which contains various information about the request being made. In my case, I'm looking up the :user_id stored in the session cookie and using that to find the user making the request.
You can then use this constraint when defining your routes.
# config/routes.rb
Rails.application.routes.draw do
get 'home', to: 'administrators#home', constraints: RoleRouteConstraint.new { |user| user.admin? }
get 'home', to: 'instructors#home', constraints: RoleRouteConstraint.new { |user| user.instructor? }
get 'home', to: 'students#home', constraints: RoleRouteConstraint.new { |user| user.student? }
end
With the above in place, an administrator making a request to /home would be routed the home action of the AdministratorsController, an instructor making a request to /home would be routed to the home action of the InstructorsController, and a student making a request to /home would be routed to the home action of the StudentsController.
More Information
If you're looking for more information, I recently wrote about this approach on my blog.
You can't do this with routes because the routing system does not have the information required to make this decision. All Rails knows at this point of the request is what the parameters are and does not have access to anything in the database.
What you need is a controller method that can load whatever data is required, presumably the user record, and redirects accordingly using redirect_to.
This is a fairly standard thing to do.
Update:
To perform all of this within a single controller action you will need to split up your logic according to role. An example is:
class HomeController < ApplicationController
def home
case
when #user.student?
student_home
when #user.admin?
admin_home
when #user.instructor
instructor_home
else
# Unknown user type? Render error or use a default.
end
end
protected
def instructor_home
# ...
render(:template => 'instructor_home')
end
def student_home
# ...
render(:template => 'student_home')
end
def admin_home
# ...
render(:template => 'admin_home')
end
end
I'd like to create a rails route for editing a user's profile.
Instead of having to use /users/:id/edit, I'd like to have a url like /edit_profile
Is it possible to create a dynamic route that turns /edit_profile into /users/{user's id}/edit, or should I do thing in a controller or?
You might want to create a separate controller for this task but you could also continue using users_controller and just check whether there is a params[:id] set:
def edit
if params[:id]
#user = User.find(params[:id])
else
#user = current_user
end
end
But you should note that /users normally routes to the index action and not show if you still have the map.resources :users route. But you could set up a differently called singular route for that:
map.resources :users
map.resource :profile, :controller => "users"
This way /users would list all the users, /users/:id would show any user and /profile would show the show the currently logged in users page. To edit you own profile you would call '/profile/edit'.
Since a route and controller serve two different purposes, you will need both.
For the controller, assuming you're storing the user id in a session, you could just have your edit method do something like:
def edit
#user = User.find(session[:user_id])
end
Then have a route that looks something like:
map.edit_profile "edit_profile", :controller => "users", :action => "edit"
This route would give you a named route called edit_profile_path
Tomas Markauskas's answer could work, but here's the answer to your question from the Rails Guide:
get 'edit_profile', to: 'users#edit'
So, when someone goes to www.yoursite.com/edit_profile, it will route to www.yoursite.com/users/edit.
Then, in your controller you can access the user with
#user = User.find(session[:current_user_id])
Assuming you set that session variable when someone logs in. Also, don't forget to check if they're logged in. This will work if your using Resourceful Routing (the Rails default) or not.
Source: http://guides.rubyonrails.org/routing.html
make the route as
get '/users/:id/edit', to: 'users#edit', as: 'edit_profile'
As explained in this link section 'The hard way' :
http://augustl.com/blog/2009/styling_rails_urls/
The url will be
/users/edit_profile
Because the ID is no longer in the URL, we have to change the code a bit.
class User < ActiveRecord::Base
before_create :create_slug
def to_param
slug
end
def create_slug
self.slug = self.title.parameterize
end
end
When a user is created, the URL friendly version of the title is stored in the database, in the slug column.
For better understanding read the link below
http://blog.teamtreehouse.com/creating-vanity-urls-in-rails
write it in any home controler.
def set_roots
if current_user
redirect_to dashboard_home_index_path
else
redirect_to home_index_path
end
end
in routes.rb file
root :to => 'home#set_roots'
match "/find_roots" => "home#set_roots"