1

This is a follow up question on Rails 4 x paper_trail: filter versions by item_id with nested resources.

—————

CONTEXT FROM PREVIOUS QUESTION

In my Rails 4 app, I have the following models:

class User < ActiveRecord::Base
  has_many :administrations, dependent: :destroy
  has_many :calendars, through: :administrations
end

class Administration < ActiveRecord::Base
  belongs_to :user
  belongs_to :calendar
end

class Calendar < ActiveRecord::Base
  has_many :administrations, dependent: :destroy
  has_many :users, through: :administrations
end

class Post < ActiveRecord::Base
    belongs_to :calendar
end

I installed the paper_trail gem to track changes on my post model as explained in the documentation and it works like a charm.

Versions are displayed in the Calendars#Index view, which serves as a dashboard to the user.

—————

Based on the answer to my previous question, I now have the following code in my CalendarsController:

def index
    @user = current_user
    @calendars = @user.calendars.all
    @comments = @user.calendar_comments.order("created_at DESC").limit(20)
    @versions = PaperTrail::Version.where(item_id: Post.where(calendar_id: @calendars.ids)).order('id DESC').limit(10)
end

Problem is now: when a user destroys a post, all the versions associated with this post disappear.

What I would like instead, is to keep track of a post even after it is destroyed, including the version mentioning that it was destroyed.

How can I achieve that?

Community
  • 1
  • 1
Thibaud Clement
  • 6,607
  • 10
  • 50
  • 103
  • may be try to define the `has_paper_trais` for destroy action as well? I am not sure how it works, but try `has_paper_trail :destroy` – Andrey Deineko Nov 15 '15 at 17:41
  • I already have `has_paper_trail :on => [:update, :destroy]` in my `post` model. The problem is not with `paper_trail`, it is with the query I guess, since I could track all versions, including when a post was destroyed, when I was pulling `@versions = PaperTrail::Version.order('id DESC').limit(10)`. – Thibaud Clement Nov 15 '15 at 17:45

2 Answers2

3

The simplest way to achieve this is to use Paper Trail's metadata feature to store the appropriate users' ids on each post's version record. In other words, denormalise your data just a little to make querying easier.

class Post < ActiveRecord::Base
  belongs_to :calendar
  has_paper_trail meta: {user_id: :user_ids}  # remember to add a user_id column to the versions table

  def user_ids
    calendar.user_ids  # or whatever you need
  end
end

Then you can do this in your CalendarsController:

@versions = PaperTrail::Version.where(user_id: current_user.id)
                               .order('id DESC')
                               .limit(10)
jayp
  • 192
  • 2
  • 13
Andy Stewart
  • 5,013
  • 2
  • 28
  • 38
1

Instead of actually destroying a post, you could just mark it as destroyed and remove it from the scope. This will retain it's version history and allow you to restore a 'deleted' post if a mistake was made.

rails g migration AddDeletedToPosts deleted:boolean

If there's a point where you no longer need a post and it's version history, say after a period of time, you can create a garbage collector for permanent deletion or migration to a separate archive.

Clarification

A version only belongs to the object it is tracking - in this case, a post. As such, when you destroy a post, you are effectively orphaning all of it’s versions. Yes, they do exist in the database and are present in a query of all versions, but you can no longer scope them because they aren’t related to anything. This is why I suggested virtually destroying posts until you no longer care about it’s version history. Does that make any sense?

Brent Eicher
  • 1,050
  • 9
  • 14
  • Thanks for your answer and your suggestion. This is a clever workaround but not exactly what I am looking for. My original query — `@versions = PaperTrail::Version.order('id DESC').limit(10)` — was allowing me to keep track of versions for a post after it is destroyed, but was not restricted to posts belonging to the calendars of the user. The new query — `@versions = PaperTrail::Version.where(item_id: Post.where(calendar_id: @calendars.ids)).order('id DESC').limit(10)` — does the opposite. I would like to be able to do both (restrain scope AND keep track of destroyed posts). Is that possible? – Thibaud Clement Nov 15 '15 at 17:28
  • See my clarified answer – Brent Eicher Nov 15 '15 at 18:30
  • Thanks. I understand your reasoning and it makes a lot of sense in the context of this question. But virtually destroying the posts will force us to change many parts of the app. Couldn't we improve on the query to make it also take into account versions of posts that have been deleted? – Thibaud Clement Nov 15 '15 at 19:27
  • 1
    No. You can't improve a query to achieve what is logically impossible. – Brent Eicher Nov 15 '15 at 20:08
  • Hey @Brent Eicher. I just stumbled upon this post and thought you could be interested: http://leonid.shevtsov.me/en/how-to-use-papertrail-for-soft-deletetion I don't yet if I am going to be able to use a similar query for my case, but I am working on it. – Thibaud Clement Nov 20 '15 at 05:15
  • Thanks for following up! I'll check out this article. – Brent Eicher Nov 20 '15 at 17:01