From ce2de4cd29f37c79d9f4789e0d383b3370b5177b Mon Sep 17 00:00:00 2001 From: Chulki Lee Date: Fri, 18 Oct 2013 12:19:19 -0700 Subject: [PATCH 1/3] Use ActiveSupport Concern It makes easy to use version class with different AR connection --- lib/paper_trail/version.rb | 72 +++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/lib/paper_trail/version.rb b/lib/paper_trail/version.rb index d7e29509a..0af5550ce 100644 --- a/lib/paper_trail/version.rb +++ b/lib/paper_trail/version.rb @@ -1,45 +1,50 @@ +require 'active_support/concern' + module PaperTrail - class Version < ::ActiveRecord::Base - belongs_to :item, :polymorphic => true - validates_presence_of :event - attr_accessible :item_type, :item_id, :event, :whodunnit, :object, :object_changes if PaperTrail.active_record_protected_attributes? + module VersionConcern + extend ActiveSupport::Concern + included do + belongs_to :item, :polymorphic => true + validates_presence_of :event + attr_accessible :item_type, :item_id, :event, :whodunnit, :object, :object_changes if PaperTrail.active_record_protected_attributes? - after_create :enforce_version_limit! + after_create :enforce_version_limit! - def self.with_item_keys(item_type, item_id) - where :item_type => item_type, :item_id => item_id - end + def self.with_item_keys(item_type, item_id) + where :item_type => item_type, :item_id => item_id + end - def self.creates - where :event => 'create' - end + def self.creates + where :event => 'create' + end - def self.updates - where :event => 'update' - end + def self.updates + where :event => 'update' + end - def self.destroys - where :event => 'destroy' - end + def self.destroys + where :event => 'destroy' + end - def self.not_creates - where 'event <> ?', 'create' - end + def self.not_creates + where 'event <> ?', 'create' + end - # These methods accept a timestamp or a version and returns other versions that come before or after - def self.subsequent(obj) - obj = obj.send(PaperTrail.timestamp_field) if obj.is_a?(self) - where("#{PaperTrail.timestamp_field} > ?", obj).order("#{PaperTrail.timestamp_field} ASC") - end + # These methods accept a timestamp or a version and returns other versions that come before or after + def self.subsequent(obj) + obj = obj.send(PaperTrail.timestamp_field) if obj.is_a?(self) + where("#{PaperTrail.timestamp_field} > ?", obj).order("#{PaperTrail.timestamp_field} ASC") + end - def self.preceding(obj) - obj = obj.send(PaperTrail.timestamp_field) if obj.is_a?(self) - where("#{PaperTrail.timestamp_field} < ?", obj).order("#{PaperTrail.timestamp_field} DESC") - end + def self.preceding(obj) + obj = obj.send(PaperTrail.timestamp_field) if obj.is_a?(self) + where("#{PaperTrail.timestamp_field} < ?", obj).order("#{PaperTrail.timestamp_field} DESC") + end - def self.between(start_time, end_time) - where("#{PaperTrail.timestamp_field} > ? AND #{PaperTrail.timestamp_field} < ?", start_time, end_time). - order("#{PaperTrail.timestamp_field} ASC") + def self.between(start_time, end_time) + where("#{PaperTrail.timestamp_field} > ? AND #{PaperTrail.timestamp_field} < ?", start_time, end_time). + order("#{PaperTrail.timestamp_field} ASC") + end end # Returns whether the `object` column is using the `json` type supported by PostgreSQL @@ -205,6 +210,9 @@ def enforce_version_limit! excess_previous_versions = previous_versions - previous_versions.last(PaperTrail.config.version_limit) excess_previous_versions.map(&:destroy) end + end + class Version < ::ActiveRecord::Base + include VersionConcern end end From 1f5f8493b78709e765f59f17efdac788b261976f Mon Sep 17 00:00:00 2001 From: Chulki Lee Date: Mon, 21 Oct 2013 20:18:42 -0700 Subject: [PATCH 2/3] Use ClassMethods module for AS::Concern including missed class methods --- lib/paper_trail/version.rb | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/paper_trail/version.rb b/lib/paper_trail/version.rb index 0af5550ce..d0df5713b 100644 --- a/lib/paper_trail/version.rb +++ b/lib/paper_trail/version.rb @@ -9,52 +9,54 @@ module VersionConcern attr_accessible :item_type, :item_id, :event, :whodunnit, :object, :object_changes if PaperTrail.active_record_protected_attributes? after_create :enforce_version_limit! + end - def self.with_item_keys(item_type, item_id) + module ClassMethods + def with_item_keys(item_type, item_id) where :item_type => item_type, :item_id => item_id end - def self.creates + def creates where :event => 'create' end - def self.updates + def updates where :event => 'update' end - def self.destroys + def destroys where :event => 'destroy' end - def self.not_creates + def not_creates where 'event <> ?', 'create' end # These methods accept a timestamp or a version and returns other versions that come before or after - def self.subsequent(obj) + def subsequent(obj) obj = obj.send(PaperTrail.timestamp_field) if obj.is_a?(self) where("#{PaperTrail.timestamp_field} > ?", obj).order("#{PaperTrail.timestamp_field} ASC") end - def self.preceding(obj) + def preceding(obj) obj = obj.send(PaperTrail.timestamp_field) if obj.is_a?(self) where("#{PaperTrail.timestamp_field} < ?", obj).order("#{PaperTrail.timestamp_field} DESC") end - def self.between(start_time, end_time) + def between(start_time, end_time) where("#{PaperTrail.timestamp_field} > ? AND #{PaperTrail.timestamp_field} < ?", start_time, end_time). order("#{PaperTrail.timestamp_field} ASC") end - end - # Returns whether the `object` column is using the `json` type supported by PostgreSQL - def self.object_col_is_json? - @object_col_is_json ||= columns_hash['object'].type == :json - end + # Returns whether the `object` column is using the `json` type supported by PostgreSQL + def object_col_is_json? + @object_col_is_json ||= columns_hash['object'].type == :json + end - # Returns whether the `object_changes` column is using the `json` type supported by PostgreSQL - def self.object_changes_col_is_json? - @object_changes_col_is_json ||= columns_hash['object_changes'].type == :json + # Returns whether the `object_changes` column is using the `json` type supported by PostgreSQL + def object_changes_col_is_json? + @object_changes_col_is_json ||= columns_hash['object_changes'].type == :json + end end # Restore the item from this version. From 00bf38ff1e60ccffa70073f801f56ef0406ec3b2 Mon Sep 17 00:00:00 2001 From: Chulki Lee Date: Tue, 22 Oct 2013 10:12:08 -0700 Subject: [PATCH 3/3] Add rspec for concern --- spec/models/version_spec.rb | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/spec/models/version_spec.rb b/spec/models/version_spec.rb index d8c83818e..2ebf3948f 100644 --- a/spec/models/version_spec.rb +++ b/spec/models/version_spec.rb @@ -1,5 +1,51 @@ require 'spec_helper' +module Foo + class Base < ActiveRecord::Base + self.abstract_class = true + end + + class Document < Base + has_paper_trail :class_name => 'Foo::Version' + end + + class Version < Base + include PaperTrail::VersionConcern + end +end +Foo::Base.establish_connection(:adapter => 'sqlite3', :database => File.expand_path('../../../test/dummy/db/test-foo.sqlite3', __FILE__)) + +module Bar + class Base < ActiveRecord::Base + self.abstract_class = true + end + + class Document < Base + has_paper_trail :class_name => 'Bar::Version' + end + + class Version < Base + include PaperTrail::VersionConcern + end +end +Bar::Base.establish_connection(:adapter => 'sqlite3', :database => File.expand_path('../../../test/dummy/db/test-bar.sqlite3', __FILE__)) + +describe PaperTrail::VersionConcern do + it 'allows included class to have different connections' do + Foo::Version.connection.should_not eq Bar::Version.connection + end + + it 'allows custom version class to share connection with superclass' do + Foo::Version.connection.should eq Foo::Document.connection + Bar::Version.connection.should eq Bar::Document.connection + end + + it 'can be used with class_name option' do + Foo::Document.version_class_name.should eq 'Foo::Version' + Bar::Document.version_class_name.should eq 'Bar::Version' + end +end + describe PaperTrail::Version do describe "Attributes" do it { should have_db_column(:item_type).of_type(:string) }