2

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&amp;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.)

Community
  • 1
  • 1
Teemu Leisti
  • 3,750
  • 2
  • 30
  • 39
  • Why are you using `button_to` as opposed to simple_forms `submit` button? – Noz Feb 08 '13 at 15:52
  • I tried that, too, earlier. I just tried it again: The ERB code `<%= f.submit %>` produces the HTML code ``, but clicking on the button again results in the error `No route matches [POST] "/session/new"`. – Teemu Leisti Feb 08 '13 at 16:01
  • Try changing `<%= simple_form_for :session do |f| %>` to `<%= simple_form_for :session, :url => session_index_path %>` – Noz Feb 08 '13 at 16:16
  • That, combined with just `<%= button_to "Submit" %>`, works. Now. As have other things earlier, but intermittently. I'll edit my question to make the problem more clear. – Teemu Leisti Feb 11 '13 at 10:05
  • 2
    Just wondering.. Rails convention is to have a pluralized controller name. So your resources should really be `sessions` and `at_users`. This would get rid of those ugly `_index` url paths, this might also solve alot of inconsistent issues you might be facing. – bullfrog Feb 11 '13 at 14:37
  • With `button_to` I generally pass the path with the options `button_to "Submit", sessions_path` – bullfrog Feb 11 '13 at 14:45
  • A good point, bullfrog. When creating this app, I did mess around a bit with the names, and might have ended up confusing where a plural was required and where a singular was. (Am I the only one who thinks that the Rails way of "automatically" pluralizing some things causes more confusion than it prevents?) I'll try creating the files from scratch with `rails generate` and then see what happens. – Teemu Leisti Feb 11 '13 at 14:47
  • yeah your not the only one.. I sometimes wondered why some things dont work and come back and realized I missed out on an `s` somewhere – bullfrog Feb 11 '13 at 14:49
  • After correcting the plural/singular forms, everything seems to work. I wrote and accepted a new answer based on that. Thanks. – Teemu Leisti Feb 12 '13 at 17:41

3 Answers3

1

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.

Community
  • 1
  • 1
Marc Baumbach
  • 10,323
  • 2
  • 30
  • 45
  • I just tried that, and unfortunately, it made no difference: the generated HTML is still `
    `. (By the way, I've made sure to stop and restart the Rails server between attempts, to obviate any possibility of caching errors.)
    – Teemu Leisti Feb 08 '13 at 15:53
  • In answer to your edit: yes, the path should work -- and now it does work! So the problem isn't really that I'm not using `button_to` incorrectly, but that Rails behaves inconsistently, sometimes interpreting the generated HTML form as a link to `/session/create`, and sometimes to `/session/new`. – Teemu Leisti Feb 08 '13 at 15:56
1

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="&#x2713;" />
    <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.

Teemu Leisti
  • 3,750
  • 2
  • 30
  • 39
1

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.

Teemu Leisti
  • 3,750
  • 2
  • 30
  • 39