undefined method `destroy' for nil:NilClass - ruby-on-rails

My app successfully creates rows for a table every time a user submits some stats. Now I want to provide a delete button to be able to delete some rows.
When I click the delete button I get the error:
undefined method `destroy' for nil:NilClass
Seems like either rails doesn't understand the destroy method in this case for whatever reason, or rails doesn't see anything to destroy, hence the nil:NilClass.
My first question is in identifying which is the case, if either of those.
My second question is fixing it :D
Here's my show.html:
<% provide(:title, "Log" ) %>
<% provide(:heading, "Your Progress Log") %>
<div class="row">
<div class="span8">
<% if #user.status_update.any? %>
<h3>Status Updates (<%= #user.status_update.count %>)</h3>
<table>
<thead>
<tr>
<th>Entry Date</th>
<th>Weight</th>
<th>BF %</th>
<th>LBM</th>
<th>Fat</th>
<th>Weight Change</th>
<th>BF % Change</th>
<th>Fat Change</th>
</tr>
</thead>
<tbody class = "status updates">
<%= render #status_updates %>
</tbody>
<% end %>
</div>
</div>
The "<%= render #status_updates %>" calls the _status_update partial.
<tr>
.
.
.
.
<% if current_user==(status_update.user) %>
<td>
<%= link_to "delete", status_update, method: :delete %>
</td>
<% end %>
</tr>
And then finally, here is the StatusUpdateController
def destroy
#status_update.destroy
redirect_to root_url
end
Here are the parameters on the error page:
{"_method"=>"delete",
"authenticity_token"=>"w9eYIUEd2taghvzlo7p3uw0vdOZIVsZ1zYIBxgfBymw=",
"id"=>"106"}

As you are not showing any code (filters or so) that could assign #status_update, I assume there is none. So, the instance variable #status_update in your in your destroy method is not set. You need to query the class with the object's ID.
Rewrite your destroy method as follows:
def destroy
#status_update = StatusUpdate.find(params[:id])
if #status_update.present?
#status_update.destroy
end
redirect_to root_url
end
Note: Replace the classname StatusUpdate with your actual class name.

Related

Pass data from index method of one controller to create of another

I generated a devise model User, and I also have a Conversation controller. I'm showing all users, except the logged in one, and I'm trying to create a new Conversation between user1 and user2, but I get redirected to the index method of the Conversation Controller, not the create one. I understood from this link that making a post from one controller to another is a bad idea Rails: How to POST internally to another controller action?.
I've also tried to make a send_message method inside the Users controller and define it as a post in routes, but I get redirected to the show method of the Users controller.
What is the clean way of doing this?
class UsersController < ApplicationController
before_action :authenticate_user!
def index
#users = User.where.not(id: current_user.id)
end
def send_message
# #conversation = Conversation.new(conversation_params)
# if #conversation.save
#
# end
end
end
index.html.erb
<div class="col-xs-12 col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h1> User's index </h1>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Email</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% #users.each do |user| %>
<tr>
<td><%= user.email %></td>
<td><%= time_ago_in_words(user.created_at) %> ago</td>
<td>
<div class="btn-group">
<%= link_to 'Send', conversations_path(sender_id: current_user.id, recipient_id: user.id) %>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
Edit:
private
def conversation_params
params.require(:conversation).permit(:sender_id, :recipient_id)
end
<ActionController::Parameters {"_method"=>"post", "authenticity_token"=>"394MDmcVVelccU//8ISYeqmk146exYc6G7SrrAhbCA/yQ/K8KTpSn/0EkXlZ4hB/g==", "recipient_id"=>"1", "sender_id"=>"3", "controller"=>"conversations", "action"=>"create"} permitted: false>
By default link_to helper sends GET request. You can do it with adding method: :post to its options.
<%= link_to 'Send', path, method: :post %>
You could redirect to new_converstion_path instead of conversations_path. And link by default sends GET not POST request.

devise admin user approval setup

Seems like the tutorial located here has a couple omissions:
When creating the admin accessible controller method, they don't specify which custom devise controller to use, or which base controller to inherit from. So I've placed my code in a PagesController:
class PagesController < ApplicationController
def approve_users
if current_user.admin?
if params[:approved] == "false"
#users = User.find_by_approved(false)
else
#users = User.all
end
end
end
end
The view code that allows you to switch between all users and all unapproved users results in a NoMethodError in Pages#approve_users: undefined method 'each' for User whenever you select to show the users for whom :approved => false. I know why noMethodErrors spring up in app development, and would normally be able to wrap my head around why I'm getting this error. It works when #users = User.all, but not when #users = User.find_by_approved(false)
<% if current_user.admin? %>
<h2>Users</h2>
<%= link_to "All Users", :action => "approve_users" %> | <%= link_to "Users awaiting approval", :action => "approve_users", :approved => "false" %>
<div class="ui form">
<table>
<thead>
<tr scope="col">
<th>First name</th>
<th>Last name</th>
<th>E-mail</th>
<th>Approve</th>
</tr>
</thead>
<% #users.each do |user| %>
<tbody>
<tr>
<td><%= user.firstname %></td>
<td><%= user.lastname %></td>
<td><%= user.email %></td>
<td class="ui checkbox">
<input type="checkbox" tabindex="0" class="hidden">
</td>
</tr>
</tbody>
<% end %>
</table>
</div>
<% end %>
The wiki says it that it provides a simple way to approve users, but their view code actually just provides a simple way to list all users. I'm assuming that I need to use a form helper.
I made a custom admin controller specifically for an admin control panel and put all of the tools in an index page. You can probably do it a number of ways, though.
The common consensus on this seems to be that if you switch #users = User.find_by_approved(false) to #users = User.where(approved: false), it works better. That's what I currently have and it works very well.
I had this problem as well, and I ended up scrapping devise and making a custom user sign in method. You should be able to make it work, though. I followed the tutorial here and it helped immensely. Basically, you want to create a method which will approve users in your admin controller. This is the one I used:
`
def approve
User.where(id: params[:user_id]).update_all(approved: true)
redirect_to admin_index_path
end
From there, you add a put method to your routes.
put 'approve_admin', to: "admin#approve", as: :approve_admin
Finally, wrap your list of users in a form tag and add a hash of all of the user IDs you want to update as a hash.
<%= form_tag(approve_admin_path, method: :put) do %>
<% for user in #unapproved_users %>
<tr>
<td class="mdl-data-table__cell--non-numeric"><%= check_box_tag "user_id[]", user.id %></td>
<td><%= user.name %></td>
<td><%= user.email %></td>
</tr>
<% end %>
</tbody>
</table>
<%= submit_tag "Mark as approved", class: 'mdl-button mdl-js-button mdl-button--raised approve' %>
<% end %>
I made a method for approving users and another for unapproving users so I switched up the #users.each iteration in favor of a for iteration. I added #unapproved_users to my index method to make this work properly. Hope this works for you!

Rails 4 Restaurant order system ActiveRecord::RecordNotFound in ItemsController#create

I am creating a simple restaurant ordering system with a one to many relationship between a menu and its items. One menu has many items. For the sake of simplicity it is not a many to many.
I am able to create menus fine, but would like to be able to add and show the menu items for that menu in the menus show action.
The menu show action displays okay but when I try to add a new menu item I get the following error:
ActiveRecord::RecordNotFound in ItemsController#create
Couldn't find Menu with 'id'=
raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
And here is the queries from the terminal:
Started POST "/items" for ::1 at 2015-01-11 16:09:44 +0000
Processing by ItemsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"***", "item"=>{"name"=>"Test", "price"=>"23", "course"=>"Main", "vegetarian"=>"1", "allergy"=>""}, "commit"=>"Add item"}
Menu Load (0.3ms) SELECT `menus`.* FROM `menus` WHERE `menus`.`id` = NULL LIMIT 1
Completed 404 Not Found in 8ms
EDIT: I followed doon's advice and looked into nested resources and this is indeed a better way of doing it. The updated code is below:
routes.rb
resources :menus do
resources :items
end
menus_controller.rb
def show
#menu = Menu.find(params[:id])
#items = #menu.items
end
items_controller.rb
def create
#menu = Menu.find(params[:menu_id])
#item = #menu.items.create!(item_params)
if #item.save
flash[:success] = "Item added!"
redirect_to #menu
else
flash[:danger] = "Errors found!"
redirect_to #menu
end
end
private
def item_params
params.require(:item).permit(:name, :price, :course, :vegetarian, :allergy, :menu_id)
end
And the Menus
show.html.erb
<%= link_to "<< Back", menus_path, data: { confirm: back_message } %>
<h1><%= #menu.name %> menu</h1>
<center><button id="toggleButton" class="btn btn-sm btn-info">Show/Hide Add Item Form</button></center>
<br>
<div class="row">
<div class="col-xs-offset-3 col-xs-6 toggleDiv hideDiv">
<%= form_for [#menu, Item.new] do |f| %>
<table class="table table-condensed table-no-border">
<tr>
<th scope="row" class="col-xs-2">Name:</th>
<td class="col-xs-10"><%= f.text_field :name %></td>
</tr>
<tr>
<th scope="row">Price:</th>
<td><%= f.text_field :price %></td>
</tr>
<tr>
<th scope="row">Course:</th>
<td><%= f.select(:course, options_for_select([['Starter', 'Starter'], ['Main', 'Main'], ['Dessert', 'Dessert'], ['Drink', 'Drink']]), prompt: "Please select...", class: 'form-control') %></td>
</tr>
<tr>
<th scope="row">Vegetarian:</th>
<td><%= f.check_box :vegetarian %></td>
</tr>
<tr>
<th scope="row">Allergy:</th>
<td><%= f.text_field :allergy %></td>
</tr>
<tr><td colspan="2"><%= f.submit "Add item", class: "btn btn-sm btn-success col-xs-offset-4 col-xs-4" %></td></tr>
</table>
<% end %>
</div>
</div>
<table class="table table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Course</th>
<th>Vegetarian</th>
<th>Allergy</th>
</tr>
</thead>
<tbody>
<% #items.each do |item| %>
<tr>
<td><%= item.name %></td>
<td><%= number_to_currency(item.price, unit: "£") %></td>
<td><%= item.course %></td>
<td><%= item.vegetarian %></td>
<td><%= item.allergy %></td>
</tr>
<% end %>
</tbody>
</table>
What I would probably do in a case like this (especially if your item doesn't exist outside of a menu) is to use Nested Resources:
http://guides.rubyonrails.org/routing.html#nested-resources
http://railscasts.com/episodes/139-nested-resources (a bit dated, but still a decent grounding)
Below are some changes to get you pointed in this direction.
config/routes.rb
resources :menu do
resources :items
end
now our urls will look like
/menu/:menu_id/items/
So we need to adjust the items_controller to get the menu by looking at :menu_id, and we don't need the hidden field anymore. I put it in a before_action as every method in the controller will build through the association.
items_controller.rb
class ItemsController < ApplicationController
before_action :find_menu
...
def create
#item = #menu.items.new(item_params)
if #item.save
redirect_to #menu, notice: 'item added'
else
redirect_to #menu, warning: 'item failed'
end
end
...
private
def find_menu
#menu = Menu.find(params[:menu_id])
end
end
if you want to show it on the menu, we need a new Item to display.
menus_controller.rb
def show
#menu = Menu.find(params[:id])
#item = #menu.items.new
end
Then we need to make use of the nested resource in the menus/show view. By passing in the array of the menu and the item, rails will generate the correct path.
menus/show.html.erb
<%= form_for [#menu,#item] do |f| %>
Are you posting to "/post" or "post/[:id]"?
#menu = Menu.find(params[:id])
Will not find anything if you do not pass it an id. The id can come from the URL params, but this means you should be sending your request to "/post/[:menuid].
You can use the gem https://github.com/charliesome/better_errors to debug your program at the controller level and execute rails console there. just put in "fail" in your controller and then you will have a command line interface where you can inspect your params value and ensure it has the id included and play with the console there.
I have now solved it. Thanks to better_errors I was able to better understand what was going on.
I implemented the show action in menus_controller.rb from aaron.
def show
#item = Item.new
#menu = Menu.includes(:items).find(params[:id])
end
I needed a way to pass the menu id into the menu_id field, so I added a hidden field in the table or show.html.erb passing the current #menu.id into the :menu_id field.
<%= f.hidden_field :menu_id, :value => #menu.id %>
I have read online that passing values through hidden fields is not a great idea though.
Then in the create action of the Items Controller I was able to make a new record as usual redirecting to the existing show view using the menu id from the item object.
def create
#item = Item.new(item_params)
if #item.save
redirect_to menu_path(#item.menu_id)
else
redirect_to menu_path(#menu.menu_id)
end
end
It feels like a bit of a hack so open to suggestions on how to improve.

ActionMailer : NoMethodError in my_class#create undefined method `each' for nil:NilClass

Actionmailer sends out this message when I try to email an index page :
"undefined method `each' for nil:NilClass"
I think I just need to pass the right argument in my controller, I just can't get my head around this. Please help!?
My inventories_controller :
def create
#inventory = Inventory.new(inventory_params)
if #inventory.save
AppMailer.send_inventory(#inventory).deliver
redirect_to inventories_path
else
render :new
end
end
Mailer class:
def send_inventory(inventory)
#inventories = inventory
mail to: inventory.email, from: "marvin8214#gmail.com", subject: "Last Month Sushi Bar Inventory - ITRBA"
end
mail view :
<div class="span5 well">
<% #inventories.each do |inventory| %>
<table>
<tr>
<td><%= inventory.name %> </td>
<td><%= inventory.amount %> </td>
<td><%= inventory.unit %> </td>
</tr>
</table>
<% end %>
</div>
In Mailer class you are passing #inventory
In mailer view however you are accessing #inventories
If you need a collection of model in your view, you require to create a collection instance variable.
UPDATE:
Mailer class:
def send_inventory(inventory)
#inventory = inventory
mail to: inventory.email, from: "marvin8214#gmail.com", subject: "Last Month Sushi Bar Inventory - ITRBA"
end
Mailer View (erb file):
Hi,
Your inventory details:
<table>
<tr>
<td><%= #inventory.name %> </td>
<td><%= #inventory.amount %> </td>
<td><%= #inventory.unit %> </td>
</tr>
</table>
Thanks

Building has many through relationship rails from existing records

I'm a bit stuck on creating a form for a has many through relationship. At the moment my models are that Songs can have many Setlists and vice versa, through Allocations.
I'm currently working on an edit page, in which a user can add songs to a setlist. The view currently looks like this:
<h1>Edit a Setlist</h1>
<div class="row">
<div class="span8">
<%=form_for(#setlist) do|f|%>
<%=f.label :date, "Set a date" %>
<span><%=f.date_select :date%><span>
<div>
<div id="library">
<%= render 'library' %>
</div>
<%= render 'songs_in_set' %>
</div>
<%=f.submit "Save", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
The library partial referred to above:
<table class= "table table-striped table-bordered">
<thead>
<th>Title</th>
<th>Artist</th>
<th>Add to Set</th>
</thead>
<tbody>
<% #songs.each do |song| %>
<tr>
<td><%= song.title %></td>
<td><%= song.artist %></td>
<td><%= link_to "ADD", '#' %></td>
</tr>
<% end %>
</tbody>
</table>
I want to convert that link in the library partial to a form for creating a new allocation, thus adding a song to the set list.
The relevant bits from my controllers:
The setlist controller:
def edit
#songs = Song.all(order: 'title')
#setlist = Setlist.find(params[:id])
#allocations = #setlist.allocations
end
def update
#setlist = Setlist.find(params[:id])
if #setlist.update_attributes(params[:setlist])
flash[:success] = "SAVED!"
redirect_to setlist_path(#setlist)
else
render 'edit'
end
end
and the allocations controller:
def new
#allocation = Allocation.new
end
def create
#allocation = Allocation.new(params[:allocation])
if #allocation.save
flash[:success] = "Songs added"
redirect_to edit_setlist_path(#allocation.setlist_id)
else
flash[:fail] = "Error"
redirect_to edit_setlist_path(#allocation.setlist_id)
end
end
I know that I've got to do something along the lines of setlist.allocations.build but I'm having trouble getting the right parameters (getting each individual song id and the setlist id). I've tried putting a form for helper within the songs.each do loop but that didn't seem to work. I'm a bit lost so any pointers in the right direction would be appreciated. Thanks in advance
Try adding this to Setlist:
accepts_nested_attributes_for :allocations, :dependent => :destroy
Then you can nest association fieldsets within your setlist form. In the library partial:
<td>
<%= f.fields_for :allocations do |ff| %>
<%= f.hidden_field :song_id, song.id %>
...
<% end %>
</td>
This should work if you want to create allocations for songs which exist already, but you might need something different for a scenario when you are also creating songs in the same form. I had a problem like that not long ago, and wasn't able to find a clean solution, but let me know and I can share that as well...

Resources