Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

Matthew Pierce
Matthew Pierce
15,779 Points

undefined method `decorate' for nil:NilClass

I'm on the "friendability" section of Building Social Features in Ruby on Rails. Things were going okay, but visiting the user_friendships #edit page now returns:

undefined method `decorate' for nil:NilClass

My tests return a similar error:

Finished in 1.735499s, 42.0628 runs/s, 57.6203 assertions/s.

  1) Error:
UserFriendshipsControllerTest#test_: #edit when logged in should assign to user_friendship. :
NoMethodError: undefined method `id' for nil:NilClass
    app/controllers/user_friendships_controller.rb:72:in `edit'
    test/controllers/user_friendships_controller_test.rb:206:in `block (3 levels) in <class:UserFriendshipsControllerTest>'


  2) Error:
UserFriendshipsControllerTest#test_: #edit when logged in should get new and return success. :
NoMethodError: undefined method `id' for nil:NilClass
    app/controllers/user_friendships_controller.rb:72:in `edit'
    test/controllers/user_friendships_controller_test.rb:206:in `block (3 levels) in <class:UserFriendshipsControllerTest>'

73 runs, 100 assertions, 0 failures, 2 errors, 0 skips

I have already read through a similar thread: https://teamtreehouse.com/community/draper-decorator-causing-problems but the solutions there haven't solved it.

user_friendships_controller.rb
class UserFriendshipsController < ApplicationController

  before_filter :authenticate_user!
  respond_to :html, :json


  def index
    @user_friendships = current_user.user_friendships.all
    respond_with @user_friendships
  end

  def accept
    @user_friendship = current_user.user_friendships.find(params[:id])
    if @user_friendship.accept!
      flash[:success] = "You are now friends with #{@user_friendship.friend.profile_name}"
    else
      flash[:error] = "Oops! That friendship could not be accepted."
    end
    redirect_to user_friendships_path
  end

  def block
    @user_friendship = current_user.user_friendships.find(params[:id])
    if @user_friendship.block!
      flash[:success] = "You have blocked #{@user_friendship.friend.profile_name}"
    else
      flash[:erorr] = "That friendship could not be blocked."
    end

    redirect_to user_friendship_path
  end

  def new
    if params[:friend_id]
      @friend = User.where(profile_name: params[:friend_id]).first
      raise ActiveRecord::RecordNotFound if @friend.nil?
      @user_friendship = current_user.user_friendships.new(friend: @friend)
    else
      flash[:error] = "Friend required"
    end
    rescue ActiveRecord::RecordNotFound
    render file: 'public/404', status: :not_found
  end

  def create
    if params[:user_friendship] && params[:user_friendship].has_key?(:friend_id)
      @friend = User.where(profile_name: params[:user_friendship][:friend_id]).first
      @user_friendship = UserFriendship.request(current_user, @friend)
      respond_to do |format|
        if @user_friendship.new_record?
          format.html do
            flash[:error] = "There was a problem creating that friend request."
            redirect_to profile_path(@friend)
          end
          format.json { render json: @user_friendship.to_json, status: :precondition_failed }
        else
          format.html do
            flash[:success] = "Friendship requested!"
            redirect_to profile_path(@friend)
          end
          format.json { render json: @user_friendship.to_json }
        end
      end  
    else
      flash[:error] = "Friend required"
      redirect_to root_path
    end
  end

  def edit
   @friend = User.where(profile_name: params[:id]).first
   @user_friendship = current_user.user_friendships.where(friend_id: @friend.id).first.decorate
  end

  def destroy
    @user_friendship = current_user.user_friendships.find(params[:id])
    if @user_friendship.destroy
      flash[:success] = "Friendship destroyed"
    end
    redirect_to user_friendships_path
  end
end

user_friendships/index.html.erb:

<div class="page-header">
  <h1>Friends</h1>
</div>

<% @user_friendships.each do |friendship| %>
  <% friend = friendship.friend %>
  <div id="<%= dom_id(friendship) %>" class="friend row">
    <div class="span1">
      <%= link_to "#{friend.profile_name}", profile_path(friend) %>
    </div>   
    <div class="span7">
        <% if friendship.pending? %>

          <em>Friendship is pending.</em> <%= link_to "Delete request", edit_user_friendship_path(friendship.friend.profile_name) %>.
        <% end %>
        <% if friendship.requested? %>

          <em>Friendship requested.</em> <%= link_to "Accept friendship", edit_user_friendship_path(friendship.friend.profile_name) %>.
        <% end %>
        <% if friendship.accepted? %>

          <em>Friendship started.</em> <%= link_to "Update friendship", edit_user_friendship_path(friendship.friend.profile_name) %>.
        <% end %>
    </div>
  </div>
