Search code examples
ruby-on-railspaperclipnomethoderror

Rails + Paperclip - NoMethodError in Projects#index - undefined method `image' for nil:NilClass


I have a rails app with a Project model and a nested Pictures model. Im using the Paperclip gem to upload images to the pictures model, and nested_form gem to nest the Picture model within the projects. Everything was working perfectly, the images were showing and the code seemed to be working fine, until suddenly after working on another part of the app, I started getting the error mentioned in the question title. Specifically, this line in my index page seems to be the issue: <%= link_to image_tag(project.pictures.first.image.url(:thumb)), project %> I cant seem to figure out the problem as it was working fine before. I even reverted back to a previous commit when it was working, and im still getting the same error. Im totally stumped. Any help would be highly appreciated!

Index:

<div id="pictures">
  <% @projects.each do |project| %>

  <div class="col-md-4">

    <div class="box panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title"><%= project.title %></h3>
      </div>
      <div class="image">
         <%= link_to image_tag(project.pictures.first.image.url(:thumb)), project %>
      </div>
      <div class="panel-body">

        <p>
          <strong>Progress:</strong>
          <%= progress_bar 0.6, label: true, alternative: 'info', striped: true %>
        </p>


        <p>
          <strong>Status:</strong>
          <%= project.status %>
        </p>


        <p>
          <strong>Phase:</strong>
          <%= project.phase %>
        </p>


        <%= link_to 'Show', project %> |
        <%= link_to 'Edit', edit_project_path(project) %> |
        <%= link_to 'Destroy', project, method: :delete, data: { confirm: 'Are you sure?' } %>
      </div>
    </div>
      </div>


  <% end %>
</div>

Projects model:

class Project < ActiveRecord::Base

  has_many :pictures, :dependent => :destroy
  has_many :teams, :dependent => :destroy

    accepts_nested_attributes_for :pictures, :reject_if => lambda { |a| a[:image].blank? }, allow_destroy: true
    accepts_nested_attributes_for :teams, :reject_if => lambda { |a| a[:member].blank? }, allow_destroy: true

end

Pictures model:

class Picture < ActiveRecord::Base

 belongs_to :project

 has_attached_file :image,
 path: ":rails_root/public/system/:attachment/:id/:style/:filename",
 url: "/system/:attachment/:id/:style/:filename",
:styles => { :medium => "900x900>", :thumb => "300x300>" }

 validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/

end

Projects controller:

class ProjectsController < ApplicationController
  before_action :set_project, only: [:show, :edit, :update, :destroy]

  respond_to :html

  def index
    @projects = Project.all
    respond_with(@projects)
  end

  def show
    respond_with(@project)
  end

  def new
    @project = Project.new
    @project.pictures.build
    @project.teams.build
    respond_with(@project)
  end


  def edit
  end

  def create

    @project = Project.new(project_params)
    if @project.save
    flash[:notice] = "Successfully created project."
    redirect_to @project
    else
    render :action => 'new'
    end

  end

  def update
    @project.update(project_params)
    respond_with(@project)
  end

  def destroy
    @project.destroy
    respond_with(@project)
  end

  private
    def set_project
      @project = Project.find(params[:id])
    end

    def project_params
      params.require(:project).permit(:id, :title, :description, :status, :phase, :location, :contractor, :designer, :area, :budget, :project_start, :construction_period, :expected_date, :picture_id, :image, pictures_attributes: [:id, :image, :_destroy], teams_attributes: [:project_id, :user_id, :id, :member, :role, :_destroy])
    end
end

Pictures controller:

class PicturesController < ApplicationController
  before_action :set_picture, only: [:show, :edit, :update, :destroy]

  respond_to :html

  def index
    @pictures = Picture.all
    respond_with(@pictures)
  end

  def show
    respond_with(@picture)
  end

  def new
    @picture = Picture.new
    respond_with(@picture)
  end

  def edit
  end

  def create
    @picture = Picture.new(picture_params)
    @picture.save
    respond_with(@picture)
  end

  def update
    @picture.update(picture_params)
    respond_with(@picture)
  end

  def destroy
    @picture.destroy
    respond_with(@picture)
  end

  private
    def set_picture
      @picture = Picture.find(params[:id])
    end

    def picture_params
      params.require(:picture).permit(:image, :id, :project_id)
    end
end

Solution

  • Most certainly, one of your projects has no pictures.

    When you call project.pictures.first.image.url(:thumb) for each of your projects, you are getting all of its pictures, then the first one of those, and then the image for that first picture.

    If your project has no pictures, then project.pictures is an empty set. When you call .first on an empty set, the result is nil. nil is an object, of class NilClass, and it has no method named image. Ensure that each one of your projects has at least one image and you will not see the error. Alternatively you can create an if statement around your link_to, so that you only evaluate that line if !project.pictures.empty?.