Search code examples
ruby-on-railsrails-activestorage

Rails error message: Undefined method `persisted?' for nil:NilClass


I have just added this line of code: <%= image_tag tweet.user.avatar %> to the following view file

<%= turbo_frame_tag dom_id(tweet) do %>  
  <div class="tweet py-3 px-2">
    <% if(Time.zone.now - tweet.created_at) > 1.day %>
      <%= image_tag tweet.user.avatar %>
      <p><%= tweet.user.display_name %>@<%= tweet.user.username %> . <%= tweet.created_at.strftime("%b %-d") %></p>
    <% else %>
      <%= image_tag tweet.user.avatar %>
      <p><%= tweet.user.display_name %>@<%= tweet.user.username %> . <%= time_ago_in_words(tweet.created_at) %></p>
    <% end %>
    <p class="mb-0"><%= tweet.body %></p>
  </div>
<% end %>

The line of code is meant to display an image which is attached to the user through ActiveStorage. I check in the console, and the image is sucessfully loaded:

=> #<ActiveStorage::Attached::One:0x000000010813cf50
 @name="avatar",
 @record=
  #<User id: 3, email: "rajdeep@gmail", created_at: "2023-03-30 08:29:06.001223000 +0000", updated_at: "2023-03-30 21:35:17.296341000 +0000", username: "rj_deep", display_name: "Raja Deep">>

However, I am obtaining this message once it is time to display the loaded image:

ActionView::Template::Error (Can't resolve image into URL: undefined method `persisted?' for nil:NilClass
 | 
        named_route = if model.persisted?
                                ^^^^^^^^^^^):
4:     <%= image_tag tweet.user.avatar %>
5:       <p><%= tweet.user.display_name %>@<%= tweet.user.username %> . <%= tweet.created_at.strftime("%b %-d") %></p>
6:     <% else %>
7:       <%= image_tag tweet.user.avatar %>
8:       <p><%= tweet.user.display_name %>@<%= tweet.user.username %> . <%= time_ago_in_words(tweet.created_at) %></p>
9:     <% end %>
10:     <p class="mb-0"><%= tweet.body %></p>

Before I had added that line of code, all the rest of the info (the tweet, the display_name, the user_name and the tweet's creation-time) were successfully displayed.

I have no idea what this refers to. I used my code editor to look up named_route = if model.persisted? thinking that it might be a line of code in one of the project's files and that this might give me a better idea as to what this error message is signaling to me, but there is no such line in the project.

Don't know if this is related, but the project uses devise for user authentication and turbo-links for immediate page reload (which was not working, but was not interfering with display either).

The project is found here https://github.com/German-Cobian/Twitter-Rails, in case you need to get a better idea of the project's design.

Any help is greatly appreciated.


Solution

  • Start by using delegation to remove the Law of Demeter violations:

    class Tweet < ApplicationRecord
      belongs_to :user
      delegate :avatar, 
               :display_name, 
               :username, 
               to: :user
    end
    

    Then remove the duplication and complexity out of the view into a helper*:

    module TweetsHelper
      def tweet_tagline(t) 
        "#{t.display_name}@#{t.username} . #{tweet_timestamp(t.created_at)}"
      end
    
      def tweet_avatar(t, **options)
        image_tag t.avatar, **options if t.avatar
      end
    
      private
    
      def tweet_timestamp(t)
        if t.today?
          t.strftime("%b %-d")
        else
          time_ago_in_words(t)     
        end
      end
    end
    

    Now the view can just spit out markup in the simplest way possible:

    <%= turbo_frame_tag dom_id(tweet) do %>  
      <div class="tweet py-3 px-2">
        <%= tweet_avatar(tweet) %>
        <p><%= tweet_tagline(tweet) %></p>
        <p class="mb-0"><%= tweet.body %></p>
      </div>
    <% end %>