Skip to content

Instantly share code, notes, and snippets.

@cblunt
Last active October 25, 2018 10:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cblunt/ef8191e676aca21ae80647e44c5bbdff to your computer and use it in GitHub Desktop.
Save cblunt/ef8191e676aca21ae80647e44c5bbdff to your computer and use it in GitHub Desktop.
Bug when versioning tag_list using paper-trail (10.0) and acts as taggable on (6.0)
# frozen_string_literal: true
# Use this template to report PaperTrail bugs.
# Please include only the minimum code necessary to reproduce your issue.
require 'bundler/inline'
gemfile(true) do
ruby '2.5.3'
source 'https://rubygems.org'
gem 'activerecord', '5.2.1'
gem 'minitest', '5.11.3'
gem 'paper_trail', '10.0.1', require: false
gem 'sqlite3', '1.3.13'
gem 'acts-as-taggable-on', '6.0.0'
end
require 'active_record'
require 'minitest/autorun'
require 'logger'
# Please use sqlite for your bug reports, if possible.
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = nil
ActiveRecord::Schema.define do
create_table :articles, force: true do |t|
t.string :title, null: false
end
create_table :users, force: true do |t|
t.text :first_name, null: false
t.timestamps null: false
end
create_table :versions 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, limit: 1_073_741_823
t.text :object_changes, limit: 1_073_741_823
t.datetime :created_at
end
add_index :versions, %i[item_type item_id]
# Acts as Taggable on
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.string "taggable_type"
t.integer "taggable_id"
t.string "tagger_type"
t.integer "tagger_id"
t.string "context", limit: 128
t.datetime "created_at"
t.index ["context"], name: "index_taggings_on_context"
t.index ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
t.index ["tag_id"], name: "index_taggings_on_tag_id"
t.index ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
t.index ["taggable_id", "taggable_type", "tagger_id", "context"], name: "taggings_idy"
t.index ["taggable_id"], name: "index_taggings_on_taggable_id"
t.index ["taggable_type"], name: "index_taggings_on_taggable_type"
t.index ["tagger_id", "tagger_type"], name: "index_taggings_on_tagger_id_and_tagger_type"
t.index ["tagger_id"], name: "index_taggings_on_tagger_id"
end
create_table "tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
t.index ["name"], name: "index_tags_on_name", unique: true
end
end
ActiveRecord::Base.logger = Logger.new(STDOUT)
require 'paper_trail'
class Article < ActiveRecord::Base
has_paper_trail
acts_as_taggable
end
class BugTest < ActiveSupport::TestCase
def test_previous_version_tag_list
# Simulate new/create
Article.create(title: 'My Article', tag_list: 'hello')
assert_equal 1, PaperTrail::Version.count
# Simulate edit/update
article = Article.first
article.update(tag_list: 'world')
assert_equal({ "tag_list" => [["hello"], ["world"]] }, article.versions.last.changeset)
end
end
@cblunt
Copy link
Author

cblunt commented Oct 25, 2018

Proposed workaround behaviour (paper-trail-gem/paper_trail#1162):

# frozen_string_literal: true

# Use this template to report PaperTrail bugs.
# Please include only the minimum code necessary to reproduce your issue.
require 'bundler/inline'

gemfile(true) do
  ruby '2.5.3'
  source 'https://rubygems.org'
  gem 'activerecord', '5.2.1'
  gem 'minitest', '5.11.3'
  gem 'paper_trail', '10.0.1'
  gem 'sqlite3', '1.3.13'

  gem 'acts-as-taggable-on', '6.0.0'
  gem 'byebug'
  gem 'database_cleaner'
end

require 'active_record'
require 'minitest/autorun'
require 'logger'

# Please use sqlite for your bug reports, if possible.
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = nil
ActiveRecord::Schema.define do
  create_table :articles, force: true do |t|
    t.string :title, null: false
    t.string :cached_tag_list
  end

  create_table :versions 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, limit: 1_073_741_823
    t.text :object_changes, limit: 1_073_741_823
    t.datetime :created_at

    t.string :tag_list
  end
  add_index :versions, %i[item_type item_id]

  # Acts as Taggable on
  create_table 'taggings', force: :cascade do |t|
    t.integer 'tag_id'
    t.string 'taggable_type'
    t.integer 'taggable_id'
    t.string 'tagger_type'
    t.integer 'tagger_id'
    t.string 'context', limit: 128
    t.datetime 'created_at'
    t.index ['context'], name: 'index_taggings_on_context'
    t.index %w[tag_id taggable_id taggable_type context tagger_id tagger_type], name: 'taggings_idx', unique: true
    t.index ['tag_id'], name: 'index_taggings_on_tag_id'
    t.index %w[taggable_id taggable_type context], name: 'index_taggings_on_taggable_id_and_taggable_type_and_context'
    t.index %w[taggable_id taggable_type tagger_id context], name: 'taggings_idy'
    t.index ['taggable_id'], name: 'index_taggings_on_taggable_id'
    t.index ['taggable_type'], name: 'index_taggings_on_taggable_type'
    t.index %w[tagger_id tagger_type], name: 'index_taggings_on_tagger_id_and_tagger_type'
    t.index ['tagger_id'], name: 'index_taggings_on_tagger_id'
  end

  create_table 'tags', force: :cascade do |t|
    t.string 'name'
    t.integer 'taggings_count', default: 0
    t.index ['name'], name: 'index_tags_on_name', unique: true
  end
end
ActiveRecord::Base.logger = Logger.new(STDOUT)
require 'paper_trail'

class KeywordList < Array
  def initialize(*args)
    add(*args)
  end

  def <<(obj)
    add(obj)
  end

  def add(*names)
    concat(*names.split(','))
    self
  end
end

class Article < ActiveRecord::Base
  has_paper_trail ignore: [:tag_list]
  acts_as_taggable

  define_model_callbacks :reify, only: :after

  before_save :cache_tag_list
  after_reify :restore_cached_tags

  private

  def cache_tag_list
    self.cached_tag_list = tag_list.join(',')
  end

  def restore_cached_tags
    self.tag_list = cached_tag_list
  end
end

class BugTest < ActiveSupport::TestCase
  def setup
    DatabaseCleaner.start
  end

  def teardown
    DatabaseCleaner.clean
  end

  def test_previous_version_tag_list
    # Simulate new/create
    Article.create(title: 'My Article', tag_list: 'hello')
    assert_equal 1, PaperTrail::Version.count

    # Simulate edit/update
    article = Article.first
    article.update(tag_list: 'world')

    assert_equal({ 'cached_tag_list' => ['hello', 'world'] }, article.versions.last.changeset)
  end

  def test_model_reify_with_cached_attribute
    # Simulate new/create
    Article.create(title: 'My Article', tag_list: 'hello')
    assert_equal 1, PaperTrail::Version.count

    # Simulate edit/update
    article = Article.first
    article.update(tag_list: 'world')

    restored = article.versions.last.reify

    assert_equal(['hello'], restored.tag_list)
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment