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

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!

Related

Why is my form not saving? form_for question

I have this form. I am new to rails and I am trying to write a simple ecommerce site. This is the only part not working. (It worked 2 days ago I sear)
<%= form_tag line_items_path do%>
<%binding.pry%>
<%= hidden_field_tag :lite_item, :order_id, #order.id%>
<%= hidden_field_tag :line_item, :menu_item_id, #menu_item.id%>
<%= number_field_tag :line_item, :quantity, 1 %>
<%= submit_tag "Add to Cart"%>
<% end %>
It gives params that look like:
#<ActionController::Parameters {"authenticity_token"=>"VECKnS5SBot1rCyekepPXZa7TyTYkfFi0KdNRTB617ZnelmQo8Lkz_cJmQ8nAmCHUdDlPu1mpkhrPvMKysfjew", "order_id"=>"1", "menu_item_id"=>"1", "quantity"=>"1", "commit"=>"Add to Cart", "controller"=>"line_items", "action"=>"create"} permitted: false>
The controller for the view looks like this:
class MenusController < ApplicationController
def index
#menu_items = MenuItem.all
end
def show
#menu_item = MenuItem.find(params[:id])
#line_items = current_order.line_items.build
end
end
The form is really going through the line_items controller
def create
binding.pry
#line_item = LineItem.create(line_item_params)
if #line_item.save
#order.line_item_id = #line_item.id
#order.save
redirect_to cart_path(#current_cart), notice: "Item added to cart."
else
redirect_to menu_path(#menu_item), alert: "Item did not add to cart."
end
end
With strong params like this
def line_item_params
params.require(:line_item).permit(:menu_item_id, :quantity, :order_id)
end
It should use the line_items_path POST>
If anything else is needed just ask. Thanks in advance.
There are a lot of problems here.
The signature is hidden_field_tag(name, value = nil, options = {}). So the parameters you would actually be creating with that form is:
{
"lite_item" => "order_id", # check your spelling...
"line_item" => "quantity"
}
Oops. And that not even going to happen as <%= number_field_tag :line_item, :quantity, 1 %> will raise since you're passing an integer where the method expects a hash.
If you really have to create the inputs manually you would want:
<%= hidden_field_tag "line_item[order_id]", #order.id %>
But since you actually have a model there is no reason why you should be using form_tag instead of form_for(#line_item) or form_with(model: #line_item).
<%= form_for(#line_item) do |form| %>
<%= form.hidden_field :order_id %>
<%= form.hidden_field :menu_item_id %>
<%= form.number_field :quantity %>
<%= form.submit_tag "Add to Cart"%>
<% end %>
The controller should also use the correct pluralization for the instance variable:
def show
#menu_item = MenuItem.find(params[:id])
#line_item = current_order.line_items.build
end
Your create method is also pretty questionable. All you should need is:
def create
# use .new not .create
#line_item = LineItem.new(line_item_params)
if #line_item.save
redirect_to cart_path(#current_cart), notice: "Item added to cart."
else
redirect_to menu_path(#menu_item), alert: "Item did not add to cart."
end
end
I have no idea why you think you need to update #order here. Your controller should just really be adding a row to what is essentially a join table.

private method `update_without_password' called for nil:NilClass

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!

Cannot enter simply form information into SQLite DB (Rails)

So, I'm running into a fairly simple problem, where I cannot enter some simple form values into my SQLite DB (Rails).
Interestingly, the code doesn't fail - I submit the form, and am redirected successfully to the correct URL - and a record IS inserted into the DB, but the columns "name, type and user_id" are not filled with anything. To clarify, the columns are blank, for that new record.
If I comment out the code in the "create" and simply spit out the params (render plain: params[:plan].inspect) I do see all the correct parameters filled out, so I have a feeling there must be something wrong in the line:
#plan = Plan.new(params[:plan])
I'm really stuck here, any guidance would be much appreciated!
The create form
<h1> Create a new plan </h1>
<%= form_for :plan, url: plans_path do |f| %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :type %><br>
<%= f.text_field :type %>
</p>
<%= f.hidden_field :user_id, :value => current_user.id %>
<p>
<%= f.submit %>
</p>
<% end %>
plans_controller.rb
class PlansController < ApplicationController
def index
#plans = Plan.all
end
def show
#plan = Plan.find(params[:id])
end
def new
#plan = Plan.new
end
def create
#render plain: params[:plan].inspect
params.permit!
#plan = Plan.new(params[:plan])
if #plan.save
redirect_to #plan
else
redirect_to dashboard_path, :notice => "Plan NOT Created!"
end
end
end
The Model
class Plan < ActiveRecord::Base
end
Edit the plans_controller.rb:-
def create
#render plain: params[:plan].inspect
#plan = Plan.new(plan_params)
if #plan.save
redirect_to #plan
else
redirect_to dashboard_path, :notice => "Plan NOT Created!"
end
end
private
def plan_params
params.require(:plan).permit(:name,:type,:user_id)
end
Change the field name type as :-
rails g migration rename_fields_in_plans
class RenameFieldsInPlans < ActiveRecord::Migration
def change
rename_column :plans, :type, :plan_type
end
end
Then run the command:-
rake db:migrate

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

Is there a way to do this in Rails without mass assignment?

Members create votes that both belong to them and to another model, Issues. Currently I'm doing this with a hidden form and passing the appropriate parameters. Here's the code on the issues index view:
<%= form_for(#vote) do |f| %>
<%= f.hidden_field "issue_id", :value => issue.id %>
<%= f.hidden_field "member_id", :value => session[:member_id] %>
<%= f.hidden_field "type", :value => :Upvote %>
<%= f.label issue.upvotes_count(issue.id) %>
<%= submit_tag "Up", :class => 'up-vote' %>
<% end %>
This doesn't seem ideal as it leaves issue_id and member_id open to mass assignment. Is there a better way to do this with a button_to tag or something?
Here's the controller code:
class VotesController < ApplicationController
#GET
def new
#vote = Vote.new
end
# POST
def create
#vote = Vote.new(params[:vote])
#vote.member_id = current_member
if #vote.save
redirect_to issues_path
else
redirect_to issues_path, notice: "you must be logged in to vote"
end
end
end
and
class IssuesController < ApplicationController
# GET
def index
#issues = Issue.find(:all)
#vote = Vote.new
end
# GET
def show
#issue = Issue.find(params[:id])
respond_to do |format|
format.html
format.js
end
end
end
Use scope in the controller:
#issue = Issue.find(params[:issue_id])
#vote = #issue.votes.new(params[:vote])
#vote.save
and do not pass member_id and issue_id to hidden fields.
If you have proper nested RESTful routes you should be able to get params[:issue_id] directly.
If issue and member_id are available before you vote.save! in the controller, you can set them manually there.
Normally you get values like member_id from current_user in the controller rather than passing it via form parameters. How you have it currently does expose you to mass-assignment.
Do members have to login before voting? If so, then you don't need to include member_id as a hidden field because you can grab current_user in the controller and this will provide good protection since there wouldn't be any advantage for a member to hack issue_id or type.

Resources