Adding :user_id to a resource (rails3 routing) - ruby-on-rails

I am trying to set up routes for the "Page" model nested within the "User" model. Only "Pages" needs to be resourcesful. (User has_many pages)
This is the original way I did it:
resources :users, path: '', only: [] do
resources :pages, path: ''
end
The above worked for me just fine. The routes I got were these:
user_pages GET /:user_id(.:format) pages#index
POST /:user_id(.:format) pages#create
new_user_pages GET /:user_id/new(.:format) pages#new
edit_user_pages GET /:user_id/:id/edit(.:format) pages#edit
user_pages GET /:user_id/:id(.:format) pages#show
PUT /:user_id/:id(.:format) pages#update
DELETE /:user_id/:id(.:format) pages#destroy
This made sense for me because /john-doe/page1 would be user_pages_path(#user, #user.pages.first).
However, new_user_pages didn't make sense because a user can only make a page for himself/herself. Therefore, each user should visit /new, not "/:user_id/new". Furthermore, what happens if the user visits another user's ":another_user_id/new" ? (it would make more sense to do new_pages_path and '/new' instead of new_user_pages_path and /:user_id/new).
Another way I tried to do the above routing:
I also realized the above can be accomplished in a shorter way due to the fact that ":users" does not need to be resourceful:
resources :pages, path => :user_id
However, this resulted in paths w/o "user" in them:
pages GET /:user_id(.:format) pages#index
POST /:user_id(.:format) pages#create
new_pages GET /:user_id/new(.:format) pages#new
edit_pages GET /:user_id/:id/edit(.:format) pages#edit
pages GET /:user_id/:id(.:format) pages#show
PUT /:user_id/:id(.:format) pages#update
DELETE /:user_id/:id(.:format) pages#destroy
What is the "rails" way of doing this? Also, should I remove "new" from the resource and define it separately?
Also, does it make sense to use scope or namespace instead?
Thanks,
Nick

Why don't you want your routes to follow the new_user_pages_path format? There might be reasons for doing it the way you want that I'm not aware of, but for me, it's a lot easier when all of my nested resource paths follow the same format.
To answer your question about preventing a user from visiting /:another_user_id/new, you can prevent this by adding a before_filter to make sure that the id getting passed to the new action matches the id of the user that is currently logged in.
This is what I'm using in the app I'm building currently:
things_controller.rb
class VendorsController < ApplicationController
#other filters
before_filter :correct_user
#controller methods
private
def correct_user
if params[:user_id]
#user = User.find(params[:user_id])
unless #user && current_user == #user
redirect_to(root_path)
end
else
redirect_to(root_path)
end
end
end
Of course, you'll need a current_user method for this to work (mine comes from devise).

Related

Rails redirect_to creates wrong URL

Haven't been able to find anything specific to this issue (other searches deal with forms and such). It's probably a simple oversight on my part. But what on earth am I missing?
GOAL: I'm simply trying to redirect from the /login page URL to the /dashboard URL if a session exists.
EXPECTED OUTCOME: Calling redirect_to dashboard_index_url or redirect_to '/dashboard' should go to https://mydomain/dashboard
CURRENT OUTCOME: if I go to https://mydomain after creating a session it redirects me to https://mydomaindashboard, note the missing slash
ATTEMPTED SOLUTIONS:
Manually type the URL https://mydomain/dashboard after creating a session, RESULT: works, so the proper route seems to exist
Make manual route in routes.rb, RESULT: behavior is exactly the same as resource routing with the missing slash
Clear browser cache, use different browswers RESULT: all exhibit same behavior
Here's what I have (abbreviated to relevant parts):
class LoginController < ApplicationController
def index
redirect_to dashboard_index_url if session[:user_id]
end
#...
end
class DashboardController < ApplicationController
before_action :require_login # calls redirect_to root_url unless session[:user_id]
def index
#...
end
end
# In routes.rb:
resources :login
resources :dashboard
# have also tried things like (removed the above line for these)
get 'dashboard' => "dashboard#index"
#Ryan Here is the current output for the routes:
$ rake routes
Prefix Verb URI Pattern Controller#Action
login_index GET /login(.:format) login#index
POST /login(.:format) login#create
new_login GET /login/new(.:format) login#new
edit_login GET /login/:id/edit(.:format) login#edit
login GET /login/:id(.:format) login#show
PATCH /login/:id(.:format) login#update
PUT /login/:id(.:format) login#update
DELETE /login/:id(.:format) login#destroy
dashboard GET /dashboard(.:format) dashboard#index
dashboard_index GET /dashboard(.:format) dashboard#index
POST /dashboard(.:format) dashboard#create
new_dashboard GET /dashboard/new(.:format) dashboard#new
edit_dashboard GET /dashboard/:id/edit(.:format) dashboard#edit
GET /dashboard/:id(.:format) dashboard#show
PATCH /dashboard/:id(.:format) dashboard#update
PUT /dashboard/:id(.:format) dashboard#update
DELETE /dashboard/:id(.:format) dashboard#destroy
root GET / login#index
SOLVED:
Well, I found the problem and it was not actually Rails, it was Apache-Passenger. There was a config with a redirect (so that all HTTP get redirected to HTTPS) at the apache level that didn't have the trailing slash floating around and causing trouble. (smacks forehead)
Gotta love those red herrings. Thanks so much guys for the quick help!
for use dashboard_index_url, you have to write it in your routes file. However as you've created in routes.rb a dashboard resource, a dashboards_url is available to you and it leads to /dashboards/index
Other way to nail this task is create a mapping
get 'dashboard' => "dashboard#index" as: :dashboard_index
and dashboard_index_url and dashboard_index_path will be available for you
Update 1
Please, look on this SO question
the problem is that path give you relative route and url - absolute. that's why you don't have additional slash in your path. Try path instead of url and it should work.
Update 2
try dashboard_index_path
How about changing your routes to
get '/login', to: redirect('/dashboard')
You can even tailor the resulting link to something more specific to your user by
get '/login', to: redirect('/dashboard'), as: '/dashboard/user[:username]' if you need to.
On a side-note, you should try cleaning up your routes as you go. There are a lot there that you are not using. Change it to
resources :dashboard, only: [:index] and add to it as you need. E.g only: [:index, :show] etc. If another developer was needed to modify your app one day we'd look at your routes and perhaps make incorrect assumptions about what we can do.
Justin

Allowing user to edit account without the :id param in the url

Hi I'd like to accomplish having a URL that's
users/edit
instead of the current
users/7/edit
I have my own auth system built upon omniauth. Thus I store their user_id in a session. How would I go about accomplishing this task?
Assuming that you use current_user, even if you are using something else just replace current_user with your method, I am using current_user here, follow these steps,
Create an action, I would name it edit_user in your users controller
def edit_user
#user = current_user # or User.find(session[:user_id])
end
Add routes to routes.rb
get "/users/edit" => "users#edit_user"
You are done, you can use the above route anywhere in the application, you can also name the route if needed.
OR, if you don't want to define a new action and want to use the existing edit action, do this
Remove routes for edit from the default resources routes and then manually define it. In this way, you can use the existing edit action
resources :users, except: [:edit]
get "/users/edit" => "users#edit"
Hope this helped!

Routes Leading me to a Different Action

I have a routes folder
resources :groups
In my GroupsController here are some actions
def new
#group = Group.new
end
def update
end
def show
#groups = Group.find(params[:id])
end
When I run rake routes I see
new_group GET /groups/new(.:format) groups#new
So now in my html page I have
a.btn.btn-primary href="/groups/new"
The funny thing is whenever I click on the link, it tells me
No route matches {:action=>"show", :controller=>"groups"}
I actually do have a route match for the show action. I also checked and do not have any filters that are redirecting me to the show action in my GroupsController. I have no idea why it's redirecting me to show. What's wrong and how do I fix this? I must be missing something obvious.
UPDATE
If I go and remove the show action from the resources method like so
resources :groups, except: [:show]
Then it tells me that it could not find an update action.
SOLVED
I found out in my new.html page there was a call to group_path, when in reality it should groups_path
I found out in my new.html page there was a call to group_path, when in reality it should groups_path.

How do you define per user routes in Rails?

I want to create per user views in Rails and am trying to figure out the best way to define them in routes.rb. Say I wanted to have the following type of pathnames:
/users/1/event_list_view
/users/1/event_map_view
/users/1/aggregated_event_view
So the endpoint that comes after the '/users/1/' is not a resource, it's just another endpoint in the controller:
class UsersController < ApplicationController
def event_list_view
end
def event_map_view
end
def aggregated_event_view
end
end
I tried a few different things, and currently have the following, but can't seem to get it to work:
resources :users do
match '/users/:id/event_list_view' => {:action=>"event_list_view", :controller=>"users"}
match '/users/:id/event_map_view' => {:action=>"event_map_view", :controller=>"users"}
match '/users/:id/aggregated_event_view' => {:action=>"aggregated_event_view", :controller=>"users"}
end
Let me know what I'm doing wrong. Thanks!
Member routes should allow you to do what you want:
resources :users do
member do
get 'event_list_view'
get 'event_map_view'
get 'aggregated_event_view'
end
end
I'm not sure what exactly you're trying to do, but it might also be worth considering alternative resource arrangements. If you're trying to show all the events associated with a user, it might make better sense to define an index route for the Event resource (etc, etc) and pass a user parameter there.

route works one place, not others

This is kind of difficult to communicate but I'll try without pasting all my code. I have Members who have one Mailbox which has many Receipts. In the header layout I have a nav that calls
<%= link_to "Message Center", member_mailbox_path(current_user.member_id) %>
It works on most pages like trails/# , the resource pages for various models
But on other pages, seems like custom route pages, I get this error
No route matches {:action=>"show", :controller=>"mailbox", :member_id=>16}
Running rake routes shows this:
member_mailbox GET /members/:member_id/mailbox/:id(.:format) mailbox#show
Routes are confusing to me, here are my routes for this problem (show message isn't tested yet) ...
resources :members do
resources :mailbox do
resources :receipts do
member do
get :show_message
end
end
end
end
The routes for the pages that are showing the error are similar to this one
match '/my_plays', :to => "trails#my_plays"
match '/my_creations', :to => "trails#my_creations"
So not sure if my routes are right. I wonder if resources :mailbox is correct since I don't have a bunch of resources for that, it's a has_one .... THX
----EDIT--- after changing route per advice:
member_mailbox POST /members/:member_id/mailbox(.:format) mailboxes#create
new_member_mailbox GET /members/:member_id/mailbox/new(.:format) mailboxes#new
edit_member_mailbox GET /members/:member_id/mailbox/edit(.:format) mailboxes#edit
GET /members/:member_id/mailbox(.:format) mailboxes#show
PUT /members/:member_id/mailbox(.:format) mailboxes#update
DELETE /members/:member_id/mailbox(.:format) mailboxes#destroy
You may want to define a mailbox as a singular resource in your routes. Otherwise, Rails will expect you to pass in both the user id and the mailbox id for member_mailbox_path to route to mailbox#show. I believe this is why you're getting a routing error. Since each user has one mailbox, there's no need to make this extra lookup part of the route. So instead of resources :mailbox, you can do resource :mailbox:
resources :members do
resource :mailbox do
resources :receipts do
member do
get :show_message
end
end
end
end
I believe this would generate the following routes:
member_mailbox POST /members/:member_id/mailbox(.:format) mailboxes#create
new_member_mailbox GET /members/:member_id/mailbox/new(.:format) mailboxes#new
edit_member_mailbox GET /members/:member_id/mailbox/edit(.:format) mailboxes#edit
GET /members/:member_id/mailbox(.:format) mailboxes#show
PUT /members/:member_id/mailbox(.:format) mailboxes#update
DELETE /members/:member_id/mailbox(.:format) mailboxes#destroy
Notice that the lack of path names next to GET, PUT, and DELETE doesn't mean they don't exist; they're just repeats of the POST path, but each responds to different HTTP methods.
To render mailboxes#show, you'll need to add a MailboxesController with a show route, which might do a look up for the member:
class MailboxesController < ApplicationController
def show
#member = Member.find(params[:member_id])
# other mailbox code...
end
end
And you'll also create a template at app/views/mailboxes/show.html.erb to render the mailbox show page.
Also, I would recommend against deeply nesting your routes, as in third level :receipts.

Resources