I am building my first rails application using the Devise gem for authentication.
The login works great but the new registration (sign up) page delivers an error on the PAGE and will not let me register.
See image
Is this an issue with strong parameters or my application controller. Your help would be greatly appreciated.
This is probably because you have validations enabled in your User model where you specified the email and password can't be blank.
So, you have to provide email and password of a user when trying to save the info.
Update
If that's not the case, then it could be related to strong parameters. See this thread.
In your application_controller.rb file, add the following:
before_filter :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:email, :password, :password_confirmation) }
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:email, :password, :password_confirmation) }
end
Configure your other actions like above too.
More update
The main issue was in the sign up form as it was not using the form builder properly and was not posting the params. I modified the app/views/devise/registrations/new.html.erb file to this:
<h2>Be cool and sign up if you dont have an account:</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="col-md-4">
<h2>Signup</h2>
<form class="form-signup">
<div class="form-group">
<label for="email">Email</label>
<%= f.text_field :email, class: "form-control", placeholder: "First Name", tabindex: "2" %>
</div>
<div class="form-group">
<label for="password">Password</label>
<%= f.password_field :password, class: "form-control", tabindex: "2", placeholder: "Password", required: true %>
</div>
<div class="form-group">
<label for="password_confirmation">Password</label>
<%= f.password_field :password_confirmation, class: "form-control", tabindex: "2", placeholder: "Password Confirmation", required: true %>
</div>
<div><%= f.submit "Sign up", class:"btn btn-primary"%></div>
</form>
<%= render "devise/shared/links" %>
</div>
</div>
</div>
<% end %>
</div>
making sure the params are posting to the controller properly, and everything is working as expected now.
With what I can see, and what you explained:
you filled in the email and password field
it went through your controller and then to the model
there is validation in your model that is complaining of the absence of email and password.
I am assuming that you are using the sign-up page generated by Devise (this is already set up to get a new user form, bound with a new instance of the User class).
From the above three points (and the assumption), it stands to reason that both the email and password that you filled in are getting lost somewhere along the way.
My guess will be that this is happening somewhere in your controller. According to your routes, that will be the registrations controller (the create action).
Do you have a custom registrations#create controller? if yes => I think your params are not being allowed into building the new User which the save action is being called on.
If that is the case, you will need something like:
def new_user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
and then building and saving the new user as:
#user = User.new(new_user_params)
#user.save
Hope this helps to throw some light into it...
Related
I'm new to rails (using rails 6), and I have a question that I didn't see answered on the site for my specific use case.
The basic scenario is the same as in many question - adding role based authentication to a rails application when using devise.
The way I implemented this was to create a roles table, and having a one to many relations between it and the users table.
I need only two users: guest and admin, and they have 1, 2 ids, respectively.
I inserted the values into the roles table manually, using the rails console.
Now, I'm having trouble with the sign up form, which is the default one devise gem created:
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
What I want to add is a select field that will show "admin" and "guest", and when the user submit the form, the right role_id will be added to the user record in the user table, but I don't get how to inform the devise view on the role table existence and how to match the string value of the role with the role id the I want the user to accualy have.
So my questions are:
How to add the roles to a select field in the devise sign up form?
How to handle after the user selects one of the roles string names, and match it with role id that will be added to the user when the form is processed?
I saw the following questions and many like them, but I didn't saw any of them dealing with matching string with id before the form is submitted:
Devise Add Admin Role
how to automatically add a role to a user when signing up using devise
Adding Fields To Devise Sign Up Using Rails 4
Multiple models associated with devise user - fill in one model based on selection of role
If there is a question answering my issue, I'm sorry and I would be glad to get the link and close this question.
If there is any more info you need, please let me know.
Thanks!
Using an enum to differentiate admins from guests seems like the best option and will prevent you from having to complicate your model/form with unnecessary things like nested attributes.
Here's an example...
Create a role column on the users table.
$ rails g migration add_role_to_users role:integer
In your migration file, make sure to set the default to 0.
add_column :users, :role, :integer, default: 0
Then, after migrating the db, in your User model add the enum...
class User < ApplicationRecord
enum role: { guest: 0, admin: 1 }
...
end
Adding another role is as simple as adding a key/value to the role enum.
Then, in your devise form, you can add something like...
<%= f.label :role %>
<%= f.select :role, User.roles.keys %>
You will also need to make sure that you are adding role as a permitted param...you seem to be adding it as a field in signup, so in ApplicationController...
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:role])
end
end
If you go with this you will also have to remove your roles table and associations.
I'm new to RoR and I am having some trouble understanding some of the code.
I tried looking it up but the results haven't helped me.
Here is the code located in the user controller. (If you need any other code, comment it and I'll update
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params) #I didn't see any parameters in the constructor
if #user.save #Checks if #user was saved?
session[:user_id] = #user.id #Creates a session? What's :user_id and #user_id?
redirect_to'/' #Redirects to http://localhost:8000/
else
redirect_to '/signup' #If all fails go back to signup page
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password)
end
end
This is part of a programming course which failed to explain this to me properly. I'm generally aware that this is for a signup form, but I am having trouble comprehending the create and user_params function processes.
When I'm asking for help I am asking you to lead me through the process of what is happening.
I also need specific help with
params.require(:user).permit(:first_name, :last_name, :email, :password)
#user = User.new(user_params) #I didn't see any parameters in the constructor
user_params is the name of a method. In ruby, you can call a method without writing () after the method name. If you look down at the bottom of the code you posted, you can see the method definition:
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password)
end
end
That method returns something, and that return value is used in the constructor. You can see what the return value is by adding the following to your code:
def create
#user = User.new(user_params)
puts '******'
p user_params
puts '******'
...
...
end
Then look in your server window for the output. You'll see something like:
******
{“first_name"=>”Joe”, “last_name”=>”Smith”, “email”=>”joe_smith#yahoo.com”}
*******
params.require has to do with security. The subject is called strong parameters, which you can read about here:
https://www.sitepoint.com/rails-4-quick-look-strong-parameters/
if #user.save #Checks if #user was saved?
Yes:
By default, save always run validations. If any of them fail the
action is cancelled and save returns false.
session[:user_id] = #user.id #Creates a session? What's :user_id and #user_id?
A session is used to make variables persist from one request to another. A session is like a Hash, and :user_id is just a random key that you are creating in the Hash. You can name the key anything you want, but it should describe the data that you are saving.
#user.id is the value you are saving in the session Hash. The id comes from the user you created here:
#user = User.new(user_params)
I'm generally aware that this is for a signup form, but I am having
trouble comprehending the create and user_params function processes.
First, you use a GET request to display the form for creating a new user--you do that by entering localhost:3000/users/new in your browser. That will display the form. The <form> tag has an action attribute, which specifies the url where the form will send a request accompanied by the data.
If you use your browser's developer tools, you can click on something like Page Source to see the raw html of the form, which will look something like this:
<form class="new_user" id="new_user" action="/users" accept-charset="UTF-8" method="post">
...
...
A POST request that is sent to the url /users is routed to the create action in the UsersController. That's because when you declare a route like:
resources :users
or
resources :photos
Rails uses the chart below to route urls to actions (in this case the urls are routed to actions in the PhotosController):
Note that the url /photos is routed to both the index and the create action. Rails checks whether the request is a GET request or a POST request to determine which action to execute.
For additional information, check out the Rails Guide on routing.
There are two thins going on here. The controller is probably mapped to /users/ path. The controller will direct all POST to create. Similarly, it will direct all GET to index. Which you don't have.
user_params is a function that was created probably as part of your scaffolding. Like rails generate ... In older versions of Rails, it wasn't like this. This allows you to say for the user scope, first_name, last_name, etc are allowed to be submitted via POST. Why is this done? Mostly security. It allows you to whitelisted parameters so that for example user.admin cannot be updated. You can read more about it here.
In your web-app you may want to create and update users' information.
For example, in both your views new.html.erb ( which create new user) and edit.html.erb (which update existing user's information), you will probably render a form to let users type their information (with bootstrap).
<div class='row'>
<div class='col-xs-12'>
<%= form_for(#user, :html => {class: "form-horizontal", role:"form"}) do |f| %>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :first_name,"FName:" %>
</div>
<div class="col-sm-8">
<%= f.text_field :last_name, class: "form-control", placeholder: "Enter username", autofocus: true %>
</div>
</div>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :last_name,"LName:" %>
</div>
<div class="col-sm-8">
<%= f.text_field :last_name, class: "form-control", placeholder: "Enter username" %>
</div>
</div>
<br>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :email, "Email:" %>
</div>
<div class="col-sm-8">
<%= f.email_field :email, class: "form-control", placeholder: "Enter your email" %>
</div>
</div>
<br>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :password, "Password:" %>
</div>
<div class="col-sm-8">
<%= f.password_field :password, class: "form-control", placeholder: "Enter your password" %>
</div>
</div>
<br>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<%= f.submit(#user.new_record? ? "Sign up now!" : "Update your account", class:'btn btn-primary btn-lg') %>
</div>
</div>
<% end %>
<div class="col-xs-6 col-xs-offset-3">
[ <%= link_to 'Cancel request and return to home', root_path %> ]
</div>
</div>
Back to your question:
By doing "params.require(:user).permit(:first_name, :last_name, :email, :password)" will allow the user controller to modify you first_name, last_name, email and password parameter with security.
I'm sure there are people out there who have done this. I started down it, but I made my app crash a few times, so I figured I'd just ask out there rather than continue driving my webapp in to oblivion. I'm using devise_invitable gem. It sends a link to invited users, they click the link, and they're directed to this view # app/views/devise/invitation.html.erb:
<h2><%= t 'devise.invitations.edit.header' %></h2>
<%= simple_form_for resource, as: resource_name, url: invitation_path(resource_name), html: { method: :put } do |f| %>
<%= devise_error_messages! %>
<%= f.hidden_field :invitation_token %>
<%= f.input :password %>
<%= f.input :password_confirmation %>
<%= f.button :submit, t("devise.invitations.edit.submit_button") %>
<% end %>
I want to add some fields, for example
<%= f.input :firstname %>
When I do that, though, it does appear in the view, though it's not saving to the User model. So, I figured I needed to modify the controller. That's where I get confused, I think because I'm trying to flop back and forth between the devise and devise_invitable readme's. I'm using devise 3.5.6 and devise_invitable 1.5.5. I tried adding the above input to the form, and changing the applicaiton controller to include
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :firstname
end
but that still doesn't save to my user model. Anyone have any advice?
You are permitting params that pass on to devise RegistrationsController create action. You can see it in definition, below params sanitizer is for :sign_up
devise_parameter_sanitizer.for(:sign_up) << :firstname
In your case it must be :accept_invitation, Since you are using devise_invitable and form submit url is invitation_path which will submit to Devise::InvitationsController#update
devise_parameter_sanitizer.for(:accept_invitation) do |u|
u.permit(:firstname)
end
More details here
Rails only checks for field confirmations if there is a corresponding _confirmation field in the request.
To explain it further, here is a sample Rails view:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, autofocus: true %>
<%= f.label :password %>
<%= f.password_field :password, autocomplete: "off" %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, autocomplete: "off" %>
<%= f.submit "Sign up" %>
<% end %>
This form works as expected.
However, if you remove:
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, autocomplete: "off" %>
Passwords are no longer confirmed.
The problem I am having is all my JSON requests are going through successfully without a corresponding _confirmation field.
This request is successful:
{
"email": "me#example.com",
"password" : "somepassword"
}
This is not:
{
"email": "me#example.com",
"password" : "somepassword",
"password_confirmation": "_somepassword
}
Also, for the second JSON, if password and password_confirmation match then the request is successful(which is understood).
I think the first request should not be successful since there is no password confirmation.
Is this a bug or an expected behaviour since confirmation fields should be tied to ActiveRecord and not HTML from helpers.
How will one replicate field confirmations in Rails based JSON APIs then?
I think that validation of the confirmation is kind of useless in the context of an JSON API. You would basically only validate that the developer read your API description.
Therefore I would argue that it makes sense that it does not validate if the confirmation key is missing. Since a form would always return both fields it would always validate user input.
If you really want to enforce the confirmation (even on the JSON API) just follow the docs:
NOTE: This check is performed only if password_confirmation is not nil. To require confirmation, make sure to add a presence check for the confirmation attribute:
validates_presence_of :password_confirmation, if: :password_changed?
I currently have a working rails contact form (taken from another SO answer), available on /contact, that uses a controller, model and mailer (Mandrill). It responds to new and create routes in the controller, and still uses activemodel features. The form does not use ajax.
I now need to try to make my contact form work on the home page, as opposed to the /contact page via a modal pop-up.
I have read a number of SO queries, about modals, but all seem to be connected with getting a modal to do something specific - and not so much on creating a modal form to mimic a normal form.
To start I added a working modal, to the homepage.
When I then try to add the form into the homepage model, I run into method errors as the form is part of the home_controller. After copying my new and create controller actions into my home controller, I realized, that the form is just hidden, and is still being run when the page loads (by the index action).
Adding #message = Message.new into the index action does not seem to help - and wouldn't I want this on the modal load ?
Here are the main moving parts of my working form from the contact page, and the working modal box from the homepage - how can I merge the two and retain the functionality I have ?
here is the contact controller:
#/app/controllers/contact_controller.rb
class ContactController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
NotificationsMailer.new_message(#message).deliver
redirect_to(root_path, :notice => "Message was successfully sent.")
else
flash.now.alert = "Please fill all fields."
render :new
end
end
end
the message model
#app/models/message.rb
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :subject, :body
validates :name, :email, :subject, :body, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
And the view:
<%= form_for #message, :url => contact_path do |form| %>
<fieldset class="fields">
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :email %>
<%= form.text_field :email %>
</div>
<div class="field">
<%= form.label :subject %>
<%= form.text_field :subject %>
</div>
<div class="field">
<%= form.label :body %>
<%= form.text_area :body %>
</div>
</fieldset>
<fieldset class="actions">
<%= form.submit "Send" %>
</fieldset>
<% end %>
Finally within my home page I have this for the modal (not containing the form)
<div class="modal fade" id="modal_contact_form">
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<h3>Contact Form</h3>
</div>
<div class="modal-body">
<p>Test Content for Modal</p>
</div>
<div class="modal-footer">
</div>
</div>
Which is called by this <li>contact</li>
My attempts have been many and varied to get this working up until this point - the main issue I need to get my head around (I think) is that the form is loaded by the index action on the home_controller - Ideally I guess I still want to use the logic in my contact_controller ?
Any advice and suggestions appreciated - at this point I am wondering if there is an easy way just to move a form into a modal or not!
Cheers,
Mizpah
note: In response to queries I have about ajax, I have only avoided ajax due to perceived difficulty and lack of progress with trying it on rails 3.2 - I would be delighted to use it if it would actually be easier to turn this into an ajaxified form ?
It seems that I was not far away after all!
After a pointer from #rubyonrails channel, the answer was:
1) To copy the form as is, into the modal. (note that different styling is probably needed - but this answer gives the functionality needed).
2) To add #message = Message.new into the index action on my home_controller.rb
I had tried something very similar to this, but must of introduced another error, and thus bypassed the correct solution.