I'm pretty new to Rails and building a store locator app using a maps API. I have a Store model and a Toilet model that are associated so that users can selected if a store has a toilet or not when they are adding a store. However, I keep getting the following error when I try and add a store and select "no" for toilet_available:
NoMethodError in Stores#create
Showing /mnt/c/Users/Me/Dropbox/coding/SpaetiApp/app/views/stores/_form.html.erb where line #7 raised:
undefined method `map' for nil:NilClass
Extracted source (around line #7):
5
6
7
8
9
10
<%= f.input :address_line3, label: "Country", input_html: { value: "Germany"} %>
<%= f.input :beer_cost, label: "Cost of a Berliner Pilsner" %>
<%= select_tag(:toilet_id, options_for_select(@toilets), :prompt => "Toilet available?" ) %>
<%= f.input :facilities, label: "Facilities" %>
<%= f.button :submit %>
Trace of template inclusion: app/views/stores/new.html.erb
I don't get this error when I select "yes" for toilet_available, however. Here are the relevant files.
stores controller:
class StoresController < ApplicationController
before_action :find_store, only: [:show, :edit, :update, :destroy]
def index
if params[:toilet].blank?
@stores = Store.all.order("created_at DESC")
else
@toilet_id = Toilet.find_by(toilet_available: params[:toilet]).id
@stores = Store.where(:toilet_id => @toilet_id).order("created_at DESC")
end
end
def show
end
def edit
@toilets = Toilet.all.map{ |t| [t.toilet_available, t.id] }
end
def update
@store.toilet_id = params[:toilet_id]
if @store.update(store_params)
redirect_to store_path(@store)
else
render 'edit'
end
end
def new
@store = current_user.stores.build
@toilets = Toilet.all.map{ |t| [t.toilet_available, t.id] }
end
def create
@store = current_user.stores.build(store_params)
@store.toilet_id = params[:toilet_id]
if @store.save
redirect_to root_path
else
render 'new'
end
end
def destroy
@store.destroy
redirect_to root_path
end
private
def store_params
params.require(:store).permit(:name, :address, :address_line2, :address_line3, :beer_cost, :toilet_id)
end
def find_store
@store = Store.find(params[:id])
end
end
Schema.rb:
ActiveRecord::Schema.define(version: 20180814220216) do
create_table "stores", force: :cascade do |t|
t.string "name"
t.string "address"
t.float "beer_cost"
t.text "facilities"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.float "latitude"
t.float "longitude"
t.boolean "toilet"
t.string "address_line2"
t.integer "user_id"
t.integer "toilet_id"
t.string "address_line3"
end
create_table "toilets", force: :cascade do |t|
t.string "toilet_available"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
end
form:
<%= simple_form_for @store,:html => { :multipart => true } do |f| %>
<%= f.input :name, label: "Spaeti Name" %>
<%= f.input :address, label: "Address" %>
<%= f.input :address_line2, label: "City", input_html: { value: "Berlin" } %>
<%= f.input :address_line3, label: "Country", input_html: { value: "Germany"} %>
<%= f.input :beer_cost, label: "Cost of a Berliner Pilsner" %>
<%= select_tag(:toilet_id, options_for_select(@toilets), :prompt => "Toilet available?" ) %>
<%= f.input :facilities, label: "Facilities" %>
<%= f.button :submit %>
<% end %>
When it hits the create action it looks like '@store.save' is returning false and so it's rendering the 'new' template. However, the @toilets variable is not being set in the create action so the template sees it as nil.
Add this line to your create action as well and that will remove the error you're seeing:
@toilets = Toilet.pluck(:toilet_available, :id)
You could also set it in a before_action for the actions you wanted:
before_action :set_toilets, except: [:show, :index]
private
def set_toilets
@toilets = Toilet.pluck(:toilet_available, :id)
end