The Digital Academy
The Digital Academy

Reputation: 15

Ruby on Rails undefined method `destroy' for nil:NilClass

I'm having an issue while deleting user posts:

undefined method `destroy' for nil:NilClass

# routes.rb
Rails.application.routes.draw do
  get 'sessions/new'
  get 'users/new'
  get 'user/new'
  root to:'pages#home'
  get '/home', to:'pages#home'
  get '/help', to:'pages#help'
  get '/about', to:'pages#about'
  get '/contact',  to:'pages#contact'
  get '/signup', to: 'users#new'
  post '/signup', to: 'users#create'
  get '/login', to: 'sessions#new'
  post '/login', to: 'sessions#create'
  delete '/logout', to: 'sessions#destroy'
  post '/micro_posts', to: 'microposts#create'
  delete '/micro_posts', to: 'microposts#destroy'

  resources :users
  resources :account_activations, only: [:edit]
  resources :microposts, only: [:create, :destroy]
end


# microposts_controller.rb 

class MicropostsController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]
  before_action :correct_user, only: [:destroy]

  def create
    @micropost = current_user.micro_posts.build(req_params)

    if @micropost.save
        flash[:success] = "Post created"
        redirect_to root_url    
    else
        @feed_items = []
        render 'pages/home'

    end     
  end

  def destroy   
    @micropost.destroy
    flash[:success] = "Post deleted"
    redirect_to request.referrer || root_url
  end    

  private

  def req_params
    params.require(:micro_post).permit(:content)
  end

  def correct_user
    @micropost = current_user.micro_posts.find_by_id(params[:id])
    redirect_to root_url if @micropost.nil?
  end    
end

# _micro_post.html.erb file

<li id="micropost" ><%= micro_post.id %>
  <%= link_to gravatar_for(micro_post.user, size: 50), micro_post.user %>
  <span class="user" ><%= link_to micro_post.user.name, micro_post.user %></span><br/>
  <span class="content" ><%= micro_post.content %></span>
  <br/> 
  <span  class="timestamp">
  posted <%= time_ago_in_words(micro_post.created_at) %> ago    

  <% if current_user?(micro_post.user) %>
    <%= link_to '  | Delete', micro_posts_path, method: :delete , data: { confirm: "Are you sure" } %>
  <% end %>    
  </span>
  <br/>    
</li>

Now what I figured out is that my error is in microposts_controller.rb file in the reference method "correct_user", because it could not find the id of the micropost and returning nil. And so we can't call the destroy method on nil object. Can you please tell me why it is not finding the micropost id?

Started DELETE "/micro_posts" for 127.0.0.1 at 2019-02-21 21:43:17 +0500
   (0.7ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
  ↳ C:/Ruby25-x64/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
Processing by MicropostsController#destroy as HTML
  Parameters: {"authenticity_token"=>"aNeBnIy/JOrWz+sQdzU2w06faILW2dM+f8SPwmXVmJs1XfJ8OqbkgPVwkpoY/HBjh8YJhVPWZ4BC1Onxg0AtWw=="}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/helpers/sessions_helper.rb:16
  MicroPost Load (0.3ms)  SELECT  "micro_posts".* FROM "micro_posts" WHERE "micro_posts"."id" IS NULL LIMIT ?  [["LIMIT", 1]]
  ↳ app/controllers/microposts_controller.rb:36
Completed 500 Internal Server Error in 56ms (ActiveRecord: 1.9ms)



NoMethodError (undefined method `destroy' for nil:NilClass):

app/controllers/microposts_controller.rb:22:in `destroy

Upvotes: 1

Views: 1573

Answers (6)

chanko
chanko

Reputation: 269

I hadn't noticed that @zeitnot had answered before me with the same recommendation.


I think your issue is your delete link not referencing the actual object. You're not passing the object id, so params[:id] is always going to be nil.

Try this to see if it works:

<% if current_user?(micro_post.user) %>
  <%= link_to '  | Delete', micro_post_path(micro_post), method: :delete , data: { confirm: "Are you sure" } %>
<% end %>

Upvotes: 0

zeitnot
zeitnot

Reputation: 1314

Try this:

<%= link_to '  | Delete', micro_post_path(micro_post), method: :delete , data: { confirm: "Are you sure" } %>

The issue is that you are using a collection route. micro_posts_path is a collection route and you need to use a member route which takes resource id. So in this scenario we remove s from the route and it becomes micro_post_path that is a member route.

Upvotes: 0

Vasilisa
Vasilisa

Reputation: 4640

If you don't mind, I'll convert my comments to the answer, to not leave the question unanswered.

You have a naming issue. The model is MicroPost, but the controller is MicropostsController. That's why you're trying to create custom routes.

You need to rename controller to MicroPostsController and it's file to micro_posts_controller.rb. Remove from routes.rb these 2 lines:

post '/micro_posts', to: 'microposts#create'
delete '/micro_posts', to: 'microposts#destroy'

And change microposts to micro_posts (note the underscore) here:

resources :microposts, only: [:create, :destroy]

Use micro_post_path(micro_post) for delete link in the view

Upvotes: 1

mechnicov
mechnicov

Reputation: 15372

It looks the problem in your correct_user method. Try to remove it and use this way:

class MicropostsController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]
  before_action :set_micropost, only: :destroy

  ...

  def set_micropost
    @micropost ||= Micropost.find params[:id]
  end
end

And in your view:

<%= link_to '  | Delete', @micropost, method: :delete , data: { confirm: "Are you sure" } %>

Upvotes: 1

HermannHH
HermannHH

Reputation: 1772

I think you should be passing the id param to your delete route in your view.

Instead of this:

<%= link_to ' | Delete', micro_posts_path, method: :delete , data: { confirm: "Are you sure" } %>

Try this: <%= link_to ' | Delete', micro_posts_path(micro_post.id), method: :delete , data: { confirm: "Are you sure" } %>

Upvotes: 0

Antoine Quellier
Antoine Quellier

Reputation: 152

Yep you have to set the value of @micropost beofre trying to destroy it. Usually this is something you want to do with the params hash so you could have sth like @micropost = Micropost.find(params[:id]) at the beginning of your destroy method. This is a line you might want to use in other methods in you controller so you can refactor it as follow

class MicropostsController < ApplicationController
  before_action :set_micropost, only: [:destroy]

 ...

  private

  def set_micropost
    @micropost = Micropost.find(params[:id])
  end
end

Hope it helps :)

Upvotes: 0

Related Questions