[Update] I still dont know what to put in my else of my create conroller to put and error message.
I am trying to validate the format of users email.
This is the html the error is coming from
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this article
from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
I experimented with this from an old ruby video but it didn't seen to pan out.
This is my user.rb model
class User < ApplicationRecord
has_secure_password
validates :email, presence: true, uniqueness: true
validates :password, length: { minimum: 4 }
validates :email, format: { with: /(\A([a-z]*\s*)*\<*([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\>*\Z)/i }
end
I get these errors currently
No route matches {:action=>"show", :controller=>"users", :id=>nil},
missing required keys: [:id]
and
undefined method `errors' for nil:NilClass
whenever the password or email makes an error from the requirements.
my create action in my sessions controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.create(password: params[:password], email: params[:email], firstname: params[:firstname], lastname: params[:lastname])
if #user.save
redirect_to user_path(#user)
else
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
def show
#user = User.find(params[:id])
end
end
When there is no user with the requested email address, your #user variable will be nil in the controller (since this is the returned value of the find_by_email method if nothing is found)
In that case, you are explicitly rendering your new view which then attempts to render errors on the #user object. However, since the variable is nil instead of the User object you are apparently expecting there, nil.errors fails with the error you are seeing there.
To solve this, you should either check in your view if you have an actual #user object, or render a more suitable view from your controller.
3 issues here
def create
#user = User.create(password: params[:password], email: params[:email], firstname: params[:firstname], lastname: params[:lastname])
redirect_to user_path(#user)
end
That function tries to create a user and regardless of success it then goes on to redirect to the show action
1) You need to check if the User.create actually succeeds then take appropriate action
2) You have no route for the show action so you need to fix that but
3) None of your actions are specifying the format. What happens if someone is using an XML feed Are you sure you want to show the user, Just show a list of users, there won't be anything much to see
Several people including asked for the full stack trace which you seem to have ignored. This would be so much easier to track down the origination of your error had you done so, including the params sent to the create action
def create
if #user = User.create(password: params[:password], email: params[:email], firstname: params[:firstname], lastname: params[:lastname])
redirect_to user_path(#user)
else
format.html { render :action => "new" }
end
end
Now you have to track down why the create failed
You should also show the errors to the user by using a flash message
Related
I have a text box in a rails application that I want to validate the pretense of an input before saving to the db. So, I tried placing a validates_precense_of callback in the correct model (as shown below).
class Suggestion < ActiveRecord::Base
attr_accessible :details, :metadata, :suggestible, :user
belongs_to :suggestable, polymorphic: true
belongs_to :user
serialize :metadata, JSON
validates_presence_of :details
end
Instead of the request failing and flashing an error message, the request is successful, and no suggestion record is ever saved in the suggestions table.
I've debugged and confirmed (see controller below) #suggestion.details.blank? and .empty return true is the text box is empty.
Here's the controller action:
def select_patients
#rx_search_request = RxSearchRequest.find(params[:id])
#groups = []
params['selected_patient_group'].each do |id, selected|
#groups << id if selected == "true"
end
unless #groups.blank?
#rx_search_request.select_multiple_patient_group_ids(#groups)
unless #rx_search_request.approval_status_reason_patients_and_approval? ||
#rx_search_request.approval_status_reason_requires_approval?
#rx_search_request.needs_consolidation!
# #rx_search_request.approve! approved_by: current_user
#redirect_url = rx_search_request_path(#rx_search_request)
# #message = "Request has been created."
else
#message = "Request has been forwarded to your admin for approval."
end
end
if params.keys.include? "suggestion"
#we are submitting a suggestion
group_ids = params[:selected_patient_group].collect{|k,v| k if v == 'true'}.compact
metadata = {
group_ids:group_ids,
patient_ids:ManualConsolidationPatientGroup.find(group_ids).collect{|g| g.manual_consolidation_patients}.flatten.collect{|_p| _p.id}
}
#suggestion = #rx_search_request.suggestions.new({
details:params[:suggestion_box],
metadata: metadata
})
#suggestion.user = current_user
#suggestion.save
# #message = "Your suggestion has been submitted."
# debugger
# flash[:alert] = 'Your suggestion cannot be blank' if #suggestion.details.empty?
flash[:alert] = 'Your suggestion has been submitted.'
end
respond_to do |format|
format.js
end
end
Changed the controller to this
unless #suggestion.details.blank?
flash[:alert] = 'Your suggestion has been submitted.'
else
flash[:alert] = 'Your suggestion cannot be blank'
end
#debugger
#suggestion.save!
also tried this
if #suggestion.save!
flash[:alert] = 'Your suggestion has been submitted.'
else
flash[:alert] = 'Your suggestion cannot be blank'
end
SOLUTION
Added bang operator ! to make correct save
You need either to check for the result of save call, or to make it save! instead for the request to fail:
if #suggestion.save
flash[:alert] = 'Your suggestion has been submitted.'
else
flash[:alert] = 'Your suggestion cannot be blank'
end
If you want the validation error to be shown after AJAX request, you need to add something like this to your <controller_name>.js.erb file:
<% flash.each do |type, message| %>
$("#your_element_id").html("<%= type.to_s.humanize %>: <%= message.html_safe %>")
<% end %>
...where you should change #your_element_id to actual HTML ID of element on your page in which you render flash messages.
I have two partial views for two different sign up forms. On my home page , based on the link one clicks on, I'm rendering respective form.(views/application/index)
= link_to 'Mentor', new_user_path(user_role: true), :class =>'btn'
= link_to 'Mentee', new_user_path, :class =>'btn'
In views/users/new.html.haml , I'm checking the user role and redirecting to the respective form.
- if params[:user_role]
= render 'mentor'
- else
= render 'mentee'
In the user model I've added validation like this.
class User < ActiveRecord::Base
email_regex = /\A[\w+\-.]+#cisco.com/i
validates :cisco_email, :presence => true,
:format => { :with => email_regex,}
validates :work_city, :presence => true
end
So, when there is any invalid field I want to direct to the same form with a flash message. My controller looks like this.
class UsersController < ApplicationController
def index
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user]) # Not the final implementation!
if #user.save
flash[:success] = "Welcome to the CSG Mentoring Tool!"
redirect_to #user
else
flash[:notice] = "Error regsitering."
if params[:user][:user_role]
render :partial => 'users/mentor'
else
render :partial => 'users/mentee'
end
end
end
end
When an invalid field entry is there, it is redirecting to 'mentee' page no matter on which page the error is made. Also the entire css styling gets changed and flash is also not displayed
Why this is not working?
if params[:user][:user_role]
render :partial => 'users/mentor'
else
render :partial => 'users/mentee'
end
params[:user][:user_role] is nil.
You can check it using lots of way:
Above your if condition raise params[:user].inspect
Why its nil?
Reason of this is You are passing new_user_path(user_role: true) user_role true, but user_role is not true in mentor form.
params[:user_role] will not set user_role = true field in mentor form.
Set user_role
<%=f.hidden_field :user_role, value: params[:user_role] %>
If its supposed to be true for mentor always
<%=f.hidden_field :user_role, value: true %>
By default flash will make them available to the next request, but sometimes you may want to access those values in the same request.
Reference
This works with redirection
flash[:success] = "Welcome to the CSG Mentoring Tool!"
This will work with render
flash.now[:success] = "Welcome to the CSG Mentoring Tool!"
There's a few of these questions in SO but none address my specific issue so I'm posting it.
I'm trying to display flash notices for success and errors in my rails project but they aren't displaying and I don't know why.
class CheckinsController < ApplicationController
def index
#checkins = Checkin.all
#checkin = Checkin.new
end
def create
#checkin = Checkin.create(params[:checkin])
if #checkin.errors.empty?
render json: #checkin, status: 201,
:notice => 'Thanks for posting your comments.'
else
flash[:notice] = "Please enter a name."
redirect_to checkins_path
end
end
end
Note that yes, there's a discontinuity of :notice and flash[:notice] but it's the only way the controller doesn't break it.
My model looks like this and is doing what it should, that is, prevents a save if the name field in the form is vacant:
class Location < ActiveRecord::Base
attr_accessible :name, :description
has_many :checkins
validates :name, presence: true
end
And I have this in my application.html.erb file:
<div id="notice"><%= flash[:notice] %></div>
Technically this should work, it should display the notice when the error is found. Is there something I'm missing?
I've also tried using...
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
per RoR docs to no avail.
One thing to note is that it's also not rendering its CSS either. Seems related but I can't figure out how.
This has been answered here: Rails Flash.now not working
Basically you need to call the method in a different manner in the view.
flash.now[:notice]
Would be the correct invocation.
I am very very new to Rails and am struggling a little.
I have users that follow a basic sign up, edit process.
I have a situation where a user can be 'part registered', i.e a users email address in the db with a random password as they where put in there via an invite process by an existing user.
When an invited user who already technically exists in the db tries to register I get 'email already exists' since I do have uniqueness checks applied to the user object and a unique index on the table.
I did turn this off and using if statements within the create method to see if the user exists and checked other values did an update or a new and save. All of the if conditions were ignored and a new user was created.
What I want to do is use the same registration form that 'creates' a user to update an existing 'part registered' user with their entered password and name if they exist (email address check) and other db flags are set to true. if the user does exist but the conditions not met load a 'forgot password' page or if user is a new user create and save it.
Any assistance or advice on how to achieve the above would be brill.
Thanks
Vicky
Here is my set up:
User class
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :email, :name, :password, :password_confirmation, :tandc
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => {:with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
validates :tandc, :presence => true
before_save :encrypt_password
.
.
.
end
I have a UsersController with the following
def new
#user = User.new
#title = "Sign Up"
end
def create
if #user.save
sign_in #user
redirect_to #user
else
#title = "Sign up"
render 'new'
end
end
def update
if #user.update_attributes(params[:user])
redirect_to #user
else
#title = "Edit"
render 'edit'
end
end
.
.
.
I have views:
user > new.html.erb
<%= form_for(#user, :html => { :class => "validateUser", :name =>"edit_user"}) do |f| %>
.
.
.
form stuff here
.
.
.
<% end %>
and user > edit.html.erb
<%= form_for(#user, :html => { :class => "validateUser", :name =>"edit_user"}) do |f| %>
.
.
.
form stuff here
.
.
.
<% end %>
my routes for users are as follows
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
A quick, but not elegant solution could be:
def create
if user = User.find_by_email(params[:user][:email])
if user.part_registered?
user.update_attributes(params[:user])
user.save
sign_in user
redirect_to user
else
#user = User.new(params[:user])
if #user.save
# just to trigger the error messages ...
else
#title = "Sign up"
render 'new'
end
end
else
#user = User.new(params[:user])
if #user.save
sign_in #user
redirect_to #user
else
#title = "Sign up"
render 'new'
end
end
end
Sorry, i don't have time right now to optimize these conditions
First off, don't remove the unique index on the user's email. You need that to be unique if you are using it as the natural key of the model, aka signing in with it.
Ask yourself, what is the point of an invitation? I have a sneaking suspicion creating a new user is not really what you want the application to do... what if the user never takes up the inviter on the invitation?
Instead you could make an Invitation object with the user's email and the inviter (if that is the point of an invitation.) Then you could give a link that passes in the invitation as a parameter: <%= link_to new_user_url, email: invitation_email_here %> and put that link in, say, an email to the invited user.
In users/new.html.erb you could pass in the value to pre-fill the form with the email:
<%= f.email_field :email, value: params[:email] %>
Also, I would put your form in a partial to not repeat yourself.
The way form_for works, if #user is new, it will issue a POST request with the filled in values to the create action of the UsersController. If #user is an existing model, it will fill the form with the existing values, issue a PUT request to the update action of the UsersController.
Long story short the edit and new views can have the identical form. To simply display the same form, make a partial in the users/ veiws directory.
<!-- app/views/users/_form.html.erb -->
<%= form_for(#user, html: { class: "validateUser" }) do |f| %>
...
<% end %>
And then in the views where you would put the form, put <%= render 'users/form' %>
You can use something along the lines of User.find_or_initialize_by_email(:email => params[:user_email]) to get the User object.
Basically, find_or_initialize will attempt to get the User from the database, and if it fails to get anything it will create a new one that you can later save.
After you get your User object, you can perform whatever you want to its fields, and then save the result to the database (either updating the old entry or creating a new one).
(Note: if you want to check whether the User previously existed, use .persisted? on the object returned by find_or_initialize, this will return a boolean value telling you if it was already in the database.)
When someone registers with the site, you could send an account verification email before logging them in. The email includes a link with a system-generated token (e.g. 'User.token'). When they click the link, your verification_controller retrieves the user by their token and lets them set their password.
# In class User < ActiveRecord::Base
# Give the user a unique, random token
user.token = SecureRandom.urlsafe_base64(20)
# In class VerificationsController < ApplicationController
# Retrieve the user with their token
#user = User.find_by_token(params[:token])
Now let's consider your case. When an invited user tries to register before accepting an invite, you can still send an account verification email. Now they have two emails: 1) from the invite and 2) from their registration. It doesn't matter...they just have to click the link and set their password.
You will probably want to expire the token since it gives a way to access the account. A popular authentication framework, AuthLogic, has a password resets tutorial that is helpful. It shows a similar technique and might give you some ideas for your hand-built solution.
You could try in users_controller.rb
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #collection, notice: 'User was successfully updated.'}
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
the add link to your view
<%= link_to content_tag(:span, "Edit"), edit_user_path(#user), :class => 'edit' %>
in new.html.erb remove the form and add
<%= render 'form' %>
(this will render the partial that you will add form code to)
create _form.html.erb and add the form code to this
then create your edit.html.erb view and add code
<%= render 'form' %>
I'm trying to implement the same concept Ryan Bates discussed in his Railscast here: http://railscasts.com/episodes/217-multistep-forms into my user sign up form.
Basically the 3 steps are as follows:
Step 1. Fill out user details
Step 2: Go to an external URL to complete billing details, which will redirect them back to this user sign up form when they're done.
Step 3: Confirm and submit data
Currently my workaround is to create the user first, then redirect to the external URL to complete payment after #user.save, but I would like to use this multistep form to prevent the user from getting saved if they don't complete payment first.
Would appreciate it if you could point me towards a direction.. thanks.
UPDATE:
My user controller:
def new
session[:user_params] ||= {}
if params[:plan_id].present?
#user = User.new(session[:user_params] && :plan_id => params[:plan_id])
else
#user = User.new(session[:user_params])
end
#user.current_step = session[:user_step]
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #user }
end
end
def create
session[:user_params].deep_merge!(params[:user]) if params[:user]
#user = User.new(session[:user_params])
#user.current_step = session[:user_step]
if #user.valid?
if params[:back_button]
#user.previous_step
elsif #user.last_step?
#user.save
elsif #user.billing_step?
#user.next_step
redirect_to("external.com") and return
else
#user.next_step
end
session[:user_step] = #user.current_step
end
if #user.new_record?
render "new"
else
session[:user_step] = session[:user_params] = nil
flash[:success] = "User saved"
redirect_to dashboard_url
end
end
My user model:
validates_presence_of :username, :first_name, :last_name, :if => lambda { |u| u.current_step == "initial" }
attr_writer :current_step
def current_step
#current_step || steps.first
end
def steps
%w[initial billing confirmation]
end
def next_step
self.current_step = steps[steps.index(current_step)+1]
end
def previous_step
self.current_step = steps[steps.index(current_step)-1]
end
def first_step?
current_step == steps.first
end
def last_step?
current_step == steps.last
end
def billing_step?
current_step == steps.second
end
My new user view:
<%= form_for #user, :url => {:action => :create, :plan_id => params[:plan_id] } do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= render "#{#user.current_step}_step", :f => f %>
<% end %>
The problem is in the user controller with the #user.billing_step?
what I need to happen after hitting the submit button is: 1.) add a step (using #user.next_step) AND 2.) redirect to an external URL.
That way, when users go back to "users/new" they're already at the final step of confirmation.
If I don't add the "and return" command at the end of the redirect, I get the "Render and/or redirect were called multiple times in this action" error. If I do, Rails doesn't add a new step and takes me back to the billing step (step 2) of the whole thing.
You could store your user's currently input form data into a session for while they are visiting the third party service to complete their payment.
If the payment service is good, they will provide you with a hash of information (or something of the sort) with details on the user's actions (whether they paid or not) and then you can use the saved session information to complete the registration if everything is ok.
Let user fill out mandatory fields
Have a button titled "Proceed to Payment"
Have the button save the user information (via ajax or submit) into the session, and redirect to the payment gateway if all required fields are so far ok
Redirect back to the order form and and check for the session values and then the payment details that the service returned (which they usually do in the return callback url)
Create user if everything is OK
Update:
I would definitely do something like this with your code:
elsif #user.billing_step?
#user.next_step
session[:user_step] = #user.current_step
return redirect_to("external.com")
else
This sets the session to the correct step and redirects, and when the user comes back they will be taken to another conditional (thus it should work?), and keeps updating the step value on the other conditions as well, since the lower session variable isn't removed.
Does this help at all? This is probably how I would do it.