OA Keefe
OA Keefe

Reputation: 31

Rails Undefined Method - NoMethodError

I am vaguely following http://guides.rubyonrails.org/getting_started.html, Part 6.

Using Rails v 4.1.8 SQLite DB

I get this error after adding a second model:

NoMethodError in Pages#show

Showing M:/ICT/Rails/Salacity/app/views/pages/show.html.erb where line

24 raised:

undefined method `links' for #

Extracted source (around line #24)

Links

<% @page.links.each do |link| %>

ID: <%= link.child__id %>

Rails.root: M:/ICT/Rails/Salacity Application Trace | Framework Trace | Full Trace

app/views/pages/show.html.erb:24:in `_app_views_pages_show_html_erb___283878207_51684456'

This is app/views/pages/show.html.erb

<p id="notice"><%= notice %></p>

<p>
  <strong>Page:</strong>
  <%= @page.page_id %>
</p>

<p>
    <strong>Body:</strong>
    <%= @page.body %>
</p>

<p>
    <strong>Branch:</strong>
    <%= @page.branch_id %>
</p>

<p>
    <strong>Location:</strong>
    <%= @page.location_id %>
</p>

<h2>Links</h2>
<% @page.links.each do |link| %>
    <p>
        <strong>ID:</strong>
        <%= link.child__id %>
    </p>

    <p>
        <strong>Body:</strong>
        <%= link.body %>
    </p>
<% end %>

<h2>Add a link:</h2>
<%= form_for([@page, @page.links.build]) do |f| %>
    <p>
        <%= f.label :page_id %><br>
        <%= f.text_field :page_id %>
    </p>
    <p>
        <%= f.label :child_id %><br>
        <%= f.text_area :child_id %>
    </p>
    <p>
        <%= f.label :order %><br>
        <%= f.number_field :order %>
    </p>
    <p>
        <%= f.label :body %><br>
        <%= f.text_area :body %>
    </p>
    <p>
        <%= f.submit %>
    </p>
<% end %>


<%= link_to 'Edit', edit_page_path(@page) %> |
<%= link_to 'Back', pages_path %>

PagesController

