Rails query parameter with same name as optional path parameter - ruby-on-rails

Suppose, I have this code in routes.rb
get "/items/(:brand)", to: "items#index", as: :items
I can't change this route, because sometimes I need urls with brand in path (not in query).
Can I create path like this:
/items?brand=TestBrand
but not this:
/items/TestBrand
via route helpers?
items_path(brand: "TestBrand") give 2'nd variant.

This is not a very good solution as it violates the RESTful conventions.
In Rails flavor REST GET /resource_name/:id maps to the show route. In the case of get "/items/(:brand)", to: "items#index", as: :items this creates an ambiguity (is the segment an item id or a brand?) when the router matches the requests and the first declared route will win which is hardly desirable.
A better solution is to declare it as a nested resource:
resources :brands, only: [] do
resources :items, only: [:index], module: :brands
end
Prefix Verb URI Pattern Controller#Action
brand_items GET /brands/:brand_id/items(.:format) brands/items#index
# app/controllers/brands/items_controller.rb
module Brands
class ItemsController < ::ApplicationController
before_action :set_brand
# GET /brands/:brand_id/items
def index
#items = #brand.items
end
def set_brand
#brand = Brand.find(params[:brand_id])
end
end
end

Additional parameters in "get" are supported by default, so maybe you could use
get "/items", to: "items#index", as: :items
or
resources :items, only: [:index]
And use path helper which you provided:
items_path(brand: "TestBrand")

To answer your question - Yes, you can
get "/items", to: "items#index", as: :items
and the following route helper will create
items_path(brand: "TestBrand")
#=> items?brand=TestBrand
NOTE:
If you are using:
recourses :items
You must alredy have this

Related

Modifying a rails route

I have the following route:
resources :trees, shallow: true, controller: 'base', param: :ed_node_id, only: %i[index show] do
resources :courses, shallow: true, controller: 'base', param: :ed_node_id, only: %i[index show]
end
Which gives me:
api_v1_tree_courses GET /api/v1/trees/:tree_ed_node_id/courses(.:format)
api_v1_course GET /api/v1/courses/:ed_node_id(.:format)
api_v1_trees GET /api/v1/trees(.:format)
api_v1_tree GET /api/v1/trees/:ed_node_id(.:format)
What i’m trying to avoid is on that first route, the param not being prefixed by :tree. Is there a way in nested routes to force the param not to have a parent route prefix or possibly using a regex to give the desired route?
Unfortunately Rails just generates the param key for nested resources by combining the singular form of the name with the param option:
module ActionDispatch
module Routing
module Mapper
class Resource
...
def nested_param
:"#{singular}_#{param}"
end
end
end
end
end
There is no option to actually alter the entire nested param - it would make a good feature request though.
The alternative is to use scope:
resources :trees, only: :index
scope '/trees/:ed_node_id' do
resources :courses,
only: :index,
as: :tree_courses
end
# shallow nesting won't work so we have to define this separately.
resources :courses, only: [:show]

api resource rails routes

So I have some API resources in my app, and some regular resources, and for regular resources I use :
resources :books
And then I could pass except: %i(destroy new edit) or only so works great! However for my resource I ll never have new/edit actions, sometimes I will need to pass except and only options too.
I was thinking to create something like:
api_resources :books
Which comes without new/edit actions by default, how would I do that?
Maybe something like this?
# config/routes.rb
Rails.application.routes.draw do
def api_resources(res)
resources res, only: [:new, :edit]
end
api_resources :a
api_resources :b
end
# output
Prefix Verb URI Pattern Controller#Action
new_a GET /a/new(.:format) a#new
edit_a GET /a/:id/edit(.:format) a#edit
new_b GET /b/new(.:format) b#new
edit_b GET /b/:id/edit(.:format) b#edit
#Amree's answer can not handle nested resources. An improvement would be:
# config/routes.rb
Rails.application.routes.draw do
def editable_resoustrong textrces(res, &block)
resources res, only: %i[new edit], &block
end
editable_resources :a
end
# output
Prefix Verb URI Pattern Controller#Action
new_a GET /a/new(.:format) a#new
edit_a GET /a/:id/edit(.:format) a#edit

Why does this route setup result in these path variables?

In my rails 4 application, I have a piece in routes.rb that looks like this:
namespace :settings do
resources :profile, only: [:index] do
put :update_user, on: :collection
end
end
The controller is located in app/controllers/settings/profile_controller.rb and looks like this:
class Settings::ProfileController < ApplicationController
def index
end
def update_user
end
end
This results in these paths from rake routes:
PUT update_user_settings_profile_index -> /settings/profile/update_user(.:format)
GET settings_profile_index -> /settings/profile(.:format)
What I don't understand is why these paths have _index in them. I would like to get rid of it. Can I? If so, how?
I think that's because you're using a singular name profile for your resources definition, which should be plural by convention. You can try using resources :profiles instead.
For me, with routes it is always easier to work backwards, asking what are the paths I want? I'm not sure what paths you want, but I'll give you some ideas here, I hope.
I'm going to assume you want one index path, since you explicitly include the the only: [:index] statement, but you do not want not a indexed path on your resources.
Try moving your only: [:index] statement into the outer do loop for settings and add an only: [:update] to your profile (or whichever action you're looking for)
namespace :settings, only: [:index] do
resources :profile, only: [:update] do
put :update_user, on: :collection
end
end
Gets you here:
update_user_settings_profile_index PUT /settings/profile/update_user(.:format) settings/profile#update_user
settings_profile PATCH /settings/profile/:id(.:format) settings/profile#update
PUT /settings/profile/:id(.:format) settings/profile#update

How to define a Rails route on resource member and have it appear at the end of the route name?

I have a route in my Rails application that looks like this:
resources :products
get :specs, on: :member
end
This results in the route helper: specs_product_path instead of product_specs_path. How can I define the route so that the "specs action" is added to the end of the helper method instead of the beginning?
You can always declare a sub-resource which follows this convention:
resources :products do
resources :specs, only: [ :index ]
end
This will require creating another controller, though, with an index action.
You should also be able to override the name with the as: option:
resources :products do
get :specs, on: :member, as: :product_specs
end
Generally it's a good idea to adhere to convention as every exception can lead to confusion or conflict down the road.

Ignore part of the path in Rails 3

I would like to be able to ignore part of the paths in my application.
For example:
example.com/products/toys/big-toy, should be routed by ignoring the 'toys' part (just products/big-toy). I am aware of the wildcard symbol available in the routes, but it ignores everything after the products path. I am not sure how to do this and keep my nested resources working.
Routes:
resources :products do
member do
match :details
end
resources :photos
end
product.rb:
def to_param
"#{category.slug}/#{slug}"
end
One way to solve this would be to use a route constraint.
Try this:
resources :products, constraints: { id: /[^\/]+\/[^\/]+/ } do
member do
match :details, via: :get
end
resources :photos
end
This will capture the product :id as anything with a slash in the middle, so /products/abc/xyz/details will route to products#details with params[:id] equal to abc/xyz.
Then, you could add a before filter in your ProductsController, like this:
class ProductsController < ApplicationController
before_filter :parse_id
// ...
def parse_id
slugs = params[:id].split("/")
params[:category_id] = slugs[0]
params[:id] = slugs[1]
end
end

Resources