Defining a custom rails route with query parameters - ruby-on-rails

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: /.*/

Related

Can I have several New methods in a rails controller on Rails 5.2?

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.

Undefined method model_name_url in if there's no show

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'

Best way to reset ActiveRecord Attribute with a link

I have a counter in my model that I want to give the user the ability to
reset it, I'm wondering what's the best way to achieve this. I can think of
two ways:
By a custom controller action.
Simple and easy but I can't decide which HTTP verb to use. I can make the
case that it should be a GET because the user clicks a link that reset
the counter and the result are always the same, i.e. counter
becomes 0. But it could also be a POST/PATCH since we are modifying
something on the server but POST/PATCH requires a form which leads to
the other way.
By a link that submits an edit form with the counter reset to 0 without
the user seeing the form.
I like this solution because it can be done with RESTful controller
methods. But I have no idea how to do that with Rails, or even if it's
possible.
So which is "Rails Way" to do this? and how do I do it?
Rather than creating a custom action, another approach is to create a well-named controller and stick to the RESTful controller method names.
config/routes.rb
resource :counter_reset, only: [:create]
app/controllers/counter_reset_controller.rb
class CounterResetController < ApplicationController
def create
# reset your counter
end
end
Then POST to counter_reset_path in your view
Personally, I would use button_to — this generates a single button that submits to the URL; it performs a POST operation by default. If you don't like the button style, you can switch to using link_to; however, keep in mind that if a user has JavaScript disabled, the request will fallback to using GET.
<%= button_to "Reset counter!", counter_reset_path %>
<%= link_to "Reset counter!", counter_reset_path, method: :post %>
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
Update:
If you prefer not to create a new controller, you can create a new route that maps to a custom action in your existing controller:
config/routes.rb
resources :counters do
post :reset, to: "counters#reset"
end
app/controllers/counters_controller.rb
class CountersController < ApplicationController
def reset
# reset your counter
end
end
In your view:
<%= button_to "Reset counter!", counter_reset_path %>
Actually you don't need a form, for me i would add a new action, it would look something like this ( of course depends on how your current routing looks like )
/user/:id/counter/reset # with action = post
And the link is very simple, you just create a link_to and add a method: :post which will add a data-method: :post in the html, the rest will be handled by the unobtrusive js.
The reason I don't recommend the form method, is users might use it to update different attributes that you might not want to update, or at least even change the counter to whatever number they want, I prefer the specific number to be defined in the controller not the view/form.

link_to custom action but wrong method?

all, I'm trying to get a custom action to work with a put method: in the
in _post.html.erb i have a link_to statement:
<%= link_to 'End now', post, :method => :put, :action => endnow %>
routes.rb contains:
resources :posts do
member do
put :endnow
end
and posts_controller.rb looks like:
class PostsController < ApplicationController
helper_method :endnow
[.. code for create, edit, destroy, etc ..]
def endnow
puts params
end
end
rake routes's relevant line looks like:
endnow_post PUT /posts/:id/endnow(.:format) posts#endnow
However, the action endnow helper doesn't run when clicking on this link.
Strangely, it does run with an index action (which i can tell from the puts command.
Of course, eventually the code for endnow will update #post, but for now, it just doesn't run properly.
Maybe i'm going about this the wrong way - all I'm trying to achieve is to update #post upon clicking the link to that post, and before showing it.
Any ideas / Alternatives?
Why not use the route helper method provided to you? Change your link to
<%= link_to 'End now', endnow_post_path(#post), method: :put %>
Things you're doing wrong:
If you want to specify the :action, use the Symbol for the action (you're missing a colon). :action => endnow should be action: :endnow
I will assume you have a #post instance variable you're passing from your controller to your action. You should be using that instead of post (unless you do in fact have a local post variable you're omitting from your code)
You are using endnow as an action; you should remove the helper_method :endnow line in your controller because it's not something you want to/should be accessing from your view.
This can all be avoided by using the route helper (for endnow_post you'd append _path to get the local route path: endnow_post_path), and pass in your #post as an argument.
Because you're trying to do a PUT request, you must make sure you have something like jquery-ujs included in your asset pipeline to convert these links to form submissions behind the scenes; browsers don't support PUT via the click of a link on their own.
As for why you're getting the template error when you get your link_to working, Rails is telling you that you need to create a app/views/posts/endnow.html.erb file. Your action has only puts params which does not terminate execution, leaving Rails to assume you still are trying to render some endnow.html.erb template.
Are there other ways to do what you're trying to do (change a single attribute of a specific model)? Sure. Are there better ways? That's pretty subjective; it may not be the most RESTful way, but it's arguably easier to deal with (if for example there are very specific authorization rules to check before updating the attribute you are modifying in endnow. Does the way you've started fleshing out work? Absolutely.
Finally, as a bump in the right direction, after you fix your link_to and remove the the helper_method as I have described above, your endnow action might look like this:
def endnow
post = Post.find!(params[:id])
post.some_attribute_here = some_new_value_here
post.save
redirect_to :root and return # <- this line sets a redirect back to your homepage and terminates execution, telling rails to do the redirect and **not** to render some endnow.html.erb file
end

Multiple options for update action

How can I pass and collect different options into a controller action.
E.g you have a Team model and you want to add or remove Users from the team?
I would assume this would go in the update action of the teams controller, but the update action also need to be able to update team details like name, address, ect.
I tried the following code but that produce some weird results to my css and produces errors.
link_to team_path(user), params[:add] ,:class => 'btn btn-mini pull-right', :method => :put
Weird results are probably caused by the mixed parentheses
params[:add}
what does your model look like? (Teams-Teammember relation?)
But in general:
- you should add actions to the appropriate controller (prob. teams_controller) for
adding and deleting members:
def add_member
end
def remove_member
end
and define routes in config/routes.rb to be able to use this actions (there are plenty of examples how that can be achieved in the comments generated), then you can use the resulting path helper for your link_to tag - check out the available routes and path helpers with
rake routes

Resources