class PagesController < ApplicationController
  before_action :set_page, only: [:show, :edit, :update, :destroy]

  # GET /pages
  # GET /pages.json
  def index
    @pages = Page.all
  end

  # GET /pages/1
  # GET /pages/1.json
  def show
    @page = Page.find(show_params)
  end

  # GET /pages/new
  def new
    @page = Page.new
  end

  # GET /pages/1/edit
  def edit
    @page = Page.find(params[:id])
  end

  # POST /pages
  # POST /pages.json
  def create
    @page = Page.new(page_params)

    respond_to do |format|
      if @page.save
        format.html { redirect_to @page, notice: 'Page was successfully created.' }
        format.json { render :show, status: :created, location: @page }
      else
        format.html { render :new }
        format.json { render json: @page.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /pages/1
  # PATCH/PUT /pages/1.json
  def update
    respond_to do |format|
      if @page.update(page_params)
        format.html { redirect_to @page, notice: 'Page was successfully updated.' }
        format.json { render :show, status: :ok, location: @page }
      else
        format.html { render :edit }
        format.json { render json: @page.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /pages/1
  # DELETE /pages/1.json
  def destroy
    @page = Page.find(params[:id])
    @page.destroy

    respond_to do |format|
     format.html { redirect_to pages_url, notice: 'Page was successfully destroyed.' }
     format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_page
      @page = Page.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def page_params
      params.require(:page).permit(:page_id, :body, :branch_id, :location_id)
    end

    def show_params
      params.require(:id)
    end
end

LinksController

class LinksController < ApplicationController
  before_action :set_link, only: [:show, :edit, :update, :destroy]

  # GET /links
  # GET /links.json
  def index
    @links = Link.all
  end

  # GET /links/1
  # GET /links/1.json
  def show
  end

  # GET /links/new
  def new
    @link = Link.new
  end

  # GET /links/1/edit
  def edit
  end

  # POST /links
  # POST /links.json
=begin
  def create
    @link = Link.new(link_params)

    respond_to do |format|
      if @link.save
        format.html { redirect_to @link, notice: 'Link was successfully created.' }
        format.json { render :show, status: :created, location: @link }
      else
        format.html { render :new }
        format.json { render json: @link.errors, status: :unprocessable_entity }
      end
    end
  end
=end
  def create
    @page = Page.find(params[:page_id])
    @link = @page.links.create(link_params)
    redirect_to page_path(@page)
  end


  # PATCH/PUT /links/1
  # PATCH/PUT /links/1.json
  def update
    respond_to do |format|
      if @link.update(link_params)
        format.html { redirect_to @link, notice: 'Link was successfully updated.' }
        format.json { render :show, status: :ok, location: @link }
      else
        format.html { render :edit }
        format.json { render json: @link.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /links/1
  # DELETE /links/1.json
  def destroy
    @link.destroy
    respond_to do |format|
      format.html { redirect_to links_url, notice: 'Link was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_link
      @link = Link.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def link_params
      params.require(:link).permit(:page_id, :child_id, :order, :body)
    end
end

Class Page has_many :links and class Link belongs_to :page Migrations are all up Links table exists in DB Not sure where else to look!

Thanks in advance

edit: I also get this error

ActiveRecord::StatementInvalid in Pages#show

Showing M:/ICT/Rails/Salacity/app/views/pages/show.html.erb where line

24 raised:

SQLite3::SQLException: no such column: links.page_id: SELECT "links".* FROM "links" WHERE "links"."page_id" = ?

Extracted source (around line #24):

Links

<% @page.links.each do |link| %>

ID: <%= link.child__id %>

Rails.root: M:/ICT/Rails/Salacity Application Trace | Framework Trace | Full Trace

app/views/pages/show.html.erb:24:in `_app_views_pages_show_html_erb___283878207_50250936'

db/migrate/create_links.rb

class CreateLinks < ActiveRecord::Migration
  def change
    create_table :links do |t|
      t.text :page_id
      t.text :child_id
      t.integer :order
      t.text :body
      t.references :page, foreign_key: true, index: true

      t.timestamps
    end
  end
end

and schema.rb

# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20150707195306) do

# Could not dump table "branches" because of following NoMethodError
#   undefined method `[]' for nil:NilClass

# Could not dump table "keywords" because of following NoMethodError
#   undefined method `[]' for nil:NilClass

# Could not dump table "links" because of following NoMethodError
#   undefined method `[]' for nil:NilClass

# Could not dump table "locations" because of following NoMethodError
#   undefined method `[]' for nil:NilClass

# Could not dump table "pages" because of following NoMethodError
#   undefined method `[]' for nil:NilClass

end

Upvotes: 3

Views: 2572

Answers (3)

OA Keefe
OA Keefe

Reputation: 31

Bit of a wild goose chase this one I think, 'show' is now displaying after renaming the column on the correct DB. Thanks for the assistance guys

Upvotes: 0

Richard Peck
Richard Peck

Reputation: 76784

I'll get into specifics in a second (you could improve your code a lot).


The error seems to be that your links method is non existent.

This could be for a number of reasons, I'll describe how to make sure it's fixed:

#app/models/page.rb
class Page < ActiveRecord::Base
   has_many :links
end

#app/models/link.rb
class Link < ActiveRecord::Base
   belongs_to :page
end

This is a standard has_many/belongs_to relationship:

enter image description here

As you can see from the diagram above, this means you have to have the appropriate foreign_keys set up in your links table:

#links
id | page_id | etc | etc | etc

You need to make sure you have page_id as either an integer or reference in your table:

$ rails g migration ChangePageIDLinks

#db/migrate/change_page_id_links__________.rb
class ChangePageIdLinks
    def change
        change_column :links, :page_id, :integer
    end
end

$ rake db:migrate

This will ensure your links database has the correct reference.

This means that each time you use @page.links, it will have a method to call. Whether you've set it up correctly is what we'll look at next:


Find

The main reason for noMethod errors in Rails (I was originally going to write this) is because you're calling a method on a non-existent variable.

Most developers are thrown by this at first (they think the "method" is the error). In Rails, when you don't have a variable that's populated, Ruby will assign it to the Nil:NilClass class.

This means the variable will actually be present, just not populated. Thus, any methods you call on it will not be existent either.

I believe you'll have this type of error too:

def show
   @page = Page.find(show_params) #-> Not necessary, although not wrong ^_^
end

Use this for your pages controller:

#app/controllers/pages_controller.rb
class PagesController < ApplicationController
  before_action :set_page, only: [:show, :edit, :update, :destroy]

  # GET /pages
  # GET /pages.json
  def index
    @pages = Page.all
  end

  # GET /pages/1
  # GET /pages/1.json
  def show
  end

  # GET /pages/new
  def new
    @page = Page.new
  end

  # GET /pages/1/edit
  def edit
  end

  # POST /pages
  # POST /pages.json
  def create
    @page = Page.new page_params

    respond_to do |format|
      if @page.save
        format.html { redirect_to @page, notice: 'Page was successfully created.' }
        format.json { render :show, status: :created, location: @page }
      else
        format.html { render :new }
        format.json { render json: @page.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /pages/1
  # PATCH/PUT /pages/1.json
  def update
    respond_to do |format|
      if @page.update(page_params)
        format.html { redirect_to @page, notice: 'Page was successfully updated.' }
        format.json { render :show, status: :ok, location: @page }
      else
        format.html { render :edit }
        format.json { render json: @page.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /pages/1
  # DELETE /pages/1.json
  def destroy
    @page.destroy

    respond_to do |format|
     format.html { redirect_to pages_url, notice: 'Page was successfully destroyed.' }
     format.json { head :no_content }
    end
  end

  private
    def set_page
      @page = Page.find params[:id]
    end

    def page_params
      params.require(:page).permit(:link_id, :body, :branch_id, :location_id)
    end
end

This should resolve the problem in your show view

--

Loop

Finally, another potential error I thought was causing exception:

<% @page.links.each do |link| %>

I've had this happen a number of times - Ruby/Rails will run the loop regardless of whether @page.links is populated. If it isn't, the error itself will not be from the loop, but the code you're trying to run inside it.

If your loop has the potentiality of having nil data, you need to make it conditional:

<% if @page.links.any? %>
   <% @page.links.each do .... %>
<% end %>

Upvotes: 0

Prashant4224
Prashant4224

Reputation: 1601

Change your migration file like this

db/migrate/timestamp_create_links.rb

class CreateLinks < ActiveRecord::Migration
  def change
    create_table :links do |t|
      t.integer :child_id
      t.integer :order
      t.text :body
      t.references :page, index: true

      t.timestamps
    end
  end
end

Upvotes: 0

Related Questions