Rails route dependent on current user - ruby-on-rails

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"

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)

Change the url based on current_user - ROR

Say for instance I have a posts controller that currently has a method user_posts which shows all of the posts that are associated with the user with the associated id as so:
def user_posts
#user = User.find(params[:id])
#posts = #user.posts.all
end
I want the url to be: foo.com/my_posts when the posts have the same ID as my current_user; How would I do this? currently my routes are set up as so:
get 'user/posts/:id', to: 'posts#user_posts', as: 'user/posts'
I know that I could create an entirely new controller action for my_posts but I want to know if there is a way to do it in the config/routes.
If for example I am browsing throughout the site and tap on a link that says "user posts" I would expect to go the the users posts and if that user happens to be me I would like the url to show website.com/my_posts
If I understand well, you have a list of users (including the currently connected user) and each has a link 'user posts' to see the user's posts.
You can simply do:
views
In your views, change the user post link according to the user id. As you loop through your users, check if the user's id is the same as the currently logged user. If yes, change the link to the /my_posts route as follow:
<% if user.id == current_user.id %>
<%= link_to "My posts", my_posts_path %>
<% else %>
<%= link_to "User posts", user_posts_path(user) %>
<% end %>
routes.rb
Add a my_posts route that points to the same controller method as user/posts.
get 'user/posts/:id', to: 'posts#user_posts', as: 'user/posts'
get 'my_posts', to: 'posts#user_posts', as: 'my_posts'
controller
In your controller method, we need to instantiate the #user to get its posts. If there is no :id in the params (like the /my_posts route), then set the #user to the current_user. If an :id is passed, set the #user by fetching it from the db.
def user_posts
#user = params[:id].present? ? User.find(params[:id]) : current_user
#posts = #user.posts.all
end
No need to do checking in the routes.rb file. This is simple and more "Rails" like.
Is this what you are looking for?
As I know - no. It's possible to create in routes redirect route and check some conditions (example from documantation):
get 'jokes/:number', to: redirect { |params, request|
path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
"http://#{request.host_with_port}/#{path}"
}
But it's impossible to check current user in routes. Redirect can be implemented in the controller with two separate actions as mentioned.
Also available a little trick - generate from the beginning 'right' routes if you use html.erb (slim/haml). For current user posts link can be generated not as usual user/posts/:id but /my_posts (it's possible to check current user id without any problems) and define two routes:
get 'user/posts/:id', to: 'posts#user_posts', as: 'user/posts'
get 'my_posts', to: 'posts#user_posts', as: 'my_posts'
In controller check request.path to find user:
user = request.path == '/my_posts' ? current_user : User.find(params[:id])
Hope it helps.
I'm guessing you didn't want to use the index method of the posts controller because you were using it to show all posts from all users, but you can still use it. Here's how:
class PostsContoller < ApplicationController
def index
#posts = if params[:user_id].present?
User.find(params[:user_id]).posts
else
Post.all
end
end
end
Then in your routes file do this:
resources :posts
resources :users do
resources :posts
end
This allows posts to be a first class resource as well as a nested resource. Now when you go to /posts/ you get all posts, but when going to /users/:user_id/posts you get only posts from the given user.
In your app, when you need to link to all posts from all users, you can do
posts_path
and when you need to link to just a user's posts you can do
user_posts_path(user)

(Rails) How to get 'id' out of edit url

I have a model called studies.
After action redirect redirect_to edit_study_path(#new_study),
URL: http://localhost:3000/studies/2/edit.
Is there anyway to customize an url after passing id ?
For example, http://localhost:3000/study
(still going to the edit path, and still with the :id in the params)
I guess what you want is to edit the current study?
In this case, it's possible, using ressource instead of ressources in the routes.
Let's have an example:
#in routes.rb
resources :studies
resource :study
Both of them will by default link to the StudiesController and call the same actions (eg. edit in your case) but in two different routes
get "/studies/:id/edit" => "studies#edit"
get "/study/edit" => "studies#edit"
in your edit action, you should then setup to handle correctly the parameters:
def edit
#study = params[:id].nil? ? current_study : Study.find(params[:id])
end
Note you need a current_study method somewhere, and store the current_study in cookies/sessions to make it works.
Example:
# In application_controller.rb
def current_study
#current_study ||= Study.find_by(id: session[:current_study_id]) #using find_by doesn't raise exception if doesn't exists
end
def current_study= x
#current_study = x
session[:current_study_id] = x.id
end
#... And back to study controller
def create
#...
#Eg. setup current_study and go to edit after creation
if study.save
self.current_study = study
redirect_to study_edit_path #easy peesy
end
end
Happy coding,
Yacine.

Rails root path based on user intput

I need to let the user select a root path (or a get) to be one of many pages he has created. That means, i want to be able to created the page "fantastic page" and set it to be the front page (http://example.com/).
Is this at all possible? I have tried sending the user to a fixed action and redirecting the user to the selected action but that leaves me with a route like this: http://example.com/page/8
My desired result is: http://example.com/
(while the page is the one selected by the user).
Any help is appreciated :)
Edit: I need to clarify. I want the user to be able to select a page or a photo. That means I need the user to be able so select both the controller and the action.
Something like this:
constraints(Subdomain) do
get "/" => '#{user_selected_controller}#{user_selected_action}/#{:id}'
end
The code above is completely wrong. But i think it illustrates what i want to accomplish.
You should explicitly render the page view from the controller.
config/routes.rb
root to: 'home#index'
controllers/home_controller.rb:
def index
#page = #get fantastic page
render 'pages/page', page: #page
end
See http://guides.rubyonrails.org/layouts_and_rendering.html#rendering-an-action-s-template-from-another-controller.
I Solved this problem not through routing but by creating a polymorphic association to both Pages and Galleries with the users table. Like this:
First create polymorphic association
# In my page model
has_one :user, :as => :home
# In my Gallery model
has_one :user, :as => :home
# In my User model
belongs_to :home, polymorphic: true
Then i used this as my controller in the users_controller:
def home
# Multitenant rails app. This refers to user who owns the subdomain visited.
#user = current_tenant
# This is not optimal, results in two queries.
if #user.home_type.classify.constantize.exists?(#user.home_id)
# This is the same as #page = Page.find(params[:id])
instance_variable_set("##{#user.home_type.downcase}", #user.home_type.classify.constantize.find(#user.home_id))
# this is the same as render 'pages/show', layout: false
render "#{#user.home_type.downcase.pluralize}/show", layout: false
else
redirect_to photos_path
end
end
My routes look like this:
# Set a page as the home screen
post 'pages/set_home/:id' => 'pages#set_home', as: :set_home_page
# User selected Home screen
get "/" => 'users#home'
Setting a home page:
# In pages_controller
def set_home
#user = current_user
#user.home = #page
if #user.save
redirect_to pages_path, notice: "Home screen set to #{#page.title}"
else
redirect_to pages_path, alert: "An error occured. Home not set."
end
end
This way i could load the relevant resource through the users.home variable, while using a normal route to the users controller home action.
This might be an inefficiently solution, but its the only one i could think of. Please feel free to suggest improvements :)

Rails: Route to custom controller action

I have a really hard time understanding routes and I hope someone can help me.
Here's my custom controller
class SettingsController < ApplicationController
before_filter :authenticate_user!
def edit
#user = current_user
end
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
end
and I have the file settings/edit.html.erb and my link
<li><%= link_to('Settings', edit_settings_path) %></li>
The route
get "settings/edit"
doesn't work for this, because then I get
undefined local variable or method `edit_settings_path' for #<#<Class:0x00000001814ad8>:0x00000002b40a80>
What route do I have to give this? I can't figure it out. If I put "/settings/edit" instead of a path it messes up as soon as I'm on a other resource page because the resource name is put BEFORE settings/edit
Thx
Following should do:
get 'settings/edit' => 'settings#edit', :as => :edit_settings
# you can change put to post as you see fit
put 'settings/edit' => 'settings#update'
If you use /settings/edit directly in link, you shouldn't have problem with other resource name being prepended in path. However, without the leading slash, i.e. settings/edit it might have that issue.
Reason why edit_settings_path is not working might be because you didn't declare a named route. You have to use :as option to define by which method you will be generating this path/url.
If you want to explicitly define the route, you would use something like
get 'settings/edit' => 'settings#edit', :as => edit_settings
This statement defines that when a GET request is received for setting/edit, call the SettingsController#edit method, and that views can reference this link using 'edit_settings_path'.
Take some time to read the Rails guide on routing. It explains routing better than any other reference out there.
Also keep in mind the rake routes task, that lists the details of all the routes defined in your application.

Resources