private method `update_without_password' called for nil:NilClass - ruby-on-rails

I am following this tutorial on cancancan authentication, and specifically wanting admins to update users without needing a password. I've also seen this suggestion, but am unsure how to adapt it into my own code. When I click update, it says 'NoMethodError in UsersController#update' private method `update_without_password' called for nil:NilClass
My app returns all the user's params, so I don't understand what's happening here. Still very new to rails - let me know if you need any further information. Thank you in advance!
USERS CONTROLLER
class UsersController < ApplicationController
before_filter :authenticate_user!
def update
if user_params[:password].blank?
user_params.delete(:password)
user_params.delete(:password_confirmation)
end
successfully_updated = if needs_password?(#user, user_params)
#user.update(user_params)
else
#user.update_without_password(user_params)
end
respond_to do |format|
if successfully_updated
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
rescue ActiveRecord::RecordNotFound
respond_to_not_found(:js, :xml, :html)
end
private
def needs_password?(user, params)
params[:password].present?
end
def update_without_password(user_params)
user.update_without_password(user_params)
end
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :name, :role_id, :accepted)
end
EDIT FORM
<h3><%= #user == #current_user ? "Your Account Settings" : "Edit User" %></h3>
<%= form_for(#user, :html => { :method => :put }) do |f| %>
<p><%= f.label :first_name %></p>
<p><%= f.text_field :first_name %></p>
<p><%= f.label :last_name %></p>
<p><%= f.text_field :last_name %></p>
<p><%= f.label :email %></p>
<p><%= f.text_field :email %></p>
<% if can? :read, Role %>
<%= collection_select(:user, :role_id, Role.all, :id, :name, {prompt: true}) %>
<% end %>
<p><%= f.submit "Update" %></p>
<% end %>
What am I missing here? Please let me know if should provide anything else. Thanks!!

It looks to me like you're seeing that error because #user is not being instantiated in the update method. You need to set #user to an instance of your User model. Without seeing your full app, I can't know for sure, but you should have access to :id in the params hash, allowing you to set up #user doing something like this:
#user = User.find(params[:id])
Since you're digging into CanCan, I'd highly encourage you to read through their documentation for the load_and_authorize_resource method. You use it in your controllers to D.R.Y. up repetitive code that loads the object or collection you want to work with. It uses the name of the controller you're in and the :id value from params to automatically instantiate your resource. So, in your show, edit, and update methods, it would instantiate the singular #user object in your UsersController, and in your index method, it would instantiate the collection #users.
FWIW, you might wonder why you aren't seeing the NoMethodError crop up on the if needs_password?(#user, user_params) line. Even though #user is used there, if you look at the definition of your needs_password? method, it takes a user argument, but doesn't actually use it, so it won't complain about the fact that #user is nil when it's called. However, in this line #user.update_without_password(user_params), you're directly sending the update_without_password method/message to the #user variable, which is nil.
I hope that helps!

Related

rails validations error messages not working

I am trying to make a login in and sign up page but when i try to validate the email and password no error messages pop up I though they are supposed to pop up the the condition isn't met.
i have tried error messages through layouts view and helper but none work or are too confusing for me to understand.
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
redirect_to root_path
end
end
def show
#user = User.find(params[:id])
end
end
This is my users controller
This is my user model
class User < ApplicationRecord
has_secure_password
validates :email, presence: true, uniqueness: true
validates :password, length: { minimum: 4 }
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+
[a-z]{2,})\Z/i, :message => "hollksd"(just for testing)
end
This is my new user view
<%= form_for #user do |form| %>
<p> First name:<%= form.text_field :firstname %></p>
<p>Last name:<%= form.text_field :lastname %></p>
<p>Email:<%= form.email_field :email %></p>
<p>Create password:<%= form.password_field :password %></p>
<%= form.submit %>
<%end%>
if anyone could help it would be appreciated!
It seems like your html elements are not associated to User model please try to use form_for tag instead somewhat like below
<%= form_for #user do |form| %>
<%= form.text_field :first_name %>
<%= form.text_field :last_name %>
<%= form.submit %>
<%end%>
Also please use render :new instead of redirect_to root_path
You you need to update your create action to something like this:
def create
#user = User.new(password: params[:password], email: params[:email], firstname: params[:firstname], lastname: params[:lastname])
if #user.save
redirect_to user_path(#user)
else
render :new
end
end
Why render new when it fail to save? Because we need to let know know that this form is not valid, and by rendering new with the #user object, you get access to #user.errors where you can do whatever you like (formally form will render a red border and error message next to input).

Rails 5.0 Error: ActiveModel::ForbiddenAttributesError [duplicate]

This question already has answers here:
ActiveModel::ForbiddenAttributesError when creating new user
(8 answers)
Closed 6 years ago.
I'm attempting to make a blog post on my web-app updatable from the browser, but when I click update in the edit for, I get this error:
ActiveModel::ForbiddenAttributesError
error in pots_controller line 33:
if #post.update_attributes(params[:post])
this is my edit.html.erb code:
<h1>Edit Post</h1>
<%= form_for #post do |f| %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %><br />
</p>
<p>
<%= f.label :body %><br />
<%= f.text_field :body %><br />
</p>
<p>
<%= f.select :category_id, Category.all.collect {|x| [x.name, x.id]}, {:include_blank => "Select One"} %><br />
</p>
<p>
<%= f.submit "Update post" %>
</p>
<% end %>
<%= link_to "Go Back", post_path %>
this is my posts_controller.rb code:
class PostsController < ApplicationController
def index
#posts = Post.find(4,5)
end
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
#category = Category.all
end
def create
#post = Post.new(params[:post])
if #post.save
redirect_to posts_path, :notice => "Your post has been saved"
else
render "new"
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update_attributes(params[:post])
redirect_to post_path, :notice => "Your post has been updated"
else
render "edit"
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path, :notice => "Your post has been deleted"
end
end
hope and thank anyone can help. Best, M
Strong Parameters
Rails uses a security mechanism called Strong Parameters by default. Its purpose is to ensure that only certain fields can be updated via a user submitted form.
#post.update_attributes(params[:post]) is an old-style syntax which does not work with strong parameters.
The updated convention is as follows
class PostsController
def update
# ...
#post.update(post_params) # instead of passing params[:post] directly, you pass it a strong parameters whitelist (see below)
end
def post_params
# we construct a strong parameters whitelist below
# require(:post) means that the `params` hash MUST contain a :post key
# permit(:title, :body, ...) = here we enumerate the attributes which we will accept from the form parameters; it acts as a whitelist
params.require(:post).permit(:title, :body, ...)
end
end
If you use rails g scaffold you can see an example of a controller which uses strong parameters.
DON'T DO THIS: To disable using strong parameters by default, you can set the following config value
config.active_record.whitelist_attributes = false
I included this for completeness' sake, however you should not do this as it will unnecessarily introduce security vulnerabilities to your code.
Additional Resources
ActiveModel::ForbiddenAttributesError when creating new user
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
https://github.com/rails/strong_parameters

Nested controller Resources, how to do update and destroy?

followed a tutorial to help me create instances within a controller. In other words transactions are created on the envelope controller. Like comments on a blog post.
Everything is working perfectly, but I don't know how to edit a transaction now or destroy one. All I need is to find how to edit an existing thing. Let me show you what I have so far:
in views/envelopes/edit (the form code was copied from where you can create new transactions)
<% #envelope.transactions.each do |transaction|%>
<%= form_for [#envelope, #envelope.transactions.build] do |f| %> <!--??? NEED EDIT INSTEAD OF BUILD ???-->
<%= f.text_field :name, "value" => transaction.name %>
<%= f.text_field :cash, "value" => transaction.cash %>
<%= f.submit "Submit" %>
<% end %>
<%= link_to "Remove", root_path %> <!--??? WANT TO REMOVE TRANSACTION ???-->
<% end %>
in routes.rb
resources :envelopes do
resources :transactions
end
in transaction controller
class TransactionsController < ApplicationController
def create
#envelope = Envelope.find(params[:envelope_id])
#transaction = #envelope.transactions.build(transaction_params)#(params[:transaction])
#transaction.save
#envelope.update_attributes :cash => #envelope.cash - #transaction.cash
redirect_to edit_envelope_path(#envelope)
end
def destroy
# ???
end
def update
# ???
end
def transaction_params
params.require(:transaction).permit(:cash, :name, :envelope_id)
end
end
def update
#transaction = #envelope.transactions.find(params[:id])
if #transaction.update(transaction_params)
redirect to #envelope, notice: 'Transaction was successfully updated'
else
redirect_to #envelope, notice: 'Transaction was not updated'
end
end
def destroy
#transaction.destroy
redirect_to #envelope, notice: 'Text here'
end

What's the solution for nil.update_attributes in ruby?

I'm unable to crack this error. Can't figure out why #customer is being assigned to value nil.
"You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.update_attributes"
Here is a snippet of the code :
def cedit
#title = "Edit Customer Information"
#customer = Customer.find(params[:id])
if request.post? and params[:customer]
attribute = params[:attribute]
case attribute
when "fname"
try_to_update #customer, attribute
when "email"
try_to_update #customer, attribute
when "add"
try_to_update #customer, attribute
end
end
end
private
def try_to_update(customer, attribute)
if customer.update_attributes(params[:customer])
flash[:notice] = "Customer's details updated."
redirect_to :action => "record", :controller => "c2"
end
end
First of all your code looks very none-rails like and breaks a couple of rails best practices. I would strongly recommend you read the official Rails guide and try to see if you can refactor some of your code.
I have too little information on what you are trying to do in the grand scale of things so I can't give you a full fledged answer. But you probably want to do something along these lines.
class CustomersController < ApplicationController
def update
#customer = Customer.find(params[:id])
if #customer.update_attributes(params[:customer])
flash[:notice] = "Customer updated"
end
redirect_to customer_path(#customer)
end
end
The view could look something like this:
<%= form_for(:customer) do |f| %>
<%= f.text_field :fname %>
<%= f.text_field :email %>
<%= f.text_field :add %>
<%= f.submit_tag "Update" %>
<% end %>
Good luck!

Why is my rails simple validation not working

I have a very basic rails app. I am playing around with validation.
Controller
class PagesController < ApplicationController
def new
#user = User.new
end
def edit
#user = User.new(:state => params[:state], :country => params[:country])
#user.save
end
end
Model
class User < ActiveRecord::Base
validates_presence_of :country
validates_presence_of :state
end
Views/pages/edit.html.erb
<% form_for :user, #user, :url => { :action => "edit" } do |f| %>
<%= f.text_field :country %>
<%= f.text_field :state %>
<%= submit_tag 'Create' %>
<% end %>
All I want to do is click Create when I have not entered anything and then have a validation come up and list the required fields. I've read some tutorials and they make it so simple. Why can't I get this to work? what am i doing wrong? When i create a scaffold then it works ok but that generates a scaffold.css in public/stylesheets. W/out scaffold right now i have no stylesheet in the public folder.
you're sending the form to the "edit" action, which doesn't do any processing. You need it to go to the "create" action, which should look something like this:
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = 'Your user was successfully created.'
redirect_to users_path
else
render :action => 'edit'
end
end
Your form_for line can be short and sweet. Also, you need to call error_messages to get the auto-generated list of errors:
<% form_for #user do |f| %>
<%= f.error_messages %>
...other fields go here...
<% end %>
See Rails conditional validation: if: doesn't working
It seems like you think validates ... if: works differently as it actually does. This line
validates :to_id, presence: true, if: :from_different_to?
translates to validate that the to_id is present if the from_different_to method returns true. When from_different_to evaluates to false then do not validate.

Resources