Question

I am working on a mock-up of reddit where I submit url & title in a form and then the database should get populated and we should be taken to a show page to see our link and title that we submitted.

My questions is why I have to include this in my controller.

  def show  
    @link = Link.find(params[:id])
  end

If I delete the @link = Link.find(params[:id]) my show doesnt work.

I get the following error:

NoMethodError in Links#show

On this line

<%= @link.title %>

In addition to this I already have this private method:

private
  def set_link
    @link = Link.find(params[:id])
  end
  def link_params
    params.require(:link).permit(:title, :url)
  end

I was comparing to another project where I generated a scaffold for something similar and I only had the private method and no need for @link = Link.find(params[:id]) in my show action.

Here is my full controller code:

class LinksController < ApplicationController

  def index
    @link = Link.all
  end

  def new
    @link = Link.new
  end

  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 action: 'show', status: :created, location: @link }
      else
        format.html { render action: 'new' }
        format.json { render json: @link.errors, status: :unprocessable_entity }
      end
    end  
  end

  def show  
    @link = Link.find(params[:id])
  end


private
  def set_link
    @link = Link.find(params[:id])
  end
  def link_params
    params.require(:link).permit(:title, :url)
  end


end

And here is my full controller for the generated scaffold:

class HighScoresController < ApplicationController
  before_action :set_high_score, only: [:show, :edit, :update, :destroy]

  # GET /high_scores
  # GET /high_scores.json
  def index
    @high_scores = HighScore.all
  end

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

  # GET /high_scores/new
  def new
    @high_score = HighScore.new
  end

  # GET /high_scores/1/edit
  def edit
  end

  # POST /high_scores
  # POST /high_scores.json
  def create
    @high_score = HighScore.new(high_score_params)

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

  # PATCH/PUT /high_scores/1
  # PATCH/PUT /high_scores/1.json
  def update
    respond_to do |format|
      if @high_score.update(high_score_params)
        format.html { redirect_to @high_score, notice: 'High score was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @high_score.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /high_scores/1
  # DELETE /high_scores/1.json
  def destroy
    @high_score.destroy
    respond_to do |format|
      format.html { redirect_to high_scores_url }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def high_score_params
      params.require(:high_score).permit(:game, :score)
    end
end

Why does the generated scaffold work without the @link = Link.find(params[:id]) in my show action while my own project won't?

Was it helpful?

Solution

You're missing the first line within the controller class of the generated scaffold:

class HighScoresController < ApplicationController
  before_action :set_high_score, only: [:show, :edit, :update, :destroy]

You need to add a before_action to your controller in order to make your set_link function run:

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

Before the show action, call the set_link function.

OTHER TIPS

You need to set the value of instance variable @link as

@link = Link.find(params[:id])

in show action because in show View of link, you would be displaying details of a particular Link object(@link) like

<%= @link.title%>

In order to do this the @link variable should be set as a Link object else you get an error.

As you already have a set_link method defined in the controller, you can reuse it across various actions using before_action.

class LinksController < ApplicationController

     before_action :set_link, only: [:show, :edit, :update, :destroy]
     ....
     ....
 end

and remove the calls to @link = Link.find(params[:id]) from show, edit, update and destroy actions( if any).

Setting up before_action with the only option would result in set_link method being called before the listed action is executed.

For eg: If you visit show url for a link then, firstly set_link would be called and after that show action(as show action is in the :only list)

Notice that the same thing (before_action :set_high_score) is setup for HighScoresController which is why redundant calls to @highscore = HighScore.find(params[:id] are avoided.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top