rails - Devise - Creating a Registration Form - ruby-on-rails

I've been banging my head against the wall try to understand how to get Devise to work with customer registration....
So On my landing page I want to show a registration form, so I added this to my view:
<%= render 'devise/registrations/new' %>
In that partial I have in the view a form tag like follows:
<%= form_for(user_registration_path, :url => user_registration_path) do |f| %>
.
.
In my application layout I have:
<% flash.each do |key, value| %>
<div class="flash <%= key %>"><%= value %></div>
<% end %>
The issue I'm having is when I submit a new registration form with invalid params, I don't see the error message?
But if I submit valid info the form does say it worked and that I need to check my email for the confirmation link, which is good.
Can you help me understand how to get this working end-2-end so I can display the errors:
Here's my full controller:
# GET /users/new
# GET /users/new.xml
# GET /users/new.json HTML AND AJAX
#-------------------------------------------------------------------
def new
respond_to do |format|
format.json { render :json => #user }
format.xml { render :xml => #user }
format.html
end
end
# GET /users/1/edit
# GET /users/1/edit.xml
# GET /users/1/edit.json HTML AND AJAX
#-------------------------------------------------------------------
def edit
respond_to do |format|
format.json { render :json => #user }
format.xml { render :xml => #user }
format.html
end
rescue ActiveRecord::RecordNotFound
respond_to_not_found(:json, :xml, :html)
end
# POST /users
# POST /users.xml
# POST /users.json HTML AND AJAX
#-----------------------------------------------------------------
def create
#user = User.new(params[:user])
if #user.save
respond_to do |format|
format.json { render :json => #user.to_json, :status => 200 }
format.xml { head :ok }
format.html { redirect_to :action => :index }
end
else
respond_to do |format|
format.json { render :text => "Could not create user", :status => :unprocessable_entity } # placeholder
format.xml { head :ok }
format.html { render :action => :new, :status => :unprocessable_entity }
end
end
end
The model:
validates :fname, :presence => true, :length => { :minimum => 2 }
validates :lname, :presence => true, :length => { :minimum => 2 }
validates :password, :presence => true, :length => { :minimum => 6 }
validates :email, :presence => true, :length => { :minimum => 6 }

From the github readme:
"Remember that Devise uses flash messages to let users know if sign in was successful or failed. Devise expects your application to call "flash[:notice]" and "flash[:alert]" as appropriate."

Where is your <%= error_messages_for %>? That is how errors will be displayed.
Also, devise needs virtual attributes in the model for additional attributes so don't forget to add :lname, :fname etc... to the devise user model or whatever you are doing.
Update
<%= f.error_messages_for :model %>
Before Rails 3 this is how errors were formatted, but in R3 it's depircated and you need to install a plugin to access errors this way. See comments for the link to the plugin

Just insert this at the beginning of your form, end before the end of it:
<%= devise_error_messages! %>
Devise is kinda tricky, I just finally managed to figure it out myself, and it's awesome. Btw, don't render the view as a partial, use templates, and then set your root to users#registrations_controller, given that you created custom controllers and views.
If you haven't, I wrote a blog post about it here

Related

Belongs_To / Has Many Relationship

I have the following:
Clients have many Reports and Reports belong to a client.
However on the creation of the Report it is not assigning the client_id into the database, but not sure why?
Am i doing something wrong here?
Client Model
class Client < ActiveRecord::Base
has_many :reports, :dependent => :destroy
end
Report Model
class Report < ActiveRecord::Base
has_attached_file :report
belongs_to :client
end
Client Controller (Update)
# PUT /clients/1
# PUT /clients/1.json
def update
#client = Client.find(params[:id])
respond_to do |format|
if #client.update_attributes(params[:client])
format.html { redirect_to [:admin,#client], :notice => 'Client was successfully updated.' }
format.json { head :ok }
else
format.html { render :action => "edit" }
format.json { render :json => #client.errors, :status => :unprocessable_entity }
end
end
end
Report Controller (Create)
# POST /reports
# POST /reports.json
def create
#report = Report.new(params[:report])
#report.client_id = params[:client][:client_id]
respond_to do |format|
if #report.save
format.html { redirect_to '/admin/clients', :notice => 'Report was successfully created.' }
format.json { render :json => #report, :status => :created, :location => #report }
else
format.html { render :action => "new" }
format.json { render :json => #report.errors, :status => :unprocessable_entity }
end
end
end
Client Edit View
<%= form_for([:admin, #client.reports.build]) do |f| %>
<label class="formlabel">Report Upload</label>
<%= f.file_field :report, :class=>"text-input small-input" %>
<div class="actions">
<br />
<%= f.submit 'Upload', :class => 'button' %>
</div>
<% end %>
Assistance would be appreciated!
I'm curious; because you're using .build in the form_for, the client may already be in the url.
What if you remove:
#report.client_id = params[:client][:client_id]
and submit, what happens then? Because this line is looking incorrectly at the params, so I wonder if you are overwriting that you built in the form_for
Either that, or a hidden field like #Adam said would work.
The client_id doesn't have a related input field in the form on your view. You could add something to your form like:
f.hidden_field :client_id
And then in your controller, set it as:
#report.client_id = params[:report][:client_id]
Alternatively, you could include the client_id in the url.
Stupid Mistake it seems needed to up the end function on the form-for for the client to close it off before opening the form-for the reports.
Then add the field for the client_id and now just hide the field as per Adam suggestion.
Thanks Steph for suggestions as this help me solve this mistake.
Thanks Everyone! :-)

Runtime error - called id for nil in Rails 3 project

I'm getting this error when I try to submit my form (/POSTS/SHOW):
RuntimeError in Posts#show
Showing /Users/fkhalid2008/loand/app/views/posts/show.html.erb where line #1 raised:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Extracted source (around line #1):
1: <%= form_remote_tag (:update => 'message', :url => {:controller => 'main', :action => 'send_message', :user_id => #post.user.id}) do %>
2: <br>
3: <br />
4: <br />
How do I fix this?
Relevant code is below:
/VIEWS/POSTS/SHOW
<%= form_remote_tag (:update => 'message', :url => {:controller => 'main', :action => 'send_message', :user_id => #post.user.id}) do %>
<br>
<br />
<br />
<div class="field">
Hello! My name is <%= f.text_field :subject %> and I'm contacting you in response to your ad. I'm interested in learning more so get in touch! Here's my contact details: <%= f.text_field :body %>.
Submit
<% end %>
POST MODEL
class Post < ActiveRecord::Base
belongs_to :user
attr_accessible :title, :job, :location, :salary
validates :title, :job, :location, :salary, :presence => true
validates :salary, :numericality => {:greater_than_or_equal_to => 1}
default_scope :order => 'posts.created_at DESC'
end
USER MODEL
class User < ActiveRecord::Base
has_many :posts
has_one :profile
has_private_messages
attr_accessible :email
validates_presence_of :email
validates_uniqueness_of :email, :message =>"Hmm, that email's already taken"
validates_format_of :email, :with => /^([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})$/i, :message => "Hi! Please use a valid email"
end
POSTS CONTROLLER
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #post }
end
end
def new
#post = Post.new
#post.user = current_user
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #post }
end
end
def edit
#post = Post.find(params[:id])
end
def create
#post = Post.new(params[:post])
#post.user = current_user
respond_to do |format|
if verify_recaptcha && #post.save
format.html { redirect_to :action=> "index"}
format.json { render :json => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.json { render :json => #post.errors, :status => :unprocessable_entity }
end
end
end
def update
#post = Post.find(params[:id])
#post.user = current_user
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, :notice => 'Post was successfully updated.' }
format.json { head :ok }
else
format.html { render :action => "edit" }
format.json { render :json => #post.errors, :status => :unprocessable_entity }
end
end
end
APPLICATION CONTROLLER (this is where I am defining current_user)
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
#_current_user ||= session[:current_user_id] &&
User.find_by_id(session[:current_user_id])
end
end
MAIN CONTROLLER (send_message is defined here)
class MainController < ApplicationController
def send_message
message = Message.new
message.subject = params[:subject]
message.body = params[:message]
message.sender = User.find session[:user]
message.recipient = User.find params[:user_id]
if message.save
ContactMailer.deliver_message_email message.recipient.email, message.id, request.host
return redirect_to "/posts"
else
render :text => "Hmm. Something seems to be wrong...let me look into it"
end
end
You don't have a user assigned to the post record represented by the #post instance variable.
Presumably a user needs to be logged in to make a post?
Also presumably you have a current user defined somewhere?
Your controller actions that use this form need to assign the user to the post record
def new
#post = Post.new
#post.user = current_user # You will need to get the current user from somewhere
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #post }
end
end
UPDATE
To make sure that your current user is assigned you should add a check to ensure the user is logged in in the controller actions. This is normally done by adding a before filter to authorize the current user which will redirect back to the login page if the current use is logged out.
Have a look at this rails cast to explain logging in and out and redirecting on a before filter http://railscasts.com/episodes/250-authentication-from-scratch
There is a revised version of the cast here but you will need a subscription for that
http://railscasts.com/episodes/250-authentication-from-scratch-revised
well worth paying for IMO
End of update
You will need to / should also assign the current user in whatever actions update the post record - i.e. the create and update actions in EXACTLY the same way.
Also, because you have not got a user assigned to a post record then you need to handle this scenario in the form so that you don't get 500 errors
You can use the #post.user.blank? boolean check to help you with this
Something like
<% if #post.user.blank? %>
<h2>There is no user assigned to this post record! This should never happen ad you should never see this message, please contact support if etc... </h2>
<% else %>
<!-- Place all your current form code here -->
<% end %>
You are getting the error because #post.user is nil in :user_id => #post.user.id.
Make sure you define #post in your post controller's show action and that it has a valid user association.

Trying to have admin user create other users, but it keeps taking me to Edit User after submitting

I was following Tony Amoyal's setup for authentication using Devise/cancan and only allowing an admin to create/register new users. I didn't follow him exactly because there is no need for users to have multiple roles in this application, but for the most part I used exactly what he suggested.
It mostly works, but the biggest issue I am having right now is when I try to create a new user and submit it on the register form, it immediately complains, takes me to Devise's edit registration form and complains about the :current_password field not being filled in. If I fill anything in at that point, it will update MY user, not the one I was trying to register.
Any help to get it to actually create the user instead of requesting more changes would be appreciated.
#controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def check_permissions
authorize! :create, resource
end
end
#controllers/users_controller.rb
class UsersController < ApplicationController
load_and_authorize_resource :except =>[:create]
...
def new
respond_to do |format|
format.json { render :json => #user }
format.xml { render :xml => #user }
format.html
end
end
...
def create
#user = User.new(params[:user])
if #user.save
respond_to do |format|
format.json { render :json => #user.to_json, :status => 200 }
format.xml { head :ok }
format.html { redirect_to :action => :index }
end
else
respond_to do |format|
format.json { render :text => "Could not create user", :status => :unprocessable_entity } # placeholder
format.xml { head :ok }
format.html { render :action => :new, :status => :unprocessable_entity }
end
end
end
end
#views/users/new.html.haml
= simple_form_for(#user, :method => :put, :html => { :class=>'form-horizontal' }) do |f|
%fieldset
%legend
= f.input :first_name
= f.input :last_name
= f.input :email
= f.input :password
= f.input :password_confirmation
.form-actions
= f.submit 'Register', :class => 'btn btn-primary'
= link_to 'Back', :back, :class => 'btn'
I was able to get around this problem by giving devise a path_prefix which would ensure this process is using my UsersController.

Limiting Comments on Posts Index in Rails App

I am new to rails and trying to limit the number of comments displayed on my Posts Index Page to 2.
Below is my posts_controller:
class PostsController < ApplicationController
before_filter :authorize, :except => [:index, :show]
# GET /posts
# GET /posts.xml
def index
#posts = Post.all(:include => :comments, :order => "created_at DESC")
#comments = Comment.find(:all, :order => "created_at DESC", :limit => 1)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #posts }
end
end
# GET /posts/1
# GET /posts/1.xml
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #post }
end
end
# GET /posts/new
# GET /posts/new.xml
def new
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #post }
end
end
# GET /posts/1/edit
def edit
#post = Post.find(params[:id])
end
# POST /posts
# POST /posts.xml
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
format.xml { render :xml => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
# PUT /posts/1
# PUT /posts/1.xml
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to(#post, :notice => 'Post was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.xml
def destroy
#post = Post.find(params[:id])
#post.destroy
respond_to do |format|
format.html { redirect_to(posts_url) }
format.xml { head :ok }
end
end
end
Below is my Post model
class Post < ActiveRecord::Base
has_attached_file :photo, :styles
=> { :medium => "600x600>", :thumb => "100x100>" },
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => "/:attachment/:id/:style/:filename"
has_many :comments
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
end
Below is my posts index view
<table>
<tr>
<th>BoxScore</th>
<th>Content</th>
</tr>
</table>
<% #posts.each do |post| %>
<%= image_tag post.photo.url(:medium), :class =>"floatleft" %>
<p>
<%= post.content %>
</p>
<div class="comments center">
<h3>Comments:</h3>
<%= render :partial => post.comments.reverse %>
<div class="links">
<% if admin? %>
<%= link_to "New Post", new_post_path %>
<%= link_to 'Edit', edit_post_path(post) %>
<%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %>
<% end %>
<%= link_to 'Comment on the Game', post %>
</div>
</div>
<% end %>
</div>
Comments Partial Below
<% div_for comment do %>
<p>
<strong>Posted <%= time_ago_in_words(comment.created_at) %> ago
</strong>
by
<br/>
<%= h(comment.commenter) %>
<br/>
<%= h(comment.body) %>
<br/>
<%= link_to 'More Comments', comment.post %>
</p>
<% end %>
I am not getting an error message, I just don't know how to limit the amount of comments we are rendering on Posts index page. Thanks
You can't specify conditions on eager loaded associations, unfortunately. Likewise, you can't limit the number of rows returned based on a condition (to my knowledge, though there are a lot of things I don't know about SQL functions).
So you're stuck with either:
Load all of the comments for the posts you're displaying in a query, and limit the number shown in your application.
Load only 2 comments for each of the posts you're displaying individually.
The optimal solution depends on your use case. If you're expecting to show only 5 posts and have thousands of comments on each, option 1 might not be very performant and option 2 might be a good solution. If you are expecting to show more posts per page and have only a handful of comments on any one (the more likely scenario), the first option is going to be your best bet.
Option 1
# controller
#posts = Post.limit(20).all
#comments = Comment.find(#posts.collect &:id).group_by &:post_id
# view
<% #comments[#post.id].first(2).each do |comment| %>
...
<% end %>
Option 2
# controller
#posts = Post.limit(5).all
# view
<% post.comments.limit(2).each do |comment| %>

Rails 3 and paperclip on update method issue

Paperclip is working well to save a user avatar but I find an issue on update.
In my view, if the user has an image saved in the model it will show an image tag with the current image next to the file upload field so you can see what your current avatar is.
If the image has no changes but the model validation fails (like no first_name), the original display image disappears, meaning that the user either has to correct the error and re-select an image and submit (update) or go back and start over without the error. Any ideas? Thanks in advance.
Heres the code:
Model
class User < ActiveRecord::Base
# Validation
validates :first_name, :presence => true
# Paperclip
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "70x70#" }
end
Controller
...
# GET /users/1/edit
def edit
#user = User.find(params[:id])
end
# POST /users
# POST /users.xml
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
# PUT /users/1
# PUT /users/1.xml
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to(#user, :notice => 'User was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
...
View
<%= form_for #user, :html => {:multipart => true} do |f| %>
<div class="row text">
<%= f.label :first_name %>
<div class="field">
<%= f.text_field :first_name %>
</div>
</div>
<div class="row">
<%= f.label :avatar %>
<div class="field">
<%= image_tag #user.avatar.url(:thumb) %>
</div>
<div class="field" id="avatar_upload">
<%= f.file_field :avatar %>
</div>
</div>
<div class="row actions"><%= f.submit %> or <%= link_to 'cancel', users_path %>.</div>
<% end %>
This is being caused by your form sending 'nil' to the server (due to the field being blank since a new avatar is not being uploaded) when your page sends a POST request to your server. Short of mucking things up with hidden temporary fields, there are a few ways around this:
Client-side validation. Prevent your page from submitting if anything is wrong and you won't end up reaching the server-side validation failure that results in the missing image.
Move the avatar field to a separate FORM object on the same page that only handles the avatar. That will ensure that the page only ever sends avatar information in a POST message if it is related to an actual avatar edit.
Move the avatar upload/update functionality to a completely separate page that only handles the avatar. This works for the same reason the reason above this one works.

Resources