Rails 3 routes double check on match - ruby-on-rails

currently, I have this on my routes.rb
match '/:username' => 'profiles#show', :as => :public_profile
now I want to use this format, and add another parameter to make another match for another route.
I don't know how to explain this but let me give you an example.
Suppose with that match above:
http://example.com/foo_username
this connects to the profile of foo_username.
Now I want to have a url like this:
http://example.com/foo_username/5-my-story
This link should go to the stories#show action with 5-my-story as its id (slug), and this story is owned by the username provided in the path. How do I configure that in routes?
I tried this one:
match '/:username/:id' => 'stories#show', :as => public_story
It works, but when I change the parameter of the username, it still goes to the story. It should return a route not found because the username does not own the story.
How do I best implement this?

In your controller, you'll have something like this:
#story = Story.find(params[:id])
You need to restrict this to the user's stories maybe like this
#user = User.find_by_username( params[:username] )
#story = #user.stories.find(params[:id])
You then have to check if #story is nil, and return a 404 if not.

Adapt this to suit your needs:
# stories_controller.rb
def show
#story = Story.find(params[:id])
raise ActiveRecord::RecordNotFound unless #story.user.name == params[:username]
# ...
end

Related

Getting param from friendly url like http://localhost:3000/user/adem-balka with Rails 5

So I want to display posts for an user in his/her profile page as at top user details, below all the posts.
I know I can get param from a url like http://localhost:3000/posts?category=article
with
if params[:category]
#category_id = Category.find_by(title: params[:category]).id
#posts = Post.where(category_id: #category_id)
end
but param doesn't work when I have an url like http://localhost:3000/user/adem-balka
So, how can I get user name to find its id and pull posts with that user id?
Thank you all.
The name of a parameter in a url is set in your routes file.
If you look at your routes in config/routes.rb, you should be able to find the line(s) that corresponds to the user model. It should look something like this:
get '/users/:name', to: 'users#show'
This means that if you go to /users/adem-balka, params[:name] will be set to 'adem-balka'. You can then access the parameter in the corresponding controller function.
What you are looking for is a path parameter, where adem-balka is say params[:username].
Assuming you have no forward slashes or dots in your parameter, this is as simple as adding /:username as part of your route, e.g.
get '/users/:username', to: 'users#show'
# in the controller
#user = User.find_by(username: params[:username])
This is all covered in the Rails Routing from the Outside In guide.
Note that the routes generated resources already contain the :id path parameter for you (for show, edit, etc.). But even if you change the controller, the generated helpers (e.g. users_path(#user)) will use the id.
To make it work with resources using say username instead of id however (e.g. users_path(#user) giving /users/ben instead of /users/5), you need to also override the to_param method, e.g.
class User < ApplicationRecord
def to_param
username #rather than id
end
end
# routes.rb
get '/users/:username' => 'users#posts'
# users_controller.rb
def posts
username = params[:username]
# etc..
end
This is described in the Rails Docs as Routing Parameters.
thank you for the answers. I learned from them 🙏
and this solved my problem
#user_id = User.friendly.find(params[:id])
#posts = Post.where(user_id: #user_id)

Add new route example.com/username while preserving existing routes

Is there a way to have example.com/username while preserving example.com/users/1 route?
To put simply, I just need to link to users's profile via username.
You can add an arbitrary route like this using:
get "/:username" => "users#show", as: :username
This will pass the username as a parameter to the show action.
Then in your views:
<%= link_to user.name, username_path(username: user.username) %>
You'll also need a controller action that knows how to handle the username parameter. If you want to use your existing users controller and preserve your /users/:id URLs too, you could do something like:
def show
#user = if params[:username].present?
User.find_by_username(params[:username])
else
User.find(params[:id])
end
end

Make pretty urls in rails. Change /users/ to a database field

Right now, my urls are being rendered as
/users/2/products
I know i can use friendly_id to have it render as
/users/username/products
But I dont want that. In my database User, there is a field role, which has entries like - worker, janitor and so on. So, I want to have
/users/2/products
as
/worker/username/products or /janitor/username/products and so on..
How do i do it ? Can i use friendly_id to do it ?
I don't know friendly_id. I can't even be sure of what you are asking, but something tells me you want to define a custom route.
Adding a custom route
in your route file, you can add
match "/worker/:username/products" => "users#show"
and in the action, check for :username parameter variable.
if params[:username]
#user = User.find_by_name(params[:username])
else
#user = User.find(params[:id])
end
Routes for your specific case
In your case, it seems that you want this in your route.rb:
match "/:role/:username/products" => "users#show"
and this in your action to use the params:
User.where("role = '?' and username = '?', pararms[:role], params[:username])
The route will make accessing a URL like /worker/fotanus/products/ end up in the action User#show with params[:role] = 'worker' and params[:username] = 'fotanus'.

Masking controllers functions -- Rails3

I would like to hide or mask example.com/users/$ID/edit to be example.com/profile/edit.
config/routes.rb
resources :users
app/controllers/users_controller.rb
def edit
#user = User.find(params[:id])
end
I've tried adding the route
match '/profile/edit' => 'users#edit', :as => :edit_profile
but, when I visit example.com/profile/edit, the edit method complains about not being able to find the user's ID.
Is there a way I can mask the ID from the browser?
If you're trying to edit the current user's profile you could use the following in your edit action:
def edit
#user = User.find_by_id( params[:id] ) || current_user
end
This will first try to look up the user by the id parameter returning nil if it can't find the user and if the return value is nil it will set #user to the return value of the current_user helper method. This assumes you're using something like Devise which provides the current_user method to get the currently logged in user.
One other note. You should change match in your route to get to specify that only get requests are valid for the edit action.
get 'profile/edit' => 'users#edit' , as: edit_profile
If you're trying to prevent users from editing other people's profiles you need something like the following after you load the user:
redirect_to( root_path ) and return unless #user == current_user
This will keep the current user from editing another user's profile.
If you want to allow logged in user to change it's profile, maybe you'll want to store user's id in the session.
So when user logs in you save it's ID into the session:
session[:user_id] = ...
Then your edit method will look like
def edit
#user = User.find(session[:user_id])
end
If that solution is not what you want, than you maybe will just need to add the ':id' parameter to the match code.

Rails route dependent on current user

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"

Resources