Undefined method `each' for nil:NilClass for show action - ruby-on-rails

I cannot seem to find the problem. In my venues show template, I want to show the venue name, and under that, I list all the venues in the database
<%= venu.name %>
<% #venus.each do |v| %>
I get the error that #venus is nil... but it is defined in my controller:
undefined method 'each' for nil:NilClass
venues_controller.rb
class VenuesController < ApplicationController
before_action :find_venue, only: [:show, :edit, :update, :destroy]
def index
#venus = Venue.all
end
def show
render :layout => nil
#venus = Venue.all
end
def new
#venu = Venue.new
end
def create
#venu = Venue.new(venue_params)
#venu.save
end
def edit
end
def update
end
def destroy
end
private
def venue_params
params.require(:venue).permit(:name, :phone, :address, :description, :type)
end
def find_venue
#venu = Venue.find(params[:id])
end
end
I have a resources :venues route in my routes.rb.
I am not sure what is causing this problem.

In your show method, you should render at the end
def show
#venus = Venue.all
render :layout => nil
end

Remove render :layout => nil from your show action.
And in your view, you need to use the instance variable #venu instead of venu
<%= #venu.name %>

I wonder why you use two instance variables for action show
#venu (via find_venue before_filter) & #venus via the action itself.
Best practice would be removing this line from action show, since show action normally used to show details for one element from a list.
#venus = Venue.all
and use #venu set by the before_filter instead.
But if you do want to keep both then re-order the lines in show action
#venus = Venue.all
render :layout => nil
Also, change the venu to #venu in the show.html.erb and if you like correct the typo in the instance variables #venu => #venue :) (Could happen to any of us)

Usually in the index method, it should show all the venus, and in the show method it would show detailed view of each venue.
Try setting something like this:
def index
#venus = Venue.all
end
def show
render :layout => nil
#venue = Venue.find(params[:id])
end
now in show.html.erb you should be able to use
#venue.name
and in your index.html.erb, you can iterate over the venus like so:
<% #venus.each do |v| %>
<%= link_to v do %>
<%= v.name %>
<% end %>
<% end %>

The above answer is correct. You can use #venus = Venue.all in your show view but because you render first it throws you an error. Just render at the end.

Related

Rails new initialized object create an empty record

I have two model called TodoList and TodoItem. In the TodoItem index page, i'm showing new form and list of todo items. Everything works perfect But it generate an empty record while in browser.
class TodoItem < ApplicationRecord
belongs_to :todo_list
end
class TodoList < ApplicationRecord
has_many :todo_items, dependent: :destroy
end
controllers have:
class TodoItemsController < ApplicationController
def index
#todo_list = TodoList.find(params[:todo_list_id])
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
end
def create
#todo_list = TodoList.find(params[:todo_list_id])
#todo_item = #todo_list.todo_items.new(params.require(:todo_item).permit(:description, :complete_at))
if #todo_item.save
redirect_to todo_list_todo_items_path(#todo_list)
end
end
end
index.html.erb
<div>
<div>
<% form_with(model: [#todo_list, #todo_item], local: true) do |f| %>
<% f.text_field :description %>
<% f.submit %>
<% end %>
</div>
<ul>
<% #todo_items.each do |todo_item| %>
<li><%= todo_item.description %></li>
<% end %>
</ul>
</div>
class TodoItemsController < ApplicationController
# use callbacks instead of repeating yourself
before_action :set_todolist, only: [:new, :create, :index]
def index
#todo_items = #todo_list.todo_items
#todo_item = TodoItem.new
end
def create
#todo_item = #todo_list.todo_items.new(todo_list_params)
if #todo_item.save
redirect_to [#todo_list, :todo_items]
else
render :new
end
end
private
def set_todolist
#todo_list = TodoList.find(params[:todo_list_id])
end
# use a private method for your params whitelist for readibility
# it also lets you reuse it for the update action
def todo_list_params
params.require(:todo_item)
.permit(:description, :complete_at)
end
end
You where setting a different instance variable (#new_todo) in you index action. The polymorphic route helpers that look up the route helpers from [#todo_list, #todo_item] call compact on the array. So if #todo_item is nil its going to call todo_lists_path instead - ooops!
You alway also need to consider how you are going to respond to invalid data. Usually in Rails this means rendering the new view. If you are rendering the form in another view such as the index view it can get kind of tricky to re-render the same view as you have to set up all the same data as that action which leads to duplication.
It seems #new_todo has been added to #todo_items somehow in index action:
def index
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
# The above line has a side effect: #todo_items = #todo_items + [#new_todo]
end
I'm not sure it's a bug or feature from Rails (I use Rails 6.1.1).
For a quick fix, you can change #todo_list.todo_items.new to TodoItem.new.

NoMethodError for nil after rendering view in controller

So I have this in my view _form.erb:
<div class="form-group">
<%= f.label :start_hour %><br>
<%= f.select :start_hour, #select_hours.map {|value| [value, value]} %>
</div>
And this in edit.erb:
<%= render 'form' %>
And this in my controller
def edit
#user = current_user
#employee = #user.employee
#hour = #employee.working_hours.find(params[:id])
#select_hours = Array.new
for i in 0..12
#select_hours.push("#{07+i}:00")
#select_hours.push("#{07+i}:30")
end
end
And then my update in my controller
def update
#employee = current_user.employee
#hour = #employee.working_hours.find(params[:id])
if #hour.update(working_hour_params)
redirect_to employee_working_hours_path(#employee)
else
render :edit
end
end
And here's my problem:
When I click update AND have wrong start_hour (custom validation, works when creating not editing), so #hour will not update. It renders again this view but with error that there is no method map for nil (so for #select_hours).
So how can I fix this?
You can use a callback in your controller and set up #select_hours for those two actions, this way if the update fails, the value will be present, but you don't have to assign the variable twice, like:
before_action :set_select_hours, only: %i[edit update]
before_action :set_employee, only: %i[edit update]
before_action :set_hour, only: %i[edit update]
def edit; end
def update
if #hour.update(working_hour_params)
redirect_to employee_working_hours_path(#employee)
else
render :edit
end
end
private
def set_select_hours
#select_hours = (0..12).flat_map do |index|
["#{07 + index}:00", "#{07 + index}:30"]
end
end
def set_employee
#employee = current_user.employee
end
def set_hour
#hour = #employee.working_hours.find(params[:id])
end
I think #employee can also be setted within a before callback.
I've added flat map to create and fill an array starting from the range, it's the "same" as before, just you don't need to initialize the array, use the for loop, and push the content for it.

Passing instance variable from controller to view in rails

I have a user profile controller called "userinfo" and it's corresponding view. The userinfo index is the root path. In the homepage(which is the userinfo index), I have a link that takes you to the user profile page. It is giving me this error when I click on the image on the view page:
My routes are:
My userinfos_controller:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#userinfors = Userinfo.where(:userinfo_id => #userinformation_user_id)
end
def show
#myvideo = Video.last
end
def new
#userinformation = current_user.userinfos.build
end
def create
#userinformation = current_user.userinfos.build(userinfo_params)
if #userinformation.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#userinformation.destroy
redirect_to userinfo_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major)
end
def find_userinfo
#userinformation = Userinfo.find(params[:id])
end
end
and my view is:
<%= link_to image_tag("student.png", class: 'right'), userinfo_path(#userinfors) %>
I thought maybe I must include ':index' in the 'before_action :find_userinfo' at the top of my controller. If I do that, the homepage doesn't even load and it gives me this error:
Try below code:
controller
def index
#userinfors = Userinfo.where(:userinfo_id => #userinformation_user_id) #pass id instead of object #userinformation_user_id
end
view
<% #userinfors.each do |u| %>
<%= link_to image_tag("student.png", class: 'right'), userinfo_path(u) %>
<% end %>
Your problem is that you're trying to do perform a lookup based on something that's not an ActiveRecord (database) attribute.
Your root goes to UserinfosController which expects #userinformation_user_id but I can't tell from your code where that comes from.
You need to define your route in order that this will be expecting for an specific param, maybe the user id, and then you're able to add the value within your view, in a link_to helper:
You could modify your routes.rb to expect an id as param:
get '/user_infors/:id', to: 'userinfos#index', as: 'userinfo_path'
Then in your controller, use a find to "find" in the database the user with such id. If you'd like to use where then that would give you a relationship with all the userinfos with the id being passed as param.
If you want so, then use Userinfo.where('userinfo_id = ?', params[:id]):
def index
#userinfors = Userinfo.find(params[:id])
end
And then in your view you can access to #userinfors:
<% #userinfors.each do |user| %>
<%= link_to image_tag 'student.png', class: 'right', userinfo_path(user) %>
<% end %>
I think you could define the index to get all the userinfors and a show method to get an specific one, as you're trying to do.

ruby on rails recieving error: First argument in form cannot contain nil or be empty

We have an error in our erb files and the screen shot is here
We have everything in games.html.erb as below
<h1>Games#game</h1>
<%= form_for(#game) do |f| %>
<div class="game-field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="actions">
<%= submit_tag "Game Created", :class=> "btn" %>
</div>
<% end %>
and here is the controller
class GamesController < ApplicationController
before_filter :store_location, :only => [:index, :show]
before_filter :require_user, :only => :show
before_filter :assign_game, :only => [:show, :destroy]
def index
#game = Game.new
end
def new
#game = Game.new
end
#game.add_player_from_user(current_user)
respond_to do |format|
format.html
format.json do render :json => {
:shouldStartNewRound => #game.is_ready_for_new_round?,
:shouldPassCards => #game.is_ready_to_pass?,
:isStartingFirstRound => #game.rounds.empty?,
:shouldReloadWaitAutoPlay => #game.should_reload?(current_player),
:shouldReloadAndJustWait => #game.should_reload_and_wait?(current_player)
}
end
end
end
def create
#game = Game.new(params[:game])
api_key = ""
api_secret = ""
#openTok = OpenTok::OpenTokSDK.new api_key, api_secret
session = #openTok.create_session request.remote_addr
#game.update_attributes(:session_id => session.session_id)
name = game[:name] # input from html
#game[:name] = name # setting input into db object (model)
if #game.save
redirect_to #game, alert: "game created"
else
render action: 'new'
end
end
def destory
#game.destory
redirect_to games_url
end
def reload
reload_partial
end
end
We are trying to create a deuces card game and we have bee stuck on that single problem. I have also looked for other similar problem that people have asked and we were not able to find the issue, it says that we have to put .new function to initialize the data. But the error is still showing up. Please help us!
You don't have a #game method in your GameController class.
Rename your #index action into #game or your View file.
I don't know how your routes file looks like but I suppose that the index action is the action that you call if somebody calls the root route, so http://localhost:3000/. If that's right then you have to rename game.html.erb into index.html.erb.
It looks like you are calling the action #game on controller Games. There is no #game method defined on the controller, so the instance variable #game never gets defined like it does in #index or #new. You can create a #game method in the controller and define #game in that method as needed.
Also, you have some code in the controller that needs to be inside of a method, starting at #game.add_player_from_user(current_user). Right now it's just executing at load time and probably not doing anything that makes any sense.
Finally, I noticed that you misspelled 'destroy' in a couple of places - search for 'destory' and replace with 'destroy' to avoid problems later.

Rails says class is nil despite console showing database

I have a database of objects (tools) in my Ruby on Rails project. When I use "rails dbconsole" and
select * from tools;
it returns a whole list of tool objects. But when I try to view the following page, it gives me an error.
Page:
<!DOCTYPE html>
<html lang="en">
<%= stylesheet_link_tag "tools", :media => "all" %>
<body>
<%= #tools.each do |tool| %>
<%= link_to(tool(image_tag.image_url)) %>
<% end %>
</body>
</html>
Error:
undefined method `each' for nil:NilClass
When I change the code to add an if statement against nil objects, the page works (without displaying any tools).
<% if #tools.nil? %>
<% else %>
<%= #tools.each do |tool| %>
<%= link_to(tool(image_tag.image_url)) %>
<% end %>
<% end %>
So it seems like #tools doesn't have any values in it, but when I look at it in the dbconsole, there are tools there. I can't figure this out, and I've spent the past few days googling for answers, so any and all ideas would be welcome!
EDIT: Added tools_controller.rb
class ToolsController < ApplicationController
before_filter :check_authentication
def check_authentication
unless session[:user_id]
session[:intended_action] = action_name
session[:intended_controller] = controller_name
redirect_to new_session_url
end
end
def new
#tool = Tool.new
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #tool }
end
end
def show
end
def index
#tools = Tool.all
end
# GET /tools/1/edit
def edit
#tool = Tool.find(params[:id])
end
# POST /tools
# POST /tools.json
def create
#tool = Tool.new(params[:tool])
respond_to do |format|
if #tool.save
format.html { redirect_to #tool, :notice => 'tool was successfully created.' }
format.json { render :json => #tool, :status => :created, :location => #tool }
else
format.html { render :action => "new" }
format.json { render :json => #tool.errors, :status => :unprocessable_entity }
end
end
end
end
Loading #tools in a before_filter for every action as #nbarraille has suggested is a bad idea, because there are many (probably most) actions where you will definitely not need the full set of tools (e.g. create and destroy). The line #tools = Tool.all hits your database so you should minimize the number of times you use it.
For the case you have here, you only need to change your show action to get this to work:
def show
#tools = Tool.all
end
However, note that normally the show action is for displaying a single resource (tool), not the whole list of resources (which is normally done in the index action). It looks like you're deviating from the normal way of doing things, is there any particular reason why?
In order for the #tools variable to be accessible from your view, you need to declare it in your controller, like this:
#tools = Tool.all
If you want it to be only accessible from one page, just declare it in the according method.
Here is an example, assuming you want to make the variable available for your home/index page:
class HomeController < ApplicationController
def index
#tools = Tool.all
respond_to do |format|
format.html # index.html.erb
end
end
end
If you want it to be accessible in all your pages, you can declare it in the before_filter method of your ApplicationController.
Here is how to do this:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :load_variables
# Load variables to be used everywhere
def load_variables
#tools = Tool.all
end
end

Resources