0

I just installed the paper_trail gem in my Rails 4 app.

So, now I have this in my schema.rb:

create_table "versions", force: :cascade do |t|
  t.string   "item_type",  null: false
  t.integer  "item_id",    null: false
  t.string   "event",      null: false
  t.string   "whodunnit"
  t.text     "object"
  t.datetime "created_at"
end

I added paper_trail to my Post model:

class Post < ActiveRecord::Base
  has_paper_trail
end

A post has_many comments and belongs_to a calendar.

I am trying to display a history of recently modified posts in my Calendars#Index view.

I used the official documentation and this tutorial for inspiration.

So, in calendars_controller.rb, I have:

def index
  @user = current_user
  @calendars = @user.calendars.all
  @comments = @user.calendar_comments.where.not(user_id: @user.id).order "created_at DESC"
  @versions = PaperTrail::Version.order('id desc').limit(20)
end

And in my Calendar index.html.erb view, I have:

<h3 class="main_title">Updates</h3>

    <div id="my_changes">

      <% if @versions.any? %>

        <table id="my_change_table">

          <tr>
            <th><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span> CALENDAR </th>
            <th><span class="glyphicon glyphicon-list" aria-hidden="true"></span> POST </th>
            <th><span class="glyphicon glyphicon-user" aria-hidden="true"></span> AUTHOR </th>
            <th><span class="glyphicon glyphicon-edit" aria-hidden="true"></span> CHANGE </th>
          </tr>

          <% @versions.each do |version| %>

          <% post = Post.find_by_id(version.item_id) %>

            <tr>
              <td><%= Calendar.find_by_id(post.calendar_id).name %></td>
              <td><%= post.subject %></td>
              <td><%= User.find_by_id(version.whodunnit).first_name %></td>
              <td><%= version.event.humanize + "d" %></td>
            </tr>

          <% end %>

        </table>

      <% else %>

        <p>There was no change to your posts yet.</p>

      <% end %>

    </div>

This works actually pretty well when a user updates a post.

However, as soon as a user destroys a post, I get the following error:

NoMethodError in Calendars#index
undefined method `item_id' for #<Post:0x007fff22e28c58>
<% post = Post.find_by_id(version.item_id) %>

In fact, this makes sense, since we destroyed the post, it does not exist any more, so we can retrieve its id.

But I thought this was precisely paper_trail's job.

So, I must be missing something.

I tried to use the version.reify and the version.previous methods, but still ran into the same issue.

Any idea how to make this work?

Thibaud Clement
  • 6,607
  • 10
  • 50
  • 103
  • 1
    might there be a misalignment between the code in the error to the one you posted? `<% post = Post.find_by_id(version.reify.item_id) %>` vs.`<% post = Post.find_by_id(version.item_id) %>` ? – davidrac Oct 26 '15 at 19:12
  • Yes, you are absolutely correct. I was trying different things while I was asking the questions, which resulted in a mix up, sorry about that. I just updated the question with the code as it should be. Thanks a lot for your help. – Thibaud Clement Oct 26 '15 at 19:15

1 Answers1

1

The problem is that after you destroy an object, reify will rerun a new instance of the object, which is unsaved and has no id.

Since it is deleted anyway you should expect Post.find_by_id(version.item_id) to not find it anyway.

Edit

You should be able to get the properties of the original object from the version (see here)

So you can change your code to something like (assuming all of the versions in your system are calendars) and I think it should work:

      <% @versions.each do |version| %>

      <% post = version.reify %>

        <tr>
          <td><%= Calendar.find_by_id(post.calendar_id).name %></td>
          <td><%= post.subject %></td>
          <td><%= User.find_by_id(version.whodunnit).first_name %></td>
          <td><%= version.event.humanize + "d" %></td>
        </tr>

      <% end %>
davidrac
  • 10,723
  • 3
  • 39
  • 71
  • Ok, I understand what you say and it makes sense. What I am trying to achieve is to display a list of the changes that were applied — eg: saved — to each post object. The problem, currently, is that I can't seem to retrieve the id of an object after it was destroyed. And that is confusing me, since I thought that was precisely what paper_trail was for. May I ask you if what I want to achieve is actually possible? – Thibaud Clement Oct 26 '15 at 19:17
  • What you can do if all you want is to display the verstion in the UI is to call reify which will give you an unsaved object that corresponds to that version and just display it in the UI. No need to look for the object in the database – davidrac Oct 26 '15 at 19:20
  • Thanks for updating your answer. Should I use a `before_filter` on the `destroy` action to get the `id` of the `post` then? Isn't there any way to achieve this directly with `paper_trail`? – Thibaud Clement Oct 26 '15 at 19:20
  • Hum, not sure I understand your comment. Just to be clearer that my original question may have been, what I want is to display in the Calendars#Index view is something like "David destroyed post #12 from calendar #4". Does that make sense? – Thibaud Clement Oct 26 '15 at 19:21
  • If all you want is to display the content of the version in the UI you can navigate the versions and query the attribute you want to display directly on the version https://github.com/airblade/paper_trail – davidrac Oct 26 '15 at 19:26
  • Well, I am not sure it is the content of the version. Because I need the subject of the post that was destroyed, and to get that, I need to do something like `Post.find_by_id(version.item_id).subject`. Which is what is causing my issue I guess. Would you mind switching to the chat, because I feel like I am getting something wrong about how paperclip works? – Thibaud Clement Oct 26 '15 at 19:35
  • see my edit. I think this should work (I'm not sure about associations, so post.calendar_id is still in question) – davidrac Oct 26 '15 at 19:39
  • Yes, it does! Thanks a lot, that's exactly what I wanted. – Thibaud Clement Oct 26 '15 at 19:43