I'm in chapter 7 of Michael Hartl's Rails Tutorial and I'm having a hard time understanding the "_path"s that he calls on for a particular test (section 7.3.4 to be exact). Here's the code in question:
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, user: { name: "",
email: "user#invalid",
password: "foo",
password_confirmation: "bar" }
end
assert_template 'users/new'
end
end
In particular, I'm confused about get signup_path and post users_path. When I go back and search the book for signup_path, Table 5.1 indicates it is the named route for [sample_app url]/signup which should direct visitors to the signup page of the website. Additionally, when I search the book for users_path, Table 7.1 indicates that it is the named route for [sample_app url]/users using the 'create' controller action because we are issuing a POST HTTP request.
NOTE: In responses, please don't suggest altering this code. This is based on Hartl's Rails Tutorial and the assumption is that the code is correct and should work as is. Please make sure your answers explain the code I've posted (or other code from his tutorial) and not modifications to the existing code.
EDIT: OK, so I think I can see the difference between signup_path and users_path: in the routes.rb file, /signup routes to users#new, which means it is routed to the users controller and specifically the 'new' method of that controller. I know the result of this is loading the new.html.erb file. Part of what I don't get is -- how does the users#new controller action know which HTML page to load? Does the 'new' method automatically call whichever view is also called 'new'? OR... (Here's the relevant controller code for the new method:
def new
#user = User.new
end
The other part of my problem is with users_path: rake routes says the POST method for /users is routes through the users#create controller/action. But my routes.rb file doesn't have a route to reflect this:
Rails.application.routes.draw do
root 'static_pages#home'
get '/help' => 'static_pages#help'
get '/about' => 'static_pages#about'
get '/contact' => 'static_pages#contact'
get '/signup' => 'users#new'
resources :users
end
So the second part of the question is: How does the routes file know to route the post users_path to the users#create controller/action?
The dont point to the same page, GET signup_path will GET you to the page where user can sign up while POST users_path is a different route where you send parameters that you have generate on GET signup_path.
Also you dont need to search tables to see to what URL each _path points, just type
rake routes
in command prompt and you will see what each path is.
This small line
resources :users
is actually a shortcut for seven different (but very common) routes that cover all CRUD (create, read, update, and delete) operations on a resource. One of them is exactly:
users_path POST /users users#create create a new user
Related
There seems to be something wrong with my routing paths. Normally I should be able to do something like <%= link_to Profile, user_path(#user||current_user) %> and I move on with my day. For whatever reason I'm failing to understand my user_path is not returning /user/:id like I would expect it to do. Instead it is returning /user.:id
To test this, I loaded a partial with the following code.
app/view/users/_test.html.rb
<%= #user %><br>
<%= #user.id %><br>
<%= link_to user_path(#user), user_path(#user) %><br>
<%= new_user_path %><br>
<%= edit_user_path(#user) %><br>
<%= url_for(#user) %>
This returned
localhost:3000/test
#<User:0x007fb6cd341f08>
1
/user.1
/users/new
/users/1/edit
/user.1
I can't figure out what is causing this to happen. The edit_user_path(#user) works perfectly, but the show doesn't. I have read the Rails Routing Guide from top to bottom about three times and I can't figure it out. The closest I found to my problem was on an old Rails 3.1 gem problem with Devise, but I'm not even using the Devise gem (maybe I should be?).
Why is my route failing? I'm not really looking for a workaround (though I suppose I'd rather have a workaround than no solution), I want to understand why this is happening and fix it. What's going on?
My routes are pretty vanilla, nothing special going on there, but just in case the problem is there and I missed it, here it is.
config/routes.rb
Rails.application.routes.draw do
root 'static#home'
%w( 404 406 422 500 503 ).each do |code|
get code, :to => "errors#show", :code => code
end
#USER PAGES
get '/test' => 'users#test'
get '/signup' => 'users#new'
post '/user' => 'users#create'
get '/user/list' => 'users#index'
post '/user/' => 'users#update'
get '/user/:id' => 'users#show'
get 'profile', to: 'users#show'
resources :users
end
I'd suggest updating your routes as follows:
config/routes.rb
Rails.application.routes.draw do
root 'static#home'
%w( 404 406 422 500 503 ).each do |code|
get code, :to => "errors#show", :code => code
end
#USER PAGES
get '/test' => 'users#test'
get 'profile', to: 'users#show', as: :user_profile
resources :users
end
...as resources :users creates all of the routes I've removed.
The duplicates may have been overwriting a default path, causing the behaviour you're seeing - if you pass an object to a url helper that doesn't expect a parameter, you see the behaviour you're getting (i.e. doesnt_have_an_id_path(#object) => /doesnt_have_an_id.1).
I've also added a name to the user profile path to avoid this clashing. See if this works (perhaps one step at a time to get the cause and effect) - otherwise, try temporarily removing the profile route.
Re that path, you may have a problem in that the users#show action will expect an :id parameter, yet that route doesn't allow for one. This might be causing the current problem, though if not, may cause issues down the line.
Hope that fixes it - shout if you've any questions / feedback when you've tried it out.
In your case the two conflicting routes are:
post '/user/' => 'users#update'
get '/user/:id' => 'users#show'
You are trying to use user_path helper method, thus you need to have user named route.
If you run rake routes for these two routes you will see this:
Prefix Verb URI Pattern Controller#Action
user POST /user(.:format) users#create
GET /user/:id(.:format) users#show
The Prefix column shows you the named routes which are defined for your application. In this case Rails auto-generated named route user for POST /user endpoint. That means user_path will return /user instead of /user/:id as you expected. Rails generate routes like these when it sees a route definition without parameters. For example if you have get /user/some/more in your routes, Rails will auto-generate named route user_some_more for you and they you will be able to use user_some_more_path helper.
In order to fix your particular case you can stop rails from generating route for POST endpoint by doing this: post '/user/', as: nil and give GET endpoint a name you want: get '/user/:id', as: 'user'. Then you will be able to use user_path(user) to generate paths of format /user/:id.
I have created a user signup page. When the user submits the form incorrectly, when displaying a validation error, it does not render on the same URL.
The signup form is located at this url:
http://localhost:3000/signup
I have added the following routes for the signup page:
resources :users
match '/signup', to: 'users#new', via: 'get'
When I submit the form, the model validation shows up but the url redirects to:
http://localhost:3000/users
I want the user to remain at the same url after submitting the form.
http://localhost:3000/signup
This is my controller code:
def new
#user = User.new
end
def create
#user = User.new(user_params) # Not the final implementation!
if #user.save
# Handle a successful save.
else
render 'new'
end
end
This is the beginning tag of my form:
<%= form_for #user, url: {action: "create"} do |f| %>
I'm working with Rails 5, but I suspect it's not too different conceptually.
In routes.rb:
get 'signup', to: 'users#new'
post 'signup', to: 'users#create'
resources :users
and in your form helper:
<%= form_for #user, url: signup_path do |f| %>
Essentially, the reason why it's not working as described in your original post is because when the form posts (rails 4.2 form helpers), it's tied to url: {action: "create"}, which by default, according to the routes automatically generated for resources :users, is post '/users', to: 'users#create (rails 4.2 routing guide). And by searching top down in routes.rb, it'll see this first and you end up with http://localhost:3000/users.
So, the changes I propose, should bind the form to post to the signup path instead which, in routes.rb, is seen as the second route entry.
I believe that #Jaskaran's question is in regards to question 2 found at the bottom of Michael Hartle's Ruby on Rails Tutorial, section 7.3.3, so I will be answering in reference to that question:
How does the URL on the unsubmitted signup form (Figure 7.12) compare to the URL for a submitted signup form (Figure 7.18)? Why don’t they match?
To answer this question, we need to understand how routing works in a Rails application. Let's walk through what happens when you visit each page and make a submission.
Your main page (home.html.erb) contains a link to your signup page (new.html.erb). That link looks like this:
<%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
The signup_path is the part we are most interested in. signup_path is a route helper and essentially tells Rails that when someone clicks on that link, Rails should behave as if they just went to your /signup page. This is why the first URL you see (before submitting the form) shows http://localhost:3000/signup.
Now let's take a look at what happens when your form is submitted. According to the new.html.erb file, the form is contained within this Ruby code:
<%= form_for(#user) do |f| %>
. . .
<% end %>
You may remember from 7.2.2 that that Ruby code is actually rendered in HTML as:
<form class="new_user" id="new_user" action="/users" accept-charset="UTF-8" method="post">
. . .
</form>
What that is saying is that when the form is submitted, it will make a POST request to /users - and that's the URL that you are seeing in your browser.
We can better understand why this is if we first stop to think - where does /users route to? You can quickly check your routes by typing bundle exec rake routes in your console. You should see it listed there:
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
So any POST requests to /users will be routed to the create method of our users_controller.rb. If you're wondering why that is, it is because we added this line to our routes.rb file:
resources :users
More details on that line can be found here.
I'm using RSpec (through the gem rspec-rails) for testing and developing
my application.
I've tried to "test" a controller and run up against a strange behavior of post and get methods (same for all the others of this kind).
In my route file:
controller :sessions do
post '/login', action: :login_create
get '/login', action: :login
get '/logout', action: :logout
end
At the beginning, I was thinking that post will simulate an http post
request at the specified url, so I've wrote in my spec:
describe "POST 'login'" do
it "returns http success" do
post 'login'
response.should be_success
response.should render_template 'sessions/login_create'
end
end
But this will call the login action, not the login_create, and then the last assert fail.
After a lot of googling and experiments, I've changed post 'login' with post :login_create and this actually works!
The strange thing is that also if I change post with get, it will continue to work! O_o
Isn't this strange? How this methods are intended to work and to be used?
In the Rails API I've not found anything else than the class: ActionController::TestCase::Behavior
What you're writing is a controller spec, not a request spec. In a controller spec, routes are not consulted at all because rspec invokes controller actions directly.
When you write post 'login', the login specifies the action name, not the URL path.
The correct way to test the login_create action would be to use post :login_create as you've discovered.
Apparently I have a unique situation that I can't find help with anywhere. I'm trying to extend my sign up process to create extra models in my database. Here is a basic flow of what I'd like to do:
User Signs up with Email/Password (along with other model values)
System creates a 'User' model
System creates a 'Profile' model
System creates a 'Company' model
System creates an 'Account' model
The biggest challenge is that I'm using Devise and I can't seem to figure out a way to test this functionality with Rspec. Here is a quick view of me simply trying to test the 'Sign Up' method (which does not work:
describe "New Users" do
describe "signing up" do
it "should create a new user" do
lambda do
post :sign_up, :user => Factory.attributes_for(:user)
response.should be_success
end.should change(User, :count).by(1)
end
end
end
I get the following error:
1) UsersController New Users signing up should create a new user
Failure/Error: post :sign_up, :user => Factory.attributes_for(:user)
ActionController::RoutingError:
No route matches {:user=>{:email=>"test#user.com", :password=>"secret", :password_confirmation=>"secret"}, :controller=>"users", :action=>"sign_up"}
# ./spec/controllers/users_controller_spec.rb:14:in `block (5 levels) in <top (required)>'
# ./spec/controllers/users_controller_spec.rb:13:in `block (4 levels) in <top (required)>'
The Devise routes configure my user sign up routes as follows:
new_user_registration GET /users/sign_up(.:format) {:action=>"new", :controller=>"devise/registrations"}
I've been pulling my hair out with this, and can't seem to find any help in this area. Perhaps I'm approaching it wrong, but I want to configure all the aspects of setting up a user's account (ie. their company defaults, profile settings, etc..) on the initial sign up form. First I need to just figure out how to test the sign up process.
Thanks in advance!
First of all the routes you should link to is, because devise are using a POST method for sign-up:
user_registration POST /users(.:format) devise/registrations#create
Maybe you can try pass there syntax such as:
it "should create a new user" do
expect {
post :create, user: FactoryGirl.attributes_for(:user)
response.should be_redirect
}.should change(User, :count).by(1)
end
EDIT
Ok then, I maybe figure out how to solve the route problem, I added a new controller:
UsersController < Devise::RegistrationsController
and also added there method sign_up. Then in routes.rb I added this code:
devise_for :users
devise_scope :user do
post "/sign_up", :to => "devise/registrations#create"
end
and finally this line before the test into the test:
#request.env["devise.mapping"] = Devise.mappings[:user]
But I have there problem with validations, but I think it is for my app because I just made it in new branch on existing system. Hope it could help you. Nevertheless, I think this approach is quite hack or not common. I would rather go with recommended Devise approach. You can always create an user dependencies with FactoryGirl viz this link. Hope it will help you.
BTW Sry for the nonsense post about sign in:) I was tired:)
Isn't it
post :users
The new_user_registration route redirects to the page containing the form (new user action), not the create user action. You should find something like in your routes:
user_registration POST /users {:action => "create", :controller => "devise/registrations"}
Check you rake routes.
I have this setup for Devise
devise_for :users, :controllers => {
:confirmations => "confirmations",
:registrations => "registrations"} do
put "confirm_user", :to => "confirmations#confirm_user"
end
and when I run
rake route
I have get strange routes for registrations_controller specialy the edit_path
edit_user_registration GET /users/edit(.:format) registrations#edit
PUT /users(.:format) registrations#update
DELETE /users(.:format) registrations#destroy
The problem is edit url for example for first user.
I expected
/users/1/edit
but I have get
/users/edit.1
I expect this route can not work but it does. Now I am not sure if I have made some mistake or if the devise generate the routes this way.
And if it generate routes that way where goes the format of request?
I can not believe that the URL might look like this.
/users/edit.1.js
Thanks for any advise?
The issue is not related to the edit url, instead it depends on the page that is linking to the edit one. You have probably a link of this form
link_to "Settings", edit_user_registration_path(#user)
that point to the edit url, which generates the unexpected url
/users/edit.id
You simply have to replace the link omitting the #user, as
link_to "Settings", edit_user_registration_path
That . is always there when showing a format. It's nothing from Devise, and there's nothing wrong with it. You're all good!
I am passing the id to the edit route but it doesn't expect an id.
The edit_user_registration_path is only for current_user so the user.id is unnecessary.
This question might be also helpful.
Devise: Allow admins to edit other users - Rails