Allow only given parameters value - ruby-on-rails

I my RoR (v 6.1) application (only API) i have route
resources :cars
This is valid URL:
http://localhost/cars
To find cars with fuel pb95 i use:
http://localhost/cars?fuel=pb95
Only pb95, pb98, gas are allowed for fuel?
How do I limit the ability to specify a fuel type outside of the list?
If someone enters a value outside the list should I display that this value cannot be used, or display that there are no cars that meet this condition?
Whitch approach looks better?

You have a few options for how to handle this.
One approach would be to check the fuel type in your controller, and render an error response if they pass an invalid fuel type.
Another approach would be via route constraints. Instead of using query params, you can use special routes to receive your params:
get 'cars/:fuel_type', to: "cars#index", fuel_type: /(pb95|pb98)/, as: :cars_with_fuel_type
This way, if an invalid fuel type is provided, the route wont match and they'll get a 404.
If you want something other than a 404, then the first option gives you more control over the error response.

Related

What would be a good API design with two identifiers?

I am not quite sure what would be a good API design for my scenario below.
We have a model ticket that has an ID and a QR code, both of which are always unique.
To query the status of a ticket, I'm sure this should look obviously like this:
GET /tickets/:id
But what should the design of the QR code API look like?
By also using GET /tickets/:id it would be unclear to the user which locator is being searched for.
GET /tickets?qr_code=:qr_code
would be the second option, but I find this so unpleasant because the returned element is always exactly one and not an array which usually would be the expected response of such a request.
How would a proper API design look like here to query the status of a single ticket by its QR code?
When there are multiple attributes that are unique then that means that each attribute can serve as an identifier in the URL.
Therefore I would document the route in the API like this
GET /tickets/:id_or_qr_code
What basically boils down to the default resource path
GET /tickets/:id
with a controller method like this
def show
#ticket = Ticket.find_by(id: params[:id]) ||
Ticket.find_by(qr_code: params[:id]) ||
# ...
end

Rails routing forcing param to be present

I'm fairly new to ruby api development and have created the below endpoint in my routes.rb
get "/users/active_users/:since"
However, when the param is not given, I want the param to default to a certain value. How do I enforce this? Also, I want to enforce that param be an integer and not alpha/alpha-numeric. Help is appreciated!
get "/users/active_users/:since"
By making this routes its compulsory to give the params[:since] , other wise it will throw back a error not routes matches
So here by i would suggest you to make routes
get "/users/active_users"
Since it's get type request so its won't affect more, you can append params[:since] in query with routes like this:-
/users/active_users?since=1999
And at your controller you will get params[:since] = 1999
So far as i know it can't be enforce routes to accept only integer params but it can be handle at controller side
params[:since].is_a? Integer
=> true
Or
params[:since].to_i

How to get an organized search URL result with slashes orders (/)?

I want a search section on the "index" from books_controller with some filter options from different authors, categories and other attributes. For example, I can search for a category "romance" and max pages = 200. The problem is that I'm getting this (with pg_search gem)
http://localhost:3000/books?utf8=%E2%9C%93&query%5Btitle%5D=et&button=
but I want this:
http://localhost:3000/books/[category_name]/[author]/[max_pages]/[other_options]
In order that if I want to disable the "max_pages" from the same form, I will get this clean url:
http://localhost:3000/books/[category_name]/[author]/[other_options]
It'll work like a block that I can add and remove.
What is the method I should use to get it?
Obs: this website, for example, has this kind of behavior on the url.
Thank you all.
You can make a route for your desired format and order. Path parameters are included in the params passed to the controller like URL parameters.
get "books/:category_name/:author/:max_pages/:other_options", to: "books#search"
class BooksController < ApplicationController
def search
params[:category_name] # etc.
end
end
If other options is anything including slashes, you can use globbing.
get "books/:category_name/:author/:max_pages/*other"
"/books/history/farias/100/example/other"
params[:other]# "example/other"
So that gets you the basic form, now for the other you showed it could just be another path since the parameter count changed.
get "books/:category_name/:author/*other_options", to: "books#search"
params[:max_pages] # nil
If you have multiple paths with the same number of parameters, you can add constraints to separate them.
get "books/:category_name/:author/:max_pages/*other", constraints: {max_pages: /\d+/}
get "books/:category_name/:author/*other"
The Rails guide has some furth information, from "Segment Contraints" and "Advanced Constraints": http://guides.rubyonrails.org/routing.html#segment-constraints
If the format you have in mind does not reasonably fit into the provided routing, you could also just glob the entire URL and parse it however you wish.
get "books/*search"
search_components = params[:search].split "/"
#...decide what you want each component to mean to build a query
Remember that Rails matches the first possible route, so you need to put your more specific ones (e.g. with :max_pages and a constraint) first else it might fall through (e.g. and match the *other).

Rails routing sometimes replaces the id in url with id-name. How do I avoid problems?

Generally the url from my report page looks like this:
http://test-account.peter:3000/offices/7/reports/index
However, sometimes it looks like this:
http://test-account.peter:3000/offices/7-peters-office/reports/index
Why does this happen?
It was not really a problem until we changed the controller action from a GET to a POST and renamed it. We had to do this so we could pack more parameters in to the ajax request. Users still have this section of the site bookmarked and it throws errors all day long.
I have tried to redirect the route:
get '/offices/*all/reports/index' => 'offices#show'
get '/offices/:office_id/reports/index' => 'offices#show'
get '/offices/:office_name/reports/index' => 'offices#show'
Is there a way to catch the name? Or do I have to prevent the name from being added to the url in the first place?
In the controller, you would be able to parse the parameter to get just the first character and check if its an integer. However, it would be much better to debug how the parameter is getting assigned to different values and ensure only the id is used. If you're linking to that route in a view, check what is being passed in the link and confirm the value is what you expect it to be.
Rails does routing it does not look in your database for matched data. So without looking at data, your three routes are exactly the same, the variable (office_id & office_name) is just named different. If you get a request on example /offices/:office_name/reports/index, rails will just match the first one since both routes match the request.
You need something in the path that indicates its a name or id. If you will really never have a name and id with the same search, then you could just have one route and try to match a id or name from the DB in the controller.

Custom rails routes not using :id column

I am attempting to create a custom rails route that allows me display information based upon the url. For example, I have products in a database with category_ids and country of origin fields. I would like to be able to type something like /products/(category_id)/canada or something to list items that match that category and country however my attempts have (obviously) been unsuccessful.
So far I've attempted
match 'products/:category_id/:country', to: "products#var_show"
and had no luck.
I've even just tried to make a route that shows the product via the serial code but rails seems to think I'm looking for an id even though I've specified the field in the route and in the controller.
match 'products/:serial', to: "products#show"
Can someone lead me in the right direction and show me what I'm doing incorrect? Thanks.
edit:
Rails seems to make the parameter :id no matter what I call it in the route and controller
Processing by ProductsController#show as HTML
Parameters: {"id"=>"481598745"}
Ideally that would be Parameters : {"serial" => "481598745"} in the second case I asked about.
Try this,
match 'products/:category_id/:country' => 'products#show',:as => :show

Resources