Rails form using GET method regardless of what is specified - ruby-on-rails

I have an HTML form intended to be used for password resetting. Its method is PATCH and it should be hitting the update method of the PasswordResetController. However, on submission, it is always make a GET request, regardless of the method specified. I can type method=ANYTHINGHERE and it always makes a GET request.
My form is simple:
<form class="group" action="<%= password_reset_url %>" method="PATCH">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
<input type="password" class="password-login" name="new_password" placeholder="New Password">
<input type="password" class="password-login" name="new_password_verify" placeholder="New Password Confirmation">
<button class="login-button">Reset Password</button>
</form>
My routes seem to be in order:
password_reset_index POST /password_reset(.:format) password_reset#create
new_password_reset GET /password_reset/new(.:format) password_reset#new
edit_password_reset GET /password_reset/:id/edit(.:format) password_reset#edit
password_reset PATCH /password_reset/:id(.:format) password_reset#update
PUT /password_reset/:id(.:format) password_reset#update
defined as resources :password_reset, only: [:new, :create, :edit, :update]
And my controller:
class PasswordResetController < ApplicationController
new, edit, create....etc.
def update
user = ...
end
end
Create, new, and edit all work perfectly fine, but it's not even getting into the update method when submitting the form. I just get No route matches [GET] "/password_reset/longTokenString. Any ideas? I feel like I've exhausted my options here.
This is not a duplicate of this question as the token string is being properly generated. The token string is also used as the id in the /password_reset/:id/edit route, and is working fine there as well.
Thanks in advance.

According to the rails strategy here is the clear explanation if what is the difference between the PUT and PATCH methods.
If you are coming from a previous version of Rails, you may be wondering why the update action of a RESTful route is mapped to the HTTP verb PATCH instead of PUT. In the HTTP standards document RFC 57892, it outlines that a PUT request to a given resource is meant to completely replace it on the origin server. However, when updating a resource in Rails, rarely, if ever, do you replace an entire resource when performing an update. For example, when updating an Active Record model, Rails sets the attribute updated_at timestamp, not the requesting client.
To follow better HTTP semantics, Rails will be using the HTTP verb PATCH for updates. PATCH allows for both full and partial updates of a resource, and is more suited to how Rails updates resources.If you are upgrading an existing Rails application, the HTTP verb PUT will still map to the update action in
RESTful routes, but it’s recommended to use PATCH moving forward.
I am going to explain how the User Edit option works in rails.
app/controllers/users_controller.rb
def edit
#user = User.find(params[:id])
end
The corresponding user edit view
app/views/users/edit.html.erb
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Save changes", class: "btn btn-primary" %>
<% end %>
Looking at the HTML source
<form accept-charset="UTF-8" action="/users/1" class="edit_user"
id="edit_user_1" method="post">
<input name="_method" type="hidden" value="patch" />
.
.
.
</form>
Note: Note here the hidden input field:
<input name="_method" type="hidden" value="patch" />
Form the Documentation:
method: (:get|:post|:patch|:put|:delete)
"In the options hash. If the verb is not GET or POST, which are natively supported by HTML forms, the form will be set to POST and a hidden input called _method will carry the intended verb for the server to interpret."
Reference: http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html
Hope so my explanation would be clear for understanding the concept. Happy Coding:)

According to the HTML standard, you can not add put as method for form. The only valid values for the method attribute are get and post. <form method="put"> is invalid HTML and will be treated like <form>, i.e. send a GET request.
You can also write form code by using rails form tag.

Some points to remember---
if you are making use of form_for tag,you dont have to worry about anything else because rails itself uses its Record identification to handle create(POST),new(GET) and update(UPDATE/PATCH) actions.
if you are not using form_for tag uniformly,which is your case,you must match the url exactly,So you need to pass the id too.
example:-
<form class="group" action="<%= password_reset_url(#your_model.id) %>" method="PATCH">
HOPE IT HELPS :)

Related

Rails form use token instead of id in action