<% end %>

user_friendships/edit.html.erb:

<div class="page-header">
  <h1>Viewing Friendships</h1>
</div>

<h3><%= @user_friendship.sub_message %></h3>

<div class="form-actions">
  <% if @user_friendship.requested? %>
    <%= form_for @user_friendship, url: accept_user_friendship_path(@user_friendship), method: :put do |form| %>
      <%= submit_tag "Accept Friendship", class: 'btn btn-primary' %>
    <% end %>
  <% end %>

  <%= form_for @user_friendship, url: block_user_friendship_path(@user_friendship), method: :put do |form| %>
    <%= submit_tag "Block Friendship", class: 'btn' %>
  <% end %>

  <%= form_for @user_friendship, url: user_friendship_path(@user_friendship), method: :delete do |form| %>
    <%= submit_tag "Delete Friendship", class: 'btn btn-danger' %>
  <% end %>
</div>
user_friendships_decorator.rb
class UserFriendshipDecorator < Draper::Decorator
  delegate_all

  decorates :user_friendship

  def friendship_state
    model.state.titleize
  end

  def sub_message
    case model.state
    when 'pending'
      "Do you really want to be friends with #{model.friend.profile_name}?"
    when 'accepted'
      "You are friends with #{model.friend.profile_name}"
    end
  end

  # Define presentation-specific methods here. Helpers are accessed through
  # `helpers` (aka `h`). You can override attributes, for example:
  #
  #   def created_at
  #     helpers.content_tag :span, class: 'time' do
  #       object.created_at.strftime("%a %m/%d/%y")
  #     end
  #   end

end
user_friendships_controller_test.rb
require 'test_helper'

