Liz Liz - 1 year ago 183
Ruby Question

Ruby on Rails Automatic Variable Name (Plural vs Singular)

I am building a rails app that has several quizzes with the exact same structure:

  • A name such as

  • new.html.erb
    views for each quiz

  • A partial
    that stores the actual quiz questions for each quiz

  • A model and controller for each separate quiz

  • Two places (the main nav and a user profile page) hold a link to the
    view (or to the
    view if people have already taken the quiz, decided by an erb if/else statement) of each quiz

I successfully managed to set up the first quiz, but as I am new to Ruby I made the (terrible) mistake of setting up a variable name that ended in -s (
) for the singular version of the quiz. This wrought havoc with Ruby's singular vs. plural naming conventions.

I currently have the code for my second quiz (
) set up, but am getting a no method error saying
undefined method 'quiz_bfs' for #<User:0x007fb9a3247f98>
on the definition of new quiz in my controller.

Here's my controller:

class QuizBfController < ApplicationController
before_action :require_sign_in

def show
@quiz_bf = QuizBf.find(params[:id])

def new
@quiz_bf = current_user.quiz_bfs || current_user.build_quiz_bfs

def create
@quiz_bf =

@quiz_bf.bf01 = params[:quiz_bfs][:bf01]
@quiz_bf.bf02 = params[:quiz_bfs][:bf02]
@quiz_bf.bf03 = params[:quiz_bfs][:bf03]
@quiz_bf.bf04 = params[:quiz_bfs][:bf04]
@quiz_bf.bf05 = params[:quiz_bfs][:bf05]
@quiz_bf.bf06 = params[:quiz_bfs][:bf06]
@quiz_bf.bf07 = params[:quiz_bfs][:bf07]
@quiz_bf.bf08 = params[:quiz_bfs][:bf08]
@quiz_bf.bf09 = params[:quiz_bfs][:bf09]
@quiz_bf.bf10 = params[:quiz_bfs][:bf10]

@quiz_bf.user = current_user

flash[:notice] = "Quiz results saved successfully."
redirect_to user_path(current_user)
flash[:alert] = "Sorry, your quiz results failed to save."
redirect_to welcome_index_path

def edit
@quiz_bf = QuizBf.find(params[:id])

def update
@quiz_bf = QuizBf.find(params[:id])


flash[:notice] = "Post was updated successfully."
redirect_to user_path(current_user)
else[:alert] = "There was an error saving the post. Please try again."
redirect_to welcome_index_path

def quiz_bfs_params
params.require(:quiz_bfs).permit(:bf01, :bf02, :bf03, :bf04, :bf05, :bf06, :bf07, :bf08, :bf09, :bf10)


Here's my model (user
has_one :quiz_bf

class QuizBf < ActiveRecord::Base
before_save :set_bfcode

def set_bfcode
self.bfcode = "#{self.bf01}#{self.bf02}#{self.bf03}-#{self.bf04}#{self.bf05}#{self.bf06}-#{self.bf07}#{self.bf08}#{self.bf09}#{self.bf10}"

belongs_to :user
validates :user, presence: true

Here is my user model:

class User < ActiveRecord::Base
before_save { = email.downcase }

validates :name, length: { minimum: 1, maximum: 100 }, presence: true
validates :password, presence: true, length: { minimum: 6 }, unless: :password_digest
validates :password, length: { minimum: 6 }, allow_blank: true
validates :email,
presence: true,
uniqueness: { case_sensitive: false },
length: { minimum: 3, maximum: 254 }


has_one :quiz_bs
has_one :quiz_bf


Here are the germane bits of my quiz partial:

<%= form_for @quiz_bf do |f| %>


<%= f.submit "Submit Answers" %>

Here's how I'm linking from my

<%= render partial: "quiz", locals: { url: quiz_bfs_path, method: :post } %>

And my

<%= render "quiz", url: quiz_bf_path(@quiz_bf), method: :put %>

And (finally) here's how I'm linking to it from my

<% if current_user.quiz_bfs == nil? %>
<%= link_to "Body Flexibility Quiz", quiz_bf_path %>
<% else %>
<%= link_to "Body Flexibility Quiz ✓", edit_quiz_bf_path(current_user.quiz_bfs) %>
<% end %>

And my users

<% if @user.quiz_bfs == nil %>
<p><%= link_to "Test Your Body Flexibility", new_quiz_bf_path %></p>
<% else %>
<h3><%= @user.quiz_bfs.bfcode %></h3>
<p><%= link_to "Retest Results", edit_quiz_bf_path(@user.quiz_bfs) %></p>
<% end %>

I know this code worked successfully for the
, but as you can see in my rake routes (shown below), the plural/singular issue of my idiotic variable name made it hard to see what was actually named what. Can anyone with more experienced ruby eyes than mine show me what I need to change?

quiz_bs GET /quiz_bs(.:format) quiz_bs#index
POST /quiz_bs(.:format) quiz_bs#create
new_quiz_b GET /quiz_bs/new(.:format) quiz_bs#new
edit_quiz_b GET /quiz_bs/:id/edit(.:format) quiz_bs#edit
quiz_b GET /quiz_bs/:id(.:format) quiz_bs#show
PATCH /quiz_bs/:id(.:format) quiz_bs#update
PUT /quiz_bs/:id(.:format) quiz_bs#update
DELETE /quiz_bs/:id(.:format) quiz_bs#destroy
quiz_bf_index GET /quiz_bf(.:format) quiz_bf#index
POST /quiz_bf(.:format) quiz_bf#create
new_quiz_bf GET /quiz_bf/new(.:format) quiz_bf#new
edit_quiz_bf GET /quiz_bf/:id/edit(.:format) quiz_bf#edit
quiz_bf GET /quiz_bf/:id(.:format) quiz_bf#show
PATCH /quiz_bf/:id(.:format) quiz_bf#update
PUT /quiz_bf/:id(.:format) quiz_bf#update
DELETE /quiz_bf/:id(.:format) quiz_bf#destroy

Answer Source

Undefined method error

The issue with the undefined method probably has to do with the call being a plural call (quiz_bfs), which expects that you have a has_many association, while your model only defines a has_one association:

has_one :quiz_bs
has_one :quiz_bf

Either a) the use of quiz_bfs is a typo and should be quiz_bf, b) the has_one is incorrect, or c) you don't need the call to current_user.quiz_bfs at all to assign a value to @quiz_bf. It's hard to tell without knowing more details, but it looks like you can just remove current_user.quiz_bfs.

Bonus on routes

While it wasn't this question, specifically, you mentioned the pain of a resource ending in 's' being treated as a plural. If you haven't seen it yet, you can have your quiz_bs routes named appropriately. To do so, you can use this form in your config/routes.rb file:

resources :quiz_bs, as: :quiz_bs

This should give you the route helper names that you would like. You can read more about that in the Overriding Named Helpers section of Rails Routing from the Outside In.

You may want to name your controller QuizBsController, as well, and you can use the controller: override on the resources route definition to do that. Try this and see if it gives you the right controller:

resources :quiz_bs, controller: 'quiz_bs'

You can always combine both route methods and use something like this:

resources :quiz_bs, controller: 'quiz_bs', as: :quiz_bs

Check out the Specifying a Controller to Use section of Rails Routing from the Outside In for more information and usage details.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download