I did a controller called api in which I have actions that return data. In the app I also have a Car model and a cars controller (with all the standard routes), but I want to move every URL that I use into the api controller and remove the URLs that I don't use from my app. In the case of the cars controller I only use the URLs in which the json of all the cars are returned or the json of a particular car is returned (i.e. something like index and show).
The problem I have is that when I use the json.jbuilder to render the list of cars I also want to print the URL of those resources (show), so for example if I have the endpoint /api/cars.json I use the following json.jbuilder.
views/api/cars.json.jbuilder
json.array!(#cars) do |car|
json.extract! family_bond, :id, :name, :image, :description
json.url car_url(car, format: :json)
end
But car_url throws an error if I delete resource: car from the routes file. This is obvious since car_urlshould return the show action URL of every car.
So my question is the following
If I create the action to show the details of a car in the api controller (for example, something like /api/car.json?id=1), how can I tell Rails to use this endpoint instead of the standard one (show) when I use the car_url` method?
I don't think there is a way to have the path and url helpers work without the route being specified in the routes, as when you use some other utility to route the URLs to the controllers. However, if you get the routes.rb to specify how to route an URL to your API, it will work.
resource :car, controller: 'api'
Related
My application has several ways to create notifications:
A user can create a notification using the standard "new" method
A link in a view can create a notification from an action using a dedicated "new_action"
etc.
So I created additional route and views for the new_action_notification_path:
resources :notifications do
member do
get :new_action
end
collection do
get :index_all
end
end
In the controller
# GET /notifications/new_action
def new_action
#playground = Playground.find(current_playground)
#notification = #playground.notifications.build( playground_id: params[:playground_id], …
And in the view:
<%= link_to t('Reject'), new_action_notification_path(
playground_id: current_playground,
description: t("#{this_object.class.name}#{'Rejected'}"),
But this does not behave as expected:
If I write new_action_notification_path in the view, as above, the generated URL looks like /notification/729/new_action?code=QWSTZ ...
If I write new_notification_path in the same place, the generated URL looks like /notification/new?code=QWSTZ ...
Why is it different, and how can I remove the notification id from the first URL?
Thanks a lot!
The reason your url's are different is because you have a nested route for the new_action.
resources :notifications do
member do
get :new_action
end
end
With a nested route, you are going to get an id number between your resources. The 729 is the id you are passing in to the link_to helper. The other route helper, new_notification_path, is creating a new notification, so it doesn't need an id. If you look at your routes, with rails routes in your console or localhost:3000/rails/info/routes in your browser, you'll see that the new_action needs an id.
new_action_notification_path GET /notifications/:id/new_action
new_notification_path GET /notifications
The :id part of the route is a placeholder for an id number (although it can be anything you pass in, really) to look up the resource before it. In this case, the id is use to look up the notification.
For example, let's say I have a Post resource.
With GET /posts I retrieve all posts (index action). What if I want to retrieve trending posts (posts with a lot of likes in the last 24 hours)? Should I implement the logic in the index action (through a parameter), or should I implement a new action trending that responds to a new endpoint GET /trending, making the API non-REST?
Note: I read this article and I understand the parameter way, but I can't figure out how adding an extra endpoint works...
you can do both. I would opt for the new action. But you can pass an extra param as well in a link:
link_to posts_path(trending: true)
In your controller you can then check if params[:trending].present? and then only pass to the posts variable #posts the trending posts.
You can also assign the trending indicator to a variable that will be passed to the index view so that you can adapt the layout (change header from "posts" to "trending posts") with if params[:trending].present? then #trending = true end
Creating a new action makes controllers and views less cluttered with conditions (if ...)
By the way, creating a new action is still a REST logic if you make it a GET query. If your new action was about updating a post it would need be a PATCHquery
Problably you have the resource :posts inside your routes.rb. This is the place you have to add the new endpoint. In rails the route to the new action.
You have
resources :posts
You should have
resources :posts do
get 'trending', on: :collection
end
Now you can see all routes that rails generate to you line, index, show, create, update... and more one trending
/posts/trending
You can have a look here: Rails guides add restful
I have a /articles page that I can access via the articles_path route helper.
Let's say I have 2 tabs on that page (e.g. something like this) that the user can click back and forth on, but it doesn't leave the page.
I have logic that allows the user to deep link to a specific tab, so either of the following url's are valid and will open the page on the specified tab directly.
/articles?tab=foo
/articles?tab=bar
Is it possible to define two new custom routes with the above urls that include the query parameter? I'd love to have a helper like articles_foo_tab_path and articles_bar_tab_path that incorporate those query parameters directly.
Thanks!
Create those helper methods:
module ArticlesHelper
def articles_foo_tab_path(article)
article_path(article, tab: 'foo')
end
def articles_foo_bar_path(article)
article_path(article, tab: 'bar')
end
end
And use them in your views:
<%= link_to #article.title, articles_foo_bar_path(#article) %>
The helper method is one solution. Alternatively you can add a route which maps the tab param to the url e.g. articles/foo or articles/bar
get "articles(filter/:filter)", to: "articles#index", filter: /.*/
I have a Ruby on Rails and ActiveAdmin application. I didn't basically change any default configuration except adding and registering a few models.
I want to enable my application with a route like GET /heartbeat and respond with a simple string to client/user. I'm wondering how could I do the following steps:
Add a custom route to my routes.rb file.
Add a custom controller under app/controllers path.
Implement a custom action and respond to user directly without any view.
routes.rb:
get 'heartbeat' => "custom_controller#heartbeat"
custom_controller.rb:
class CustomController < ApplicationController
def heartbeat
render inline: "Some string to the client/user"
end
end
Avoiding the Rails render stack will save you some processing and be faster. You can do this at the router level via a simple Rack "application" that returns a response code:
get 'heartbeat', to: proc { [204, {}, []] }
Anything that responds to call and returns [status, headers, body] is rack compliant so you can leverage a proc to do just that, right in the router. In this case, we send a 204 No Content which should be sufficient for a heartbeat, but you can always return custom data/headers.
Update:
I can only imagine that this was downvoted because people don't understand why this is better. Here's a quick attempt to explain:
In case it wasn't clear, you don't need a controller action at all with this method. Here's the equivalent solution to the accepted answer:
get 'heartbeat', to: proc { [200, {}, ['Some string to the client/user']] }
Sticking that line in your Rails routes.rb file will be equivalent to creating a new controller, view and route entry, with one key difference: It avoids the Rails response rendering stack so should be much faster than the accepted solution.
I have an idea and I am not sure if is is already done.Right now to make url search engine friendly developers customize to_param function of model class. Then they call to_s function to get the id of the elmenet.
What if I want to create url for not model but for string. Lets say I create link for post controller's search action. The only variable in the url is search_string. So how can i create seo link for this search page. I know how to create links etc etc. but my problem is that I want to call function on this string such as to_param or something like that to make seo string, also in the controller to perform a search I have to humanize this string again. So what I want is this:
In every view, I dont want to use urlizing method to make it se friendly
In every controller, I do not want to call any function to humanize string back again, this should done by router
In the router:
match 'search/:string' => 'Post#search', :as => :search, before => some_before_router_helper_function, after => some_after_router_helper_function
In these helper function what I will do is that i will parametrize any params I want, then I will humanize any params back again
example before/after router helpers:
def some_before_router_helper_function
string = string.underscore.dasherize
end
def some_after_router_helper_function
param[:string] = param[:string].undasherize.un_underscore
end
As Heikki pointed: https://github.com/svenfuchs/routing-filter