In a standard rails form_for like below, it will return an html form where the action is /posts/1/comments. But including the post.id is not good for security, instead i want it to use the post.token for that record instead.
Rails form_for
<%= form_for([#post, Comment.new]) do |f| %>
<%= f.text_field :text %>
<% end %>
Html of form_for
<form class="new_comments" id="new_video_marker" action="/posts/1/comments" accept-charset="UTF-8" method="post">
<input type="text" name="comments[text]" id="comment_text">
</form>
You need to create a token.
$ rails generate migration AddTokenToPost post_token:string
Modify your post.rb :
# app/models/post.rb
class Post < ApplicationRecord
before_create :set_post_token
private
def set_post_token
self.post_token = generate_post_token
end
Try this for your form :
<%= form_for([#post, Comment.new]) do |f| %>
<%= f.hidden_field :token %>
<%= f.text_field :text %>
<% end %>
But including the post.id is not good for security
Who fooled you into thinking that?
This is a classic example of security by obscurity. Sure somebody can't just guess that the url is /posts/1/comments but if you're not authorizing the resource properly /posts/somereallylongtoken/comments is no more secure. If not UUID's would be magically more secure and there would be no one using auto-incrementing integers for PKs.
You can use single use tokens which are placed in the URL for stuff like user confirmations but thats only because you're sending a link in a email and you're limited to GET. Thats used as a nonce to authorize the user. Not just to obfuscate an ID.

Rails - Simple Forms - undefined method `model_name' for nil:NilClass

I am trying to make a form where you select tables that you want to export. I made a simple form with a list of tables that can be exported. My plan was to allow the user to toggle check boxes for the tables they want to export and as a result they would be able to download a zip file containing the tables.
Currently, when I try to go to the page with the form, I get an error:
undefined method 'model_name' for nil:NilClass
The majority of the usage of simple forms that I see online consists of using forms to create new items to save in their models. As a result, it seems that the line simple_form_for #example would mean that when the user clicks the submit button, there is a line in the controller such as #example = SomeClass.new". My understanding is that the user input of the form is saved in #example and can be used by the controller. However, as I am not creating a new item in the model, I just want to use the values from #example, I am not sure what to put in the controller to get rid of the error so that I can code the rest of the function in the controller.
Controller:
class FormController < ApplicationController
def index
#options = []
print(#options)
end
end
The form used:
<h2>Which tables do you want to export?</h2>
<div class="well">
<% tables_in_model = %w(Table1 Table2 Table3) %>
<%= simple_form_for #selected_options, :url => form_index_path, :method => :get do |f| %>
<%= f.input :options, as: :check_boxes, collection: tables_in_model %>
<%= f.button :submit, as: :Submit %>
<% end %>
</div>
As you said correctly in your question, simple_form should be used to render forms to the user when her actions are related to the creation or edition of ActiveRecord models.
For instance, when writing down code to enable a search feature, where your goal is to simply pass a bunch of user chosen params to a controller, you should not use it. I believe you are in a similar position with the feature you described.
Simple solution though: use rails form related DSL to get your form going!
Hope it's the answer you needed. Feel free to ask for more details if needed. Cheers!
i think you are using simple_form you need to specify like
#selected_options = SelectedOptionModel.new(params)
into your controller
then it passes into View.
if you don't have any model you can use form_tag
like this:
<%= form_tag("/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
this will create html form like this:
<form accept-charset="UTF-8" action="/search" method="get">
<input name="utf8" type="hidden" value="✓" />
<label for="q">Search for:</label>
<input id="q" name="q" type="text" />
<input name="commit" type="submit" value="Search" />
</form>

Why does this form helper generate two inputs?

On a model, I have attr_accessor: :email_settings.
In a view, I have:
<%= form_for some_model do |f| %>
<%= f.fields_for :email_settings do |email_settings| %>
<%= email_settings.label :general, _("General updates") %>
<%= email_settings.check_box :general %>
General site updates
<% end %>
<% end %>
But in the HTML, this does not create one input for some_model[email_settings][general], it creates two. One hidden and one a checkbox:
<label for="user_email_settings_general">General updates</label>
<input name="user[email_settings][general]" type="hidden" value="0">
<input id="user_email_settings_general" name="user[email_settings][general]" type="checkbox" value="1">
General site updates
What's going on here? Why is there a hidden input and a checkbox for the same value, when I only want a checkbox?
Also in the controller action that the form is submitted to, I do this:
def update
puts "email_params: #{email_params}"
end
def email_params
params.require(:user).permit(:email_settings)
end
Which outputs:
Unpermitted parameters: email_settings
email_params: {}
Not sure how email_settings is being interpreted as "unpermitted" when I'm explicitly permitting it.
From the browser should always send a response.
If you do not select the box, will send the hidden field to understand that something has been sent.
In this way the rails will always receive an indication that the box was selected or not.
similar answer is here: Why does the check_box form helper generate two checkboxes, one hidden?

simple_form delete method instead post method

In simple_form is possible to use the http delete verb instead the default post verb?
<%= simple_form_for #object , method: :delete do |f| %>
<%= f.input :instance_name, as: :check_boxes, collection: #roles %>
<%= f.button :submit %>
<% end %>
It doesn't works.
Unfortunately simply stating that it doesn't work is not helpful in understanding the problem you're seeing, but I'll make a guess based on my own initial confusion with the "method:" parameter. Most browsers don't support PUT and DELETE methods, so what simple_form_for does is generate a form with a POST method, but it also adds a hidden field to pass the actual method. So:
simple_form_for #service, url: service_path, method: :delete
generates:
<form action="/services/6" method="post">
<input name="_method" type="hidden" value="delete" />
....
Rails uses that to call the correct controller method. Hope that helps.

button_to works only intermittently, and often results in a "no route" error

Added on edit, 2013-02-11:
I should make it clearer that the problem I'm having is not that I can't get the ERB code I write to produce correct HTML code. Rather, it is that my Rails installation sometimes interprets the HTML code correctly, so that clicking the link has the right effect, and sometimes incorrectly, producing a "No route" error. More details in my answer below.
Original question, 2013-02-08:
I'm making a simple login form using Rails 3.2.11. I've tried several ways of coding the Submit button of the form, but each has failed due to a missing route error. I got one method to work, and checked in my code. However, now the very same code fails.
This is the code of app/views/session/new.html.erb (I'm using simple_form, but not its button wrapper):
<h2>Log in</h2>
<%= simple_form_for :session do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= button_to "Submit", session_index_path %>
<% end %>
The relevant part of the HTML code that this produces is:
<h2>Log in</h2>
<form accept-charset="UTF-8" action="/session/new" class="simple_form session" method="post" novalidate="novalidate">
<div style="margin:0;padding:0;display:inline">...</div>
...the input fields...
<form action="/session" class="button_to" method="post">
<div>
<input type="submit" value="Submit" />
<input name="authenticity_token" type="hidden" value="...token value here..." />
</div>
</form>
</form>
This is my config/routes.rb:
MyApp::Application.routes.draw do
resources :at_user
resources :session, :only => [:new, :create, :destroy]
match 'login' => 'session#new', as: :login
match 'logout' => 'session#destroy', as: :logout
root to: 'main#index'
end
This is what the command rake routes outputs:
at_user_index GET /at_user(.:format) at_user#index
POST /at_user(.:format) at_user#create
new_at_user GET /at_user/new(.:format) at_user#new
edit_at_user GET /at_user/:id/edit(.:format) at_user#edit
at_user GET /at_user/:id(.:format) at_user#show
PUT /at_user/:id(.:format) at_user#update
DELETE /at_user/:id(.:format) at_user#destroy
session_index POST /session(.:format) session#create
new_session GET /session/new(.:format) session#new
session DELETE /session/:id(.:format) session#destroy
login /login(.:format) session#new
logout /logout(.:format) session#destroy
root / main#index
The target path of button_to is session_index_path, which should cause the create method of SessionController to be called -- and for a while, it did. Now, after I've restarted Rails, pressing the button instead produces an error page, with the text
No route matches [POST] "/session/new"
For some reason, Rails has started to think that the target of button_to is session#new instead of session#create. It's as if it thinks that the HTTP method it's supposed to call is GET instead of POST -- however, the HTML code shows that the method is post.
By the way, another thing I earlier tried was to give button_to the action and method parameters, as documented here:
<%= button_to "Submit", options: {action: 'create', method: :post} %>
then this is what's generated:
<form action="/session/new?options%5Baction%5D=create&options%5Bmethod%5D=post" class="button_to" method="post">
which doesn't look like what I want, either. button_to's default HTTP method is POST, which appears in the result, but the :options hash is just tacked onto the end of the URL, and the word create appears nowhere else.
The first answer to this question says that, unlike what the documentation says, one should not put the parameters of button_to in a hash, but give them directly. So, I tried this:
<%= button_to "Submit", action: 'create' %>
However, the action create still does not show up in the generated HTML:
<form action="/session" class="button_to" method="post">
So, those were things I tried before attempting to using the named path method, which worked for a minute, but for some mysterious reason, doesn't anymore.
Any ideas what I'm doing wrong? (It's probably something obvious I've overlooked.)
The button_to documentation is actually wrong in a few places (Note my answer here: Why is this button_to rendering incorrectly in Rails 3.2.11?). To fix that Submit button, use this:
<%= button_to "Submit", { action: 'create' }, method: :post %>
The options and html_options hashes need to have explicit separations defined using the {} braces depending on what you are trying to accomplish.
The create action won't show up in the URL since a POST HTTP method to /session is routed to the create action.
So far, the method suggested by Cyle in a comment above seems to work best, namely, adding url: session_index_path to the simple_form_for declaration:
<h2>Log in</h2>
<%= simple_form_for :session, url: session_index_path do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= button_to "Submit" %>
<% end %>
I haven't yet experienced a route error when clicking the "Submit" button produced by this code.
I'm not accepting this answer, though, because the base problem, which I haven't solved yet, is the fickleness of Rails; that is, code that has worked before suddenly stops working. For instance, today I first tried this code:
<%= simple_form_for :session do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= button_to "Submit1" %>
<%= button_to "Submit2", session_index_path %>
<% end %>
Pressing the "Submit2" button worked. However, when I removed the line that contained button "Submit1", stopped and restarted the Rails server (as I've done every time between attempts), and reloaded the page, pressing the Submit2 button produced a "No route" error, even though the HTML code of the button remained the same:
<form action="/session" class="button_to" method="post">
<div>
<input type="submit" value="Submit2" />
<input name="authenticity_token" type="hidden" value="...this value was the same also..." />
</div>
</form>
Very puzzling. Might be related to the details of my setup, though it isn't that exotic.
Added on edit:
When I took look at the relevant part of the HTML that is produced by Cyle's method, I noticed that it's the same as for almost all the other cases:
<form accept-charset="UTF-8" action="/session" class="simple_form session" method="post" novalidate="novalidate">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" ... />
</div>
...the username and password input fields...
<form action="/session" class="button_to" method="post">
<div>
<input type="submit" value="Submit" />
<input name="authenticity_token" type="hidden" value="voOGIAbJfwvjxVRbk02V5l6zn6iwvOiMvaeauPBIrqU=" />
</div>
</form>
So it might be just luck that this has worked for me so far.
The problem was most probably that after I created the application, I decided to change the names of a couple of the classes and the associated files. This resulted in class and file names that didn't follow the Rails convention on pluralizing nouns in certain contexts. For instance, I had a file session_controller.rb that contained a class named SessionController; the correct names are sessions_controller.rb and SessionsController.
I created a dummy Rails application, and then commanded rails generate scaffold AtUser and rails generate scaffold Session, and then used the resulting filenames and identifiers as a guide for correcting the actual application. Now, I don't get those "No route" errors anymore. The command rake routes outputs:
at_users GET /at_users(.:format) at_users#index
POST /at_users(.:format) at_users#create
new_at_user GET /at_users/new(.:format) at_users#new
edit_at_user GET /at_users/:id/edit(.:format) at_users#edit
at_user GET /at_users/:id(.:format) at_users#show
PUT /at_users/:id(.:format) at_users#update
DELETE /at_users/:id(.:format) at_users#destroy
sessions POST /sessions(.:format) sessions#create
new_session GET /sessions/new(.:format) sessions#new
session DELETE /sessions/:id(.:format) sessions#destroy
login /login(.:format) sessions#new
logout /logout(.:format) sessions#destroy
root / main#index
Before this, I saw a "No route" error also when I included a file in another file without having required the former at the top of the latter. After performing the renaming fix, I tried to reproduce this error, and it did reoccur: Rails mistakenly reports a missing route, when the actual problem is a missing require statement. This suggests that Rails's error reporting mechanism is buggy.
Thanks to user bullfrog for pointing me in the right direction.

Resources