Trying to call for a specific method - routing error - ruby-on-rails

I have user and product models. User's table has a "discount" field. I trying to call method "apply_discount" from users/index view, which multiplies all prices and given discount.
users/index.html.haml:
- #users.each do |user|
# user data
= form_tag(action:'apply_discount', method: :put) do
= number_field_tag :discount
= submit_tag
UsersController has a method named "apply_discount".
My problem is routing error when I submitting form:
localhost:3000/assets?action=apply_discount&controller=users&method=put
No route matches [POST] "/assets"
Please explain me why this form requests assets. And how to make right request.

Having this in your url probably means your form tag has some incorrect syntax so I don't think your problem is a routing error.
?action=apply_discount&controller=users&method=put
should look something like
<%= form_tag(:controller => "foo", :action => "bar", :method => "post" %>
<% end %>
you should have a route setup for the action you are wanting to get to in the controller
check out this guide
http://guides.rubyonrails.org/form_helpers.html

Actually, I've found that if there is not a corresponding good route in routes.rb, it makes the url be:
/assets?controller=xxx&ampaction=yyy
If it finds the route you get the correct
/xxx/yyy
(assuming xxx is the controller and yyy is the action you specified).
So if you are building a non-REST-ful url you better have it in the route table first.

Related

form_for and form_tag parameters for custom action

I would like to use a drop action in the friendships controller to drop requests of friendship, and I am struggling to understand how to create a working form and working routes.
I added the following route in config/routes.rb
resources :friendships, only: [:create, :destroy] do
member do
post :drop
end
end
This would generate the named route drop_friendship_path(id).
My question is now how to create a working form, that is what parameters am I necessarily required to use with form_for or form_tag.
Since in view I would iterate on #users requesting a friendship, I would not know the id of the request to delete, so I cannot use the above named route. Which one of the following is correct?
<%= form_for(current_user.friendship_request.find_by(requester_id: user.id), url: { action: drop }) %>
<%= form_tag({controller: "friendships_controller", action: "drop"}) do %>
I struggled to find documentation on parameters to use with form_for and form_tag. The api documentation says that the :url
argument for form_for may be represented in the same way as values passed to url_for or link_to. So for example you may use a named route directly. Documentation on url_for or link_to however does not add anything else.
I found only examples, not an exhaustive documentation, in the form helpers section of guides.rubyonrails.org for form_for and form_tag, so I realized that url for form_for can have a hash of subparameters (however only action is reported) , and that the controller and action parameters can be passed to form_tag.
What is the complete list of parameters for the url hash in the form_for helper?
What is the relative list of parameters for the form_tag helper?
Which ones are required in my form?
If, instead of specifying controller and action, I used inside form_for or form_tag:
url: drop_friendship_path(#friendship_request)
and defined #friendship_request in the drop action, would that work?
A better way is to use button_to or link_to helpers for your purpose. button_to generates a form. link_to generates a link, but can also send post request with {method: :post} option.
Why do you think you can't use the drop_friendship_path(id) helper method? You can:
<% request_id = current_user.friendship_request.find_by(requester_id: user.id) %>
<%= button_to "Drop", drop_friendship_path(request_id) %>
<!-- or -->
<%= link_to "Drop", drop_friendship_path(request_id), method: :post %>
Why don't you use the existing destroy action to delete friendships instead of drop ?
And also, sending a separate query for each user to get a friendship record is not a good thing. You should think of how you can avoid this. There are many solutions, but it is not the subject of the question.

Submitting a form with GET method which doesn't match route

I want to filter results by category and I'd like to use the GET method instead of POST. However, I am doing something wrong that I can't figure out: the form's action does not match the defined route, so it triggers a different method.
Here's the form:
<div>
<%= form_tag '/expenses/search', method: 'get' do %>
<%= select_tag 'category_name', options_from_collection_for_select(Category.order(:name), :name, :name) %>
<%= submit_tag 'search' %>
<% end %>
</div>
Sending this form produces an URL like the following:
http://localhost:3000/expenses/search?utf8=%E2%9C%93&category_name=Alcohol&commit=Search
However the route is defined like this:
resources :expenses
get 'expenses/search/:category_name', to: 'gastos#search_by_category'
This means the URL where the form is submitted isn't the one I'm trying to submit it to. It's matched with the one corresponding to the show method, as you can imagine.
How can I submit the form to the matching URL? What is the usual way to deal with this situation?
You didn't set your route properly as it has unrecognized :category_name segment. Your route should be defined like this:
get 'expenses/search', to: 'gastos#search_by_category`
If your route is nested on expenses I recommend to use block function
resources :expenses do
collection do
match 'search', to: 'gastos#search_by_category`, via: :get
end
end
Is a good practice to use rials routes helper, try to avoid put routes with plain text, in your case will be:
<%= form_tag search_expenses_path, method: 'get' do %>
<%= select_tag 'category_name', options_from_collection_for_select(Category.order(:name), :name, :name) %>
<%= submit_tag 'search' %>
<% end %>
Furthermore, don't confuse 'query params' with 'url params'
http//www.host.com/profile/12?type='json'
In this example '12' is a url param and is expresed with :(nameofparam) in routes files but 'type' is a query param that are not expresed on rails routes.
It's supposed to work like that, since it's client-side.
You see, parameters are sent by the browser, that (in general) has no understanding of how your site routing works inside. Submitting a form, in general, requires an URL (to submit params to) and a set of parameters, which in case with GET typically* gets passed as a query string.
The browser will eventually hit the exact route that is specified in form's URL and supply all the form's parameters in a query string appended to the end in usual format:
...?category=stuff
You simply cannot expect the browser to hit a different route (which query string is not part of) with one form just because it has a different value in one of the <input>s.
* I've never actually seen this done differently, but I didn't find a firm requirement of this either.
Do you really want pretty search links that badly?
You could try to circumvent this by placing a "prettifying redirection" – direct search queries to that action, but do not perform search there: instead use the received parameters to construct a route and redirect your user to it.
def search_redirect
redirect_to whatever_search_path(category: params[:category])
end
That would trigger the route helper to build the pretty adress that conforms to the defined routes.
Too hacky?
Well, you could go with submitting a form through JavaScript and alter the parameters and URL request in any way you want. But this is still hacky and I wouldn't do either. Query string in search requests looks perfectly fine to me.

Rails: Why am I getting "No route matches"?

Snippet from routes.rb:
resources :templates do
post :add_rates
resources :rates
delete :remove_rate
end
Now I try to use the "add_rates" path in my form.
Tried both:
<%= form_for(#template.template_id, :html => {:class=>"form-horizontal"},:url=> { :action=>:add_rates}) do |f| %>
and:
<%= form_tag(:template_add_rates) do %>
But I'm always getting: No route matches {:action=>"add_rates", :controller=>"templates"}
Any help would be appreciated.
You're doing it wrong:
form_for [#template, :add_rates], html: { class: "form-horizontal } do
or
form_for #template, url: template_add_rates_path(#template), html: {class: "form-horizontal" } do
The template, and its ID, and the action you want (add_rates) all have to be passed in as the same parameter. You can't give it the template ID as the first argument, and then try to tack additional URL parameters onto it. Additionally, in both cases, you're missing key parts of the URL. In the first one, you're just giving it an ID, and :add_rates; Rails can't take an arbitrary number and know that it's a template ID that you're giving it. In the second case, you're giving it :template_add_rates; how is Rails supposed to know which template you're trying to add rates to, without a template ID? You need to give it all three pieces of the route you're trying to match: /templates/:template_id/add_rates.
There are also a bunch of other weird issues/errors with the code you've posted:
#template.template_id should be #template.id, unless you've explicitly deviated from Rails' conventions, which you should almost never do.
Your routes are pretty weird. You shouldn't be adding a add_rate route, you should be using the routes provided by your nested resources :rates line.
Your routes should look like this:
resources :templates do
resources :rates
end
This gives you routes like POST /templates/:template_id/rates for creating rates (instead of your add_rate route), and DELETE /templates/:template_id/rates/:rate_id for deleting rates (instead of your remove_rate route).

Routing error Rails 3.2.1

I'm trying to create a very simple site the sends and receives parameters from the URL (or link), the app should support any number of parameters, i.e. http://localhost:3000/action=receive&controller=pages&email=mail%40site.com&name=Vinny, and then be able to receive/print them.
I think I have located the problem to my route. It currently looks like this:
match 'pages/*params' => 'pages#receive'
I create the link that sends the params like this:
<%= link_to "Send Params", :action => "receive", :name => "Vinny", :email => "mail#site.com" %>
When I click the link I get the following error.
No route matches [GET] "/assets"
And the URL looks like this:
http://localhost:3000/assets?action=receive&controller=pages&email=mail%40site.com&name=Vinny
Note asset?.
If my route instead looks like this:
match 'pages/:name/:email' => 'pages#receive'
It works, but then I'm limited to the specified params.
Any tips on how to solve this would be great.
Okay, I think I see what is happening. Specifying parameters in the routes is not necessary to get the values into the controller's action. So, replace the map you specified earlier in the routes.rb with
resources :pages
If you add to your link
:controller => "pages"
It should go to the correct controller + action. (this may not be necessary in your case)
In that action, you can grab all of the params from the hash
email = params[:email]
account = params[:name]
Etc.
Info on routing
Info on params

Using named routes with parameters and form_tag

I'm trying to create a simple search form in Rails, but I think I'm missing something.
I have a named route for search:
map.search ":first_name/:last_name", :controller => "home", :action => "search"
I'm trying to use that in my search form:
<% form_tag(search_path, :method => 'get') do %>
<%= text_field_tag(:first_name) %>
<%= text_field_tag(:last_name) %>
<%= submit_tag("Search") %>
<% end %>
But when I load up the search form I get an ActionController::RoutingError:
search_url failed to generate from {:action=>"search", :controller=>"home"} - you may have ambiguous routes, or you may need to supply additional parameters for this route. content_url has the following required parameters: [:first_name, :last_name] - are they all satisfied?
What am I missing? I thought the fields defined in my form would be automatically linked up with my route parameters. :-/
Update:
I understand that search_path is generated before the form is displayed now, so it can't be updated. Obvious in hindsight!
I changed my routes:
map.search 'search', :controller => "home", :action => "search"
map.name ':first_name/:last_name', :controller => "home", :action => "name"
And now the search action just does:
def search
redirect_to name_path(params)
end
It all works a treat! The main goal here was getting that URL from the name named route as result of doing a search. Thanks guys!
form_for generates form and it has to have specified all parameters that are needed to create search_path, so it should look like:
<% form_tag(search_path, :firstname => 'some_text', :lastname => 'some_text', :method => 'get') do %>
or at least something like this. HTML tag form has parameter action='/some/url' and that's why you have to specify all parameters for search_path. But the above example won't work as you expected.
So what you can do?
Create empty form that has action='/' and with js replace it with content of your input text fields before submitting.
Create another route, on example /search that recives parameters from submit and then redirects to correct path.
Probably there is also some better ways to do it ;)
First, search_path is actually a method, which takes a hash of options. It is this method which should receive :first_name and :last_name.
Second, a browser can only submit form parameters as the body of a POST request, or as query string parameters (for any kind of request method). So there's unfortunately no way a browser's native submit function can generate that kind of URL.
Another way of thinking of it: What you're doing here is filling the form tag's action attribute with an URL. Rails needs a complete URL as you're building the form. So all parameters in your route need to be specified when the form helper is called, rather than at the next POST request.
So unfortunately, what you're trying to do is not possible in a normal Rails application.
(If you really want to, you might be able to pull it off by writing your own form helpers and a bit of Javascript to replace the browser's native submit function. The Javascript would then construct that URL based on the form fields. I'd argue against it, though.)

Resources