Ruby rails change browser url with resources - ruby-on-rails

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

How to use named routes when using FriendlyId at the root path in Rails

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

Use same controller with associations in top-level and nested paths in Rails

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'

Rails url generation error on a form_for nested route and custom route

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]

How to define a custom nested path in rails 3

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?

Custom rails routing helper method

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

Resources