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.
Related
I have a controller Sagepay, which have number of custom method (not RESTfull resources). How can I write routes for those actions? So far I tried:
namespace :sagepay, controller: :sagepay, as: :sagepay do
post :notification
get :iframe_breaker
get :accept_payment
end
This however tries mapping to sagepay/sagepay_controller instead simple sagepay_controller.
Obviously I can do this:
match '/sagepay/notification' => 'sagepay#notification', via: :post, as: sagepay_notification
(etc)
which works, but this is not a solution I am looking for. Is there any way I can write it in common block?
The reason you get sagepay/sagepay_controller is the reason why namespaces are used. In order to get sagepay/notification you can define a resource and add collection routes within it, as follows:
# config/routes.rb
resource :sagepay, controller: :sagepay do
collection do
post :notification
get :iframe_breaker
get :accept_payment
end
end
Update:
In order to define only the routes defined within the collection and avoid definition of the seven restful routes that Rails create you can pass in only option to the resource definition as follows:
# config/routes.rb
resource :sagepay, controller: :sagepay, only: [] do
collection do
post :notification
get :iframe_breaker
get :accept_payment
end
end
I think I found the solution:
scope 'sagepay', controller: :sagepay, as: :sagepay do
post :notification
get :iframe_breaker
get :accept_payment
end
I have a simple problem where in a routes/url name is determined by a user role. Currently the route displayed is /new_admin/dispensaries. If the user has a role of either manager or executive then the named route should be '/dashboards/dispensaries'.
It's kind of simple but the hard part is that in my routes.rb:
namespace :new_admin do
resources :vendor_templates
resources :markdown_docs
resources :email_lists
namespace :moderation do
resources :reported_reviews
end
resources :users do
member do
get :user_bans
post :ban_unban, to: 'user_bans#create'
delete :ban_unban, to: 'user_bans#destroy'
end
end
# TODO - this should be written generically to support dispensary/doctors/whatever
get '/dispensaries/reviews', :to => "reviews#all", :as => :all_reviews
get '/dispensaries/pictures', :to => "pictures#all", :as => :all_pictures
get '/dispensaries/videos', :to => "videos#all", :as => :all_videos
get "/dispensaries/autocomplete", to: "dispensaries#autocomplete"
resources :vendors do
resources :ownership_transfers, only: [:new, :create]
end
...
I'm kind of stuck since if I change the new_admin routes, so many other routes will be affected. Any idea guys?
We've actually done something like this. It's not pretty, but this solution worked for us:
Slugs
You're basically alluding to a type of your routes called Slugs. This is where you use a name instead of an ID, allowing you to make a user-friendly route (such as /delivery/today). The problem is that in order to create these routes, you have to define them individually in the routes file
There are two Gems you can use to handle your slugged routes -- FriendlyID & Slugalicious. Both of these allow you to create slugged routes, but FriendlyID basically just changes the ID, whilst Slugalicious is a totally independent system
We used Slugalicious for the code below, however, you'll probably want FriendlyID (there's a RailsCast for it here):
Routing
The problem you have is that routes are outside the scope of the RESTful controller interface, which means you'll have to call all the routes exclusive of your resources references in the routes.rb file
If you use Slugalicious, it has its own Slugs database, which means we can use it to create the routes on the fly, like this:
#Slugs
begin
Slug.all.each do |s|
begin
get "#{s.slug}" => "#{s.sluggable_type.downcase.pluralize}#show", :id => s.slug
rescue
end
end
rescue
end
This is live code, and outputs all the slugs in the routes file dynamically. The way we managed to get this to update programmatically was to use an Observer Class like this:
class SlugObserver < ActiveRecord::Observer
def after_save(slug)
Rails.application.reload_routes!
end
def after_destroy(slug)
Rails.application.reload_routes!
end
end
I appreciate you may have your answer already, but as you're a beginner, I felt I could help out by explaining the slug stuff for you
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).
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.
I have very interesting scenario:
I've specified two controllers, one for global events and another another once for company specific events. In routes, it is specified like this:
resources :companies do
resources :events
end
resources: events
Running rake routes I can see the routes being generated:
events GET /events(.:format) events#index
company_events GET /companies/:company_id/events(.:format) events#index
Both paths seem to route to the same controller (the global one)...
I have the second controller under controller/companies that goes something like this:
class Companies::EventsController < ApplicationController
# stuff
end
It never routes in that controller above, no matter whether I use company_evens_path(#company). always goes to the other one.
It used to work in rails 2.3 for me, I'm currently using 3.2
Ok as stated above, I would recommend doing something like this:
def index
if params[:company_id]
#events = Company.find(params[:company_id]).events
else
#events = Events.all
end
end
although if you need to you can specify a controller:
resources :companies do
resources :events, :controller => "companies/events"
end
resources: events
and just create a companies folder inside your controllers folder to put your Companies::EventsController inside