class UserFriendshipsControllerTest < ActionController::TestCase
  context "#index" do
    context "when not logged in" do
      should "redirect to login page" do
        get :index
        assert_response :redirect
      end
    end

    context "when logged in" do
      setup do
        @friendship1 = create(:pending_user_friendship, user: users(:matthew), friend: create(:user, profile_name: 'Pending'))
        @friendship2 = create(:accepted_user_friendship, user: users(:matthew), friend: create(:user, profile_name: 'Active'))

        sign_in users(:matthew)
        get :index
      end

      should "get the index page without error" do
        assert_response :success
      end

      should "assign user_friendships" do
        assert assigns(:user_friendships)
      end

      should "display friend's names" do
        assert_match /Pending/, response.body
        assert_match /Active/, response.body
      end

      should "display pending information on a pending friendship" do
        assert_select "#user_friendship_#{@friendship1.id}" do
          assert_select "em", "Friendship is pending."
        end
      end
    end
  end

  context "#new" do
        context "when not logged in" do
            should "redirect to login page" do
                get :new
                assert_response :redirect
            end
        end

        context "when logged in" do
            setup do
                sign_in users(:matthew) 
            end

            should "get new and return success" do
                get :new
                assert_response :success
            end

            should "set a flash error if the friend_id params is missing" do
                get :new, {}
                assert_equal "Friend required", flash[:error]
            end

            should "display the friend's name" do
                get :new, friend_id: users(:jim)
                assert_match /#{users(:jim).profile_name}/, response.body
            end

            should "asisgn a new user friendship to the correct friend" do
                get :new, friend_id: users(:jim)
                assert_equal users(:jim), assigns(:user_friendship).friend
            end

            should "asisgn a new user friendship to the currently logged in user" do
                get :new, friend_id: users(:jim)
                assert_equal users(:matthew), assigns(:user_friendship).user
            end

            should "return a 404 status if no friend is found" do
                get :new, friend_id: 'invalid'
                assert_response :not_found
            end

            should "ask if you really want to friend the user" do
                get :new, friend_id: users(:jim)
                assert_match /Do you really want to friend #{users(:jim).profile_name}?/, response.body
            end

        end
    end

  context "#create" do
    context "when not logged in" do
      should "redirect to the login page" do
        get :new
        assert_response :redirect
        assert_redirected_to new_user_session_path
      end
    end

    context "when logged in" do
      setup do
        sign_in users(:matthew)
      end

      context "with no friend_id" do
        setup do
          post :create
        end

        should "set the flash error message" do
          assert !flash[:error].empty?
        end

        should "redirect to the site root" do 
          assert_redirected_to root_path
        end
      end

      context "successfully" do
        should "create two user friendship objects" do
          assert_difference 'UserFriendship.count', 2 do
            post :create, user_friendship: { friend_id: users(:mike).profile_name }
          end
        end
      end

      context "with a valid friend_id" do
        setup do
          post :create, user_friendship: { friend_id: users(:mike)}
        end

        should "assign a friend object" do
          assert assigns(:friend)
          assert_equal users(:mike), assigns(:friend)
        end

        should "assign a user_friendship object" do
          assert assigns(:user_friendship)
          assert_equal users(:matthew), assigns(:user_friendship).user
          assert_equal users(:mike), assigns(:user_friendship).friend
        end

        should "create a friendship" do
          assert users(:matthew).pending_friends.include?(users(:mike))
        end

        should "redirect to the profile page of the friend" do
          assert_response :redirect
          assert_redirected_to profile_path(users(:mike))
        end

        should "set the flash success message" do
          assert flash[:success]
          assert_equal "Friendship requested!",flash[:success]
        end
      end
    end
  end

  context "#accept" do
    context "when not logged in" do
      should "redirect to the login page" do
        put :accept, id: 1
        assert_response :redirect
        assert_redirected_to new_user_session_path
      end
    end

    context "when logged in" do
      setup do
        @user_friendship = create(:pending_user_friendship, user: users(:matthew))
        sign_in users(:matthew)
        put :accept, id: @user_friendship
        @user_friendship.reload
      end

      should "assign a user_friendship" do
        assert assigns(:user_friendship)
        assert_equal @user_friendship, assigns(:user_friendship)
      end

      should "update the state to accepted" do
        assert_equal 'accepted', @user_friendship.state
      end

      should "have a flash success message" do
        assert_equal "You are now friends with #{@user_friendship.friend.profile_name}", flash[:success]
      end
    end
  end

  context "#edit" do
    context "when not logged in" do
      should "redirect to login page" do
        get :edit, id: 1
        assert_response :redirect
      end
    end

    context "when logged in" do
      setup do
        @user_friendship = create(:pending_user_friendship, user: users(:matthew))
        sign_in users(:matthew) 
        get :edit, id: @user_friendship.friend.id
      end

      should "get new and return success" do
        assert_response :success
      end

      should "assign to user_friendship" do
        assert assigns(:user_friendship)
      end
    end
  end

  context "#destroy" do
    context "when not logged in" do
      should "redirect to the login page" do
        delete :destroy, id: 1
        assert_response :redirect
        assert_redirected_to new_user_session_path
      end
    end

    context "when logged in" do
      setup do
        @friend = create(:user)
        @user_friendship = create(:accepted_user_friendship, friend: @friend, user: users(:matthew))
        create(:accepted_user_friendship, friend: users(:matthew), user: @friend)

        sign_in users(:matthew)
      end

      should "delete user friendships" do
        assert_difference 'UserFriendship.count', -2 do
          delete :destroy, id: @user_friendship
        end
      end

      should "set the flash" do
        delete :destroy, id: @user_friendship
        assert_equal "Friendship destroyed", flash[:success]
      end
    end
  end

  context "#block" do
    context "when not logged in" do
      should "redirect to the login page" do
        put :block, id: 1
        assert_response :redirect
        assert_redirected_to new_user_session_path
      end
    end

    context "when logged in" do
      setup do
        @user_friendship = create(:pending_user_friendship, user: users(:matthew))
        sign_in users(:matthew)
        put :block, id: @user_friendship
        @user_friendship.reload
      end

      should "assign a user friendship" do
        assert assigns(:user_friendship)
        assert_equal @user_friendship, assigns(:user_friendship)
      end

      should "update the user friendship state to blocked" do
        assert_equal 'blocked', @user_friendship.state
      end
    end
  end

end

Any help is greatly appreciated! I've been struggling with this for a while.

1 Answer

Matthew Pierce
Matthew Pierce
15,779 Points

It was really simple. I was just missing curly braces in where({})

user_friendships_controller.rb:

def edit
   @friend = User.where(profile_name: params[:id]).first
   @user_friendship = current_user.user_friendships.where({friend_id: @friend}).first.decorate
  end