Search code examples
ruby-on-railsinstance-variablespartials

Rails 5.2: Variables not accessible in header partial


Beginner here. Please be patient.

I'm attempting to port a PHP site to Rails and so far doing quite well, but can't figure out this one...

# [controllers/pages_controller.rb]
class PagesController < ApplicationController
  before_action :authenticate, except: [:show]
  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
    # This works:
    @page_title = @page.title

    # Do I really need to do this for each var?
    @page_description = @page.description
  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 added.' }
        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 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 deleted.' }
      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(:title, :type, :flag, :caption, :description, :share_image, :complete, :state, :offline_message, :active_from, :active_until, :mailing_list, :feed_url, :visible)
    end
end

If I write <%= @page.description %> into the partial directly, I get an error. If I write <%= @page_description %> instead (with underscore), no error. The error reads:

NoMethodError at /login
undefined method `description' for nil:NilClass

I want to use it like this in my site header:

[views/layouts/application.rb]
...
<meta name="keywords" content="<%= @page.keywords %>">
...

The information in this variable and many others is stored in the pages table because every page has different owners, facebook feeds, description, keywords, copyright date, and so on. I want all that info to show up in the meta tags for each page.

So as far as I understand it, I go to pages/1 in the browser and the controller action show is accessed, which works even with no code inside; The variables from the table row with id 1 are all showing on the page itself but not in the header or footer partials.


Solution

  • No, you don't have to use @page_description.

    The error indicates that you don't have a @page object instantiated. Make sure that you have one. You can inspect the the @page object in the view like this:

    <%= @page.inspect %>
    

    If it says nil, then you need to set up this instance variable in your controller. For example:

    @page = Page.find(params[:id]) 
    

    Edit:

    In this case it seems like you have a partial that is used in every layout that references the @page object. Now in the login page, the sessions / users controller does not provide this object.

    Either you only use this partial in pages which are created by the page controller or you wrap your code with something like this:

    <% if @page.present? %>
      <%= @page.description %>
    <% end %>
    

    To find out what controller you are in, in the partial, you can add this line:

    <%= controller.class %>