#245 Tracking Changes on Action Text

Summary

Without using any gems, we look at tracking changes within Action Text. It seems like a simple thing, but challenges arise through several layers of abstraction due to the flexibility that Action Text provides.
action text audit rails 9:49

Resources

Summary

# Terminal
rails action_text:install
rails g scaffold articles title
rails g model version user:belongs_to item:references{polymorphic}
rails db:migrate

# models/article.rb
class Article < ApplicationRecord
  has_rich_text :content
  has_many :versions, as: :item, dependent: :destroy
end

# models/version.rb
class Version < ApplicationRecord
  belongs_to :user
  belongs_to :item, polymorphic: true
  has_rich_text :content
end

# config/initializers/action_text_version.rb
module CurrentScope
  mattr_accessor :user
end

ActiveSupport.on_load(:action_text_rich_text) do
  ActionText::RichText.class_eval do
    before_save :record_change

    private

    def record_change
      return unless self.body_changed?
      return if name == 'content' && record_type == 'Version'

      Version.create(item: record, content: self.body_was, user: CurrentScope.user)
    end
  end
end

# application_controller.rb
class ApplicationController < ActionController::Base
  around_action :user_for_version

  private

  def user_for_version
    CurrentScope.user = current_user if user_signed_in?
    yield
  end
end

# articles_controller.rb
    def article_params
      params.require(:article).permit(:title, :content)
    end

# views/articles/_form.html.erb
  <div class="field">
    <%= form.label :content %>
    <%= form.rich_text_area :content %>
  </div>

# views/articles/show.html.erb
<p><%= @article.content %></p>

<h2>
  Versions
  <%= @article.versions.size %>
</h2>

<table class='table'>
  <thead>
    <tr>
      <th>ID</th>
      <th>Previous Content</th>
      <th>User</th>
    </tr>
  </thead>

  <tbody>
    <% @article.versions.order(id: :desc).each do |version| %>
      <tr>
        <td><%= version.id %></td>
        <td><%= version.content %></td>
        <td><%= version.user.email %></td>
      </tr>
    <% end %>
  </tbody>
</table>