I have spent a while trying and searching for answers to debug this.
I am following Railscast 250 (Authentication from scratch) which is intended for Rails 3 on Rails 4.
Obviously there is a problem of strong parameters which I think I have solved using the usual method.
I am currently getting this error:
undefined method `password' for #User:0xb640d880
Extracted source (around line #32):
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
I know the controller can access the password attribute, but for some reason the model can't even though I am validating the presence of :password in the model.
user.rb
class User < ActiveRecord::Base
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
logger.warn user_params[:password]
#user = User.new(email: user_params[:email], password_hash: user_params[:password_hash], password_salt: user_params[:password_salt])
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, 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
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:email, :password_hash, :password_salt, :password)
end
end
_form.html.erb (view)
<%= form_for(#user) 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 %>
<div class="field">
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br>
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password %><br>
<%= f.password_field :password %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Thanks for your help!
I have created an sample project of user authentication so please check it.
Users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
#raise params.inspect
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
private
def user_params
params.require(:user).permit(:email, :password_hash, :password_salt, :password)
end
end
new.html.erb
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
</p>
<p>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</p>
<p class="button"><%= f.submit %></p>
<% end %>
user.rb
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
#validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
def encrypt_password
#raise password.inspect
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
Above code is working properly.
Your model is missing an attr_accessor for password. As you don't have a password column in your table but still need to receive the password attribute (so you can process it with a salt and then turn it into a hash), you need to have this accessor.
Add this in your user model:
attr_accessor :password
EDIT: Just like Amit Sharma pointed out in his demo.
Related
I am new to Rails and was creating a demo web shop app for study.
I could create the products smoothly, both via rails console and by the url localhost:300/products/new.
My problem is when I want to update them.
I have _form.html.erb partial getting rendered both in new.html.erb and edit.html.erb
In /products/id/edit though the button "Update Product" is actually destroying the product instead of updating it
This is the _form.htlm.erb:
<%= form_for(#product) do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="row">
<div class="col-sm-4 col-xs-12">
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name, :class => "input-group input-group-sm" %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :image_url %><br>
<%= f.text_field :image_url %>
</div>
<div class="field">
<%= f.label :color %><br>
<%= f.text_field :color %>
</div>
<div class="actions">
<%= f.submit %>
</div>
</div>
</div>
Please tell me if you need more data
Thanks,
Anna
Updat: here below my routes.rb
Rails.application.routes.draw do
resources :products
get 'news/index' => 'news#index', as: :news
get 'store' => 'store#index', as: :store
get 'contact' => 'contact#index', as: :contact
get 'products/edit' => 'products#edit'
get 'products/destroy' => 'products#destroy'
get 'about' => 'about#index', as: :about
get 'landing_page' => 'static_pages#landing_page', as: :landing_page
get 'home/index'
root 'static_pages#landing_page'
resources :orders, only: [:index, :show, :create, :destroy]
I have pryed in the products_controller and found this:
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
The Edit.html.erb had also three options for: Show|Delete|Back The Delete option was coded with : <%= link_to 'Delete', #product.destroy %> I deleted this line and the product got edited instead of being cancelled.
I am trying to set up in rails 3.2.0 with ruby 1.9.3 a
has_and_belongs_to_many relationship between two tables I have the proper join table migration set up with both foreign keys and my models are as such
student.rb
class Student < ActiveRecord::Base
attr_accessible :birthday, :id, :name, :year, :course_ids
has_and_belongs_to_many :courses
accepts_nested_attributes_for :courses
end
course.rb
class Course < ActiveRecord::Base
attr_accessible :id, :coursePrefix, :roomNumber, :name, :student_ids
has_and_belongs_to_many :students
end
_form.html.erb
<%= form_for(#student) do |f| %>
<% if #student.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#student.errors.count, "error") %> prohibited this student from being saved:</h2>
<ul>
<% #student.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :id %><br />
<%= f.text_field :id %>
</div>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :birthday %><br />
<%= f.date_select :birthday, :start_year => 1930 %>
</div>
<div class="field">
<%= f.label :year %><br />
<%= f.text_field :year %>
</div>
<div>
<%= f.collection_select(:courses, #courses, :id, :name)%>
</div>
<br />
<div class="actions">
<%= f.submit %>
</div>
<% end %>
and student_controller.rb
# GET /students/new
# GET /students/new.json
def new
#student = Student.new
#courses = Course.all
respond_to do |format|
format.html # new.html.erb
format.json { render json: #student }
end
end
# GET /students/1/edit
def edit
#student = Student.find(params[:id])
#courses = Course.all
end
# POST /students
# POST /students.json
def create
#student = Student.new(params[:student])
respond_to do |format|
if #student.save
format.html { redirect_to #student, notice: 'Student was successfully created.' }
format.json { render json: #student, status: :created, location: #student }
else
format.html { render action: "new" }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
# PUT /students/1
# PUT /students/1.json
def update
#student = Student.find(params[:id])
#courses = #student.courses.find(:all)
respond_to do |format|
if #student.update_attributes(params[:student])
format.html { redirect_to #student, notice: 'Student was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
Again I am trying to get the form element to allow a student to select a course and be saved in that table under the JOIN method, however everytime I try to look for help I get different errors and cannot find a solution
Thank you for your help
You just have to give access to the :courses attribute of the Student model:
class Student < ActiveRecord::Base
attr_accessible :birthday, :id, :name, :year, :course_ids, :courses
# ^ ^ ^ ^
Why? Because your controller receive params formatted like this:
params = {
student: {
id: 12,
name: "Bob the Sponge",
courses: [5, 7], # Course ids
etc: ...
}
}
And the controller is trying to create a Student object directly with the params, so when he is trying to update the courses attribute of the Student, it can't because you didn't define it as accessible.
ActiveRecord::RecordNotFound in UsersController#index
Couldn't find UserSession without an ID
Rails.root: C:/Sites/login_app
Application Trace | Framework Trace | Full Trace
app/controllers/application_controller.rb:9:in current_user_session'
app/controllers/application_controller.rb:14:incurrent_user'
app/views/layouts/application.html.erb:12:in _app_views_layouts_application_html_erb__233847886_33215772'
app/controllers/users_controller.rb:7:inindex'
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user private
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
end
class HomeController < ApplicationController
def index
end
end
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #user_session }
end
end
def create
#user_session = UserSession.new(params[:user_session])
respond_to do |format|
if #user_session.save
format.html { redirect_to(:users, :notice => 'Login Successful') }
format.xml { render :xml => #user_session, :status => :created, :location => #user_session }
else
format.html { render :action => "new" }
format.xml { render :xml => #user_session.errors, :status => :unprocessable_entity }
end
end
end
def destroy
#user_session = UserSession.find
#user_session.destroy
respond_to do |format|
format.html { redirect_to(:users, :notice => 'Goodbye!') }
format.xml { head :ok }
end
end
end
class UsersController < ApplicationController
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
def new
#user = User.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(:users, :notice => 'Registration successfull.') }
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
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.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_url }
format.json { head :no_content }
end
end
end
layout/apllication.html
LoginApp
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
<% if current_user %>
<%= link_to "Edit Profile", edit_user_path(current_user.id)%>
<%= link_to "Logout", :logout%>
<% else %>
<%= link_to "Register", new_user_path%>
<%= link_to "Login", :login %>
<% end %>
<%= yield %>
user_seesion/_form.html
<%= form_for(#user_session) do |f| %>
<% if #user_session.errors.any? %>
<%= pluralize(#user_session.errors.count, "error") %> prohibited this user_session from being saved:
<% #user_session.errors.full_messages.each do |msg| %>
<%= msg %>
<% end %>
<% end %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :password %>
<%= f.password_fields :password %>
<%= f.submit %>
<% end %>
user_sessions/edit.html
Editing user_session
<%= render 'form' %>
<%= link_to 'Show', #user_session %> |
<%= link_to 'Back', user_sessions_path %>
user_sessions/index.html :
Listing user_sessions
Username
Password
<% #user_sessions.each do |user_session| %>
<%= user_session.username %>
<%= user_session.password %>
<%= link_to 'Show', user_session %>
<%= link_to 'Edit', edit_user_session_path(user_session) %>
<%= link_to 'Destroy', user_session, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
<%= link_to 'New User session', new_user_session_path %>
user_sessions/new.html
Login
<%= render 'form' %>
<%= link_to 'Back', user_sessions_path %>
user_sessions/show.html
<%= notice %>
Username:
<%= #user_session.username %>
Password:
<%= #user_session.password %>
<%= link_to 'Edit', edit_user_session_path(#user_session) %> |
<%= link_to 'Back', user_sessions_path %>
-users/_form.html
<%= form_for(#user) do |f| %>
<% if #user.errors.any? %>
<%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:
<ul><br/>
<% #user.errors.full_messages.each do |msg| %><br/>
<li><%= msg %></li><br/>
<% end %><br/>
</ul><br/>
</div><br/>
<% end %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<%= f.submit 'create user'%>
<% end %>
users/edit.html
Editing user
<%= render 'form' %>
<%= link_to 'Show', #user %> |
<%= link_to 'Back', users_path %>
users/index.html :
Listing users
<%= notice %>
Username
Email
<% #users.each do |user| %>
<%= user.username %>
<%= user.email %>
<%= link_to 'Show', user %>
<%= link_to 'Edit', edit_user_path(user) %>
<%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %>
<% end %>
<%= link_to 'New User', new_user_path %>
users/new.html :
New user
<%= render 'form' %>
<%= link_to 'Back', users_path %>
users/show.html :
<%= notice %>
Username:
<%= #user.username %>
Email:
<%= #user.email %>
Crypted password:
<%= #user.crypted_password %>
Password salt:
<%= #user.password_salt %>
Persistence token:
<%= #user.persistence_token %>
<%= link_to 'Edit', edit_user_path(#user) %> |
<%= link_to 'Back', users_path %>
In user_session.rb, you need to change the inherited class to Authlogic::Session::Base, like so:
class UserSession < Authlogic::Session::Base
...
end
Also, next time use a gist or something similar to paste large chunks of code, or format it at least. Hope that helps!
I give up. I'm trying to build a simple nested form with 2 models following Railscasts #196 episode and doesn't work. Can someone send a working example please so I can test on my environment. I'm using 3.1.0
For example when I try to build 3 questions on the form only 1 question field appears, then survey_id is never passed across.
I would appreciate your help after 2 days and nights on it. I got missing something really big. Thanks
Model
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions
attr_accessible :name, :questions_attributes
end
class Question < ActiveRecord::Base
belongs_to :survey
attr_accessible :survey_id, :name
end
Controller
def new
#survey = Survey.new
4.times { #survey.questions.build }
respond_to do |format|
format.html # new.html.erb
format.json { render json: #survey }
end
end
def create
#survey = Survey.new(params[:survey])
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render json: #survey, status: :created, location: #survey }
else
format.html { render action: "new" }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
View
<%= form_for(#survey) do |f| %>
<% if #survey.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#survey.errors.count, "error") %> prohibited this survey from being saved:</h2>
<ul>
<% #survey.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<%= fields_for :questions do |builder| %>
<%= builder.label :name, "Question" %>
<%= builder.text_field :name %>
<% end %>
<br /><br />
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Sadly loses my form after unsuccessful validation the data which have the user typed in. How can I change this, so that the form doesn't losing the data?
View _form.html.erb:
<%= form_for(#user, :html => {:name => "newUser""}) do |f| %>
<div class="field_left">
<%= f.label :last_name, "Last name<span>*</span>".html_safe %><br />
<%= f.text_field :last_name %>
</div>
<% end %>
<div class="field">
<%= f.fields_for :table1 do |ff| %>
<%= ff.text_field :name %>
<% end %>
</div>
Controller users_controller:
def new
#user = User.new
1.times {#user.table1.build}
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #user }
end
end
def create
#user = User.new(params[:user])
#table1= params[:user][:table1][:name]
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
Thanks for your help!
The solution is to set an instance variable as value like the following code.
<%= f.fields_for :table1 do |ff| %>
<%= ff.hidden_field :name, :value => #value %>
<% end %>
Use: <%= f.fields_for #table1 do |ff| %>
Make sure that #table1 is set in the controller with the data that is received from the form submission.
Is better to use
<%= f.fields_for :table1, f.object.table1 do |ff| %>
<% ff.text_field :name %>
<% end %>
You won't have to set values, i.e. <% ff.text_field :name, value: 'something' %>.