I have a nested resources:
resources :topic do
resource :fruits, only: [:edit, :update]
end
I want the browser url to be /topic/:topic_id/fruits(.:format) (without /edit) instead of /topic/:topic_id/fruits/edit(.:format)
In my action controller I see I have these routes
GET /topic/:topic_id/fruits/edit
fruits#edit
PATCH /topic/:topic_id/fruits/
fruits#update
PUT /topic/:topic_id/fruits/
fruits#update
what you are looking for is called a member route and it is well explained here
difference between collection route and member route in ruby on rails?
Related
I have a basic Page model in Rails that I'm using with FriendlyId to allow admins to create pages like "/about" or "/contact".
I have the following in my routes file (config/routes.rb) to ensure that the slugs for each page appear at the root of the site, such as https://example.com/about, etc:
resources :pages, except: [:show]
resources :pages, only: [:show], path: "/"
The problem is, with this approach, I can't use the normal named route like page_path(#page) in my views(or tests or controllers for that matter) because that routes to "/pages/about" and I get a "No route matches [GET] pages/about" error.
I could do both routes in my routes file so that "/pages/about" and "/about" work like this:
resources :pages
resources :pages, only: [:show], path: "/"
But, that creates an SEO duplicate content problem. I suppose I could create a helper that sets the rel="canonical" url for each page in the html header, but that also feels like a hack. I'd prefer for there to just be 1 version of each page at the root and also have a named route such as "page_path" or "root_page_path" that I can use throughout my app.
The hack I've come up with for the time being is <%= link_to "#{page.slug}" %>, but not having a named route seems very brittle.
What is a more "correct" way to do this in Rails?
I expected something like this to work:
resources :pages, only: [:show], path: "/", as: "page"
But that doesn't work either. Nothing in the Rails guide on routing is really helping either.
You need top switch the order of their definitions:
resources :pages, only: [:show], path: "/"
resources :pages, except: [:show]
resources only give name to the first path with given url. However - you will have the problem now as the pages/:id path (for delete and update) has now no route helper (as it is normally the same as show).
EDIT: As mentioned in the comment - it will also automatically match /pages path to a show action with id equal to pages - not a great idea! Which leads to better option:
resources :pages, except: [:show]
get :id, to: "pages#show", as: :root_page
Which gives you root_page_path(#page) helper for :show action and page_path(#page) for :update and :delete
I would like my REST API to have several routes, such as:
GET /posts/
GET /posts/1
POST /posts
GET /users/
GET /users/1
GET /users/1/posts
POST /users/1/posts
Is it possible to reuse the same controller for those nested routes under the users collection?
It looks like you want nested routes. Try this is your config/routes.rb
resources :posts
resources :users do
resources :posts
end
This has more info. You could also use match or post and get verb methods individually. There are also many options for nested routes.
https://guides.rubyonrails.org/routing.html.
ALTERNATIVELY
in config/routes.rb:
get 'users/:id/posts', to: 'users#posts'
and in controllers/users_controller.rb
before_action :set_user, only: [:users_posts, :show, :edit, :update, :destroy]
...
def posts
#posts = #user.posts
end
With the second option you can KISS by keeping POST/PATCH/UPDATE/DESTROY at their native home like /posts and /posts/42. Just treat :user_id as a form variable in that case, with whatever extra validation you might need, perhaps referencing a session var.
LASTLY
You can actually put this in your config/routes.rb. But now you're probably writing new forms because :user_id is a route parameter. I'd file that under extra complexity. Maybe it fits your situation though.
post 'users/:id/posts', to: 'users#posts_create'
So I have two models a booker and a booking_ticket. I have nested the routes of booking_ticket under booker, but I also want to create a new booking_ticket without a booker, so I created a custom route. My action works properly when I try to create using the nested route but not on my custom path.
my routes.rb
resources :bookers do
resources :booking_tickets
end
get '/booking_tickets/new', to: 'booking_tickets#new', as: 'new_booking_ticket'
This is the error I'm getting from rails when I use the custom path:
I don't understand where the error is coming from.
You need to create a custom route for create action, your current custom resource is for new action:
post '/booking_tickets/create', to: 'booking_tickets#create'
Or, since you seem to be using defaults, just replace your current custom route with:
resources :booking_tickets, only: [:new, :create]
Be sure to add it outside resources :bookers block:
resources :bookers do
resources :booking_tickets
end
resources :booking_tickets, only: [:new, :create]
I have a model named client and another model called client_preference. The relationship between them is one client has many client_preferences.
Now I want to create methods for updating and deleting client_preferences. For that I generate the routes as:
map.resources :clients do |client|
client.resources :client_preferences, only: [:edit, :update, :destroy]
end
and I get the following named routes:
edit_client_client_preference GET /clients/:client_id/client_preferences/:id/edit(.:format) {:controller=>"client_preferences", :action=>"edit"}
client_client_preference PUT /clients/:client_id/client_preferences/:id(.:format) {:controller=>"client_preferences", :action=>"update"}
DELETE /clients/:client_id/client_preferences/:id(.:format) {:controller=>"client_preferences", :action=>"destroy"}
Now, I want to customize the routes names from client_client_preference to client_preference and similarly for other routes generated so that the client is word not repeated twice in the path names generated. Is there a way to do that or do I need to manually define the routes?
Any help or pointers will be highly helpful.
Using the :as option should allow for creating cleaner named helpers:
map.resources :clients do |client|
client.resources :client_preferences, only: [:edit, :update, :destroy], as: 'preference'
end
This should now map to client_preference.
Hope it helps!
There is a keyword :as which helps you to create customized path names in Rails.
Also There is existing answer to this question: Rails 3 nested resources short name?
I am having issues getting my helper method names to work properly, any suggestions would be great:
#config/routes.rb
resources :junkie, only: [:show, :index, :destroy], as: :junkie do
get :merge, on: :collection
end
So I was having issues because I the singular form of junkies is junky, but when I make this change and look at the routes it changes the #merge helper to:
merge_junkie_index GET /junkies/merge(.:format) junkies#merge
Is there any way to change this to just merge_junkie? I tried removing it from the resource black and using the match syntax: get "junkies/merge" => "junkies#merge", as: :junkie but for some odd reason this directed me to the show method even though the route was right.
The solution is a ugly one but it works, since the show route is the only one that is affected by the as: :junkie you can break it out put the merge route in a separate block. The ordering of the resource also matters for some reason, if you do not put the merge first, it will interpret the url /junkie/merge/ as a id and hit the show action. So it should look like this in your routes file:
resources :junkies, only: [:index] do
get :merge, on: :collection
end
resources :junkies, only: [:show, :destroy], as: :junkie