Skip to content

Commit

Permalink
Merge pull request #1 from crossroads/rails_5
Browse files Browse the repository at this point in the history
Rails 5 changes
  • Loading branch information
steveyken authored Nov 10, 2021
2 parents ce15b5c + d94dd1e commit 6cb9c6b
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 69 deletions.
57 changes: 39 additions & 18 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
PATH
remote: .
specs:
acts_as_revisionable (1.3.0.sermo)
activerecord (~> 3.2)
acts_as_revisionable (1.5.0.sermo)
activerecord (>= 4.0)

GEM
remote: https://rubygems.org/
specs:
activemodel (3.2.19)
activesupport (= 3.2.19)
builder (~> 3.0.0)
activerecord (3.2.19)
activemodel (= 3.2.19)
activesupport (= 3.2.19)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.19)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
arel (3.0.3)
builder (3.0.4)
activemodel (4.2.10)
activesupport (= 4.2.10)
builder (~> 3.1)
activerecord (4.2.10)
activemodel (= 4.2.10)
activesupport (= 4.2.10)
arel (~> 6.0)
activesupport (4.2.10)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
arel (6.0.4)
builder (3.2.3)
byebug (9.0.6)
coderay (1.1.1)
concurrent-ruby (1.0.5)
diff-lcs (1.2.5)
i18n (0.6.11)
multi_json (1.10.1)
i18n (0.9.0)
concurrent-ruby (~> 1.0)
method_source (0.8.2)
minitest (5.10.3)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
pry-byebug (3.4.2)
byebug (~> 9.0)
pry (~> 0.10)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
Expand All @@ -31,13 +44,21 @@ GEM
rspec-expectations (2.14.5)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.6)
slop (3.6.0)
sqlite3 (1.3.9)
tzinfo (0.3.40)
thread_safe (0.3.6)
tzinfo (1.2.3)
thread_safe (~> 0.1)

PLATFORMS
ruby

DEPENDENCIES
acts_as_revisionable!
pry
pry-byebug
rspec (= 2.14.1)
sqlite3

BUNDLED WITH
1.16.0
4 changes: 3 additions & 1 deletion acts_as_revisionable.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ Gem::Specification.new do |s|
s.test_files = `git ls-files -- spec/*`.split("\n")
s.require_paths = ['lib']

s.add_runtime_dependency 'activerecord', '~> 3.2'
s.add_runtime_dependency 'activerecord', '>= 4.0'
s.add_development_dependency 'sqlite3', '>= 0'
s.add_development_dependency 'rspec', '= 2.14.1'
s.add_development_dependency 'pry'
s.add_development_dependency 'pry-byebug'
end
16 changes: 8 additions & 8 deletions lib/acts_as_revisionable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ def acts_as_revisionable(options = {})
extend ClassMethods
include InstanceMethods
class_name = acts_as_revisionable_options[:class_name].to_s if acts_as_revisionable_options[:class_name]
has_many_options = {:as => :revisionable, :order => 'revision DESC', :class_name => class_name}
has_many_options = {:as => :revisionable, :class_name => class_name}
has_many_options[:dependent] = :destroy unless options[:dependent] == :keep
has_many :revision_records, has_many_options
alias_method_chain :update, :revision if options[:on_update]
has_many :revision_records, -> { order('revision DESC') }, has_many_options
alias_method_chain :_update_record, :revision if options[:on_update]
alias_method_chain :destroy, :revision if options[:on_destroy]
end
end
Expand Down Expand Up @@ -152,7 +152,7 @@ def save_restorable_associations(record, associations)
if associations.kind_of?(Hash)
associations.each_pair do |association, sub_associations|
associated_records = record.send(association)
reflection = record.class.reflections[association].macro
reflection = record.class.reflections[association.to_s].macro

if reflection == :has_and_belongs_to_many
associated_records = associated_records.collect{|r| r}
Expand All @@ -168,7 +168,7 @@ def save_restorable_associations(record, associations)
end
end

associated_records = [associated_records] unless associated_records.kind_of?(Array)
associated_records = [associated_records] unless associated_records.kind_of?(ActiveRecord::Associations::CollectionProxy)
associated_records.each do |associated_record|
save_restorable_associations(associated_record, sub_associations) if associated_record
end
Expand Down Expand Up @@ -212,7 +212,7 @@ def store_revision(*args)
begin
revision_record_class.transaction do
begin
read_only = self.class.first(:conditions => {self.class.primary_key => self.id}, :readonly => true)
read_only = self.class.where(self.class.primary_key => self.id).readonly.first
if read_only
callback = self.class.acts_as_revisionable_options[:before_store_revision]
if callback
Expand Down Expand Up @@ -297,9 +297,9 @@ def revision_record_class
private

# Update the record while recording the revision.
def update_with_revision
def _update_record_with_revision
store_revision do
update_without_revision
_update_record_without_revision
end
end

Expand Down
27 changes: 13 additions & 14 deletions lib/acts_as_revisionable/revision_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ class RevisionRecord < ActiveRecord::Base
class << self
# Find a specific revision record.
def find_revision(klass, id, revision)
find(:first, :conditions => {:revisionable_type => klass.base_class.to_s, :revisionable_id => id, :revision => revision})
where(:revisionable_type => klass.base_class.to_s, :revisionable_id => id, :revision => revision).first
end

# Find the last revision record for a class.
def last_revision(klass, id, revision = nil)
find(:first, :conditions => {:revisionable_type => klass.base_class.to_s, :revisionable_id => id}, :order => "revision DESC")
where(:revisionable_type => klass.base_class.to_s, :revisionable_id => id).order("revision DESC").first
end

# Truncate the revisions for a record. Available options are :limit and :max_age.
Expand All @@ -27,10 +27,10 @@ def truncate_revisions(revisionable_type, revisionable_id, options)
conditions = ['revisionable_type = ? AND revisionable_id = ?', revisionable_type.base_class.to_s, revisionable_id]
if options[:minimum_age]
conditions.first << ' AND created_at <= ?'
conditions << options[:minimum_age].ago
conditions << options[:minimum_age].seconds.ago
end

start_deleting_revision = find(:first, :conditions => conditions, :order => 'revision DESC', :offset => options[:limit])
start_deleting_revision = where(conditions).order('revision DESC').offset(options[:limit]).first
if start_deleting_revision
delete_all(['revisionable_type = ? AND revisionable_id = ? AND revision <= ?', revisionable_type.base_class.to_s, revisionable_id, start_deleting_revision.revision])
end
Expand All @@ -40,7 +40,7 @@ def truncate_revisions(revisionable_type, revisionable_id, options)
# The +revisionable_type+ argument specifies the class to delete revision records for.
def empty_trash(revisionable_type, max_age)
sql = "revisionable_id IN (SELECT revisionable_id from #{table_name} WHERE created_at <= ? AND revisionable_type = ? AND trash = ?) AND revisionable_type = ?"
args = [max_age.ago, revisionable_type.name, true, revisionable_type.name]
args = [max_age.seconds.ago, revisionable_type.name, true, revisionable_type.name]
delete_all([sql] + args)
end

Expand Down Expand Up @@ -133,7 +133,7 @@ def deserialize_hash(data)
end

def set_revision_number
last_revision = self.class.maximum(:revision, :conditions => {:revisionable_type => self.revisionable_type, :revisionable_id => self.revisionable_id}) || 0
last_revision = self.class.where(:revisionable_type => self.revisionable_type, :revisionable_id => self.revisionable_id).maximum(:revision) || 0
self.revision = last_revision + 1
end

Expand Down Expand Up @@ -173,7 +173,7 @@ def attributes_and_associations(klass, hash)

if hash
hash.each_pair do |key, value|
if klass.reflections.include?(key.to_sym)
if klass.reflections.include?(key.to_s)
association_attrs[key] = value
else
attrs[key] = value
Expand All @@ -185,19 +185,18 @@ def attributes_and_associations(klass, hash)
end

def restore_association(record, association, association_attributes)
association = association.to_sym
reflection = record.class.reflections[association]
association = association
reflection = record.class.reflections[association.to_s]
associated_record = nil

begin
if reflection.macro == :has_many
if association_attributes.kind_of?(Array)
# Pop all the associated records to remove all records. In Rails 3.2 setting the value of the list
# will immediately affect the database
# Clear records
records = record.send(association)
while records.pop do
# no-op
end
records.to_a # load records
records.target.clear

association_attributes.each do |attrs|
restore_association(record, association, attrs)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/acts_as_revisionable/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module ActsAsRevisionable
VERSION = '1.3.0.sermo'
VERSION = '1.5.0.sermo'
end
5 changes: 1 addition & 4 deletions spec/acts_as_revisionable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ class RevisionableTestModel < ActiveRecord::Base
has_one :one_thing, :class_name => 'RevisionableTestOneThing'
has_and_belongs_to_many :non_revisionable_test_models

attr_protected :secret

acts_as_revisionable :limit => 3, :dependent => :keep, :associations => [:one_thing, :non_revisionable_test_models, {:many_things => :sub_things}]

def set_secret(val)
Expand Down Expand Up @@ -464,7 +462,7 @@ class RevisionableSubclassModel < RevisionableNamespaceModel
model.reload
ActsAsRevisionable::RevisionRecord.count.should == 0

model.should_receive(:update).and_raise("update failed")
model.should_receive(:save).and_raise("update failed")
model.name = 'new_name'
begin
model.store_revision do
Expand Down Expand Up @@ -654,7 +652,6 @@ class RevisionableSubclassModel < RevisionableNamespaceModel
ActsAsRevisionable::RevisionRecord.count.should == 1
model.name.should == 'new_name'
model.one_thing(true).name.should == 'new_other'

model.restore_revision!(1)
RevisionableTestModel.count.should == 1
RevisionableTestOneThing.count.should == 1
Expand Down
51 changes: 29 additions & 22 deletions spec/revision_record_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -333,28 +333,31 @@ def self.base_class
end

it "should be able to truncate the revisions for a record" do
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(:name => 'name'))
revision.revision = 20
ActsAsRevisionable::RevisionRecord.should_receive(:find).with(:first, :conditions => ['revisionable_type = ? AND revisionable_id = ?', 'TestRevisionableRecord', 1], :offset => 15, :order => 'revision DESC').and_return(revision)
ActsAsRevisionable::RevisionRecord.should_receive(:delete_all).with(['revisionable_type = ? AND revisionable_id = ? AND revision <= ?', 'TestRevisionableRecord', 1, 20])
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, 1, :limit => 15)
revisionable = TestRevisionableRecord.create!(:name => 'name')
revisions = 3.times.map do
ActsAsRevisionable::RevisionRecord.create!(revisionable)
end
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, revisionable.id, :limit => 2)
expect(ActsAsRevisionable::RevisionRecord.order(revision: :asc).to_a).to eq(revisions.from(1))
end

it "should be able to truncate the revisions for a record by age" do
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(:name => 'name'))
revision.revision = 20
time = 2.weeks.ago
minimum_age = double(:integer, :ago => time, :to_i => 1)
Time.stub(:now).and_return(minimum_age)
ActsAsRevisionable::RevisionRecord.should_receive(:find).with(:first, :conditions => ['revisionable_type = ? AND revisionable_id = ? AND created_at <= ?', 'TestRevisionableRecord', 1, time], :offset => nil, :order => 'revision DESC').and_return(revision)
ActsAsRevisionable::RevisionRecord.should_receive(:delete_all).with(['revisionable_type = ? AND revisionable_id = ? AND revision <= ?', 'TestRevisionableRecord', 1, 20])
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, 1, :minimum_age => minimum_age)
revisionable = TestRevisionableRecord.create!(:name => 'name')
revisions = 3.downto(1).map do |i|
ActsAsRevisionable::RevisionRecord.create!(revisionable).tap do |revision|
revision.update!(created_at: i.weeks.ago)
end
end
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, revisionable, :minimum_age => 15.days)
expect(ActsAsRevisionable::RevisionRecord.order(revision: :asc).to_a).to eq(revisions.from(1))
end

it "should not truncate the revisions for a record if it doesn't have enough" do
ActsAsRevisionable::RevisionRecord.should_receive(:find).with(:first, :conditions => ['revisionable_type = ? AND revisionable_id = ?', 'TestRevisionableRecord', 1], :offset => 15, :order => 'revision DESC').and_return(nil)
ActsAsRevisionable::RevisionRecord.should_not_receive(:delete_all)
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, 1, :limit => 15)
revisionable = TestRevisionableRecord.create!(:name => 'name')
ActsAsRevisionable::RevisionRecord.create!(revisionable)
expect {
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, revisionable.id, :limit => 15)
}.not_to change(ActsAsRevisionable::RevisionRecord, :count)
end

it "should not truncate the revisions for a record if no limit or minimum_age is set" do
Expand All @@ -364,15 +367,19 @@ def self.base_class
end

it "should be able to find a record by revisioned type and id" do
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(:name => 'name'))
ActsAsRevisionable::RevisionRecord.should_receive(:find).with(:first, :conditions => {:revisionable_type => 'TestRevisionableRecord', :revisionable_id => 1, :revision => 2}).and_return(revision)
ActsAsRevisionable::RevisionRecord.find_revision(TestRevisionableRecord, 1, 2).should == revision
revisionable = TestRevisionableRecord.create!(:name => 'name')
revision_record = ActsAsRevisionable::RevisionRecord.create!(revisionable)
found_revision = ActsAsRevisionable::RevisionRecord.find_revision(
TestRevisionableRecord, revisionable.id, revision_record.revision
)
expect(found_revision).to eq revision_record
end

it "should find the last revision" do
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(:name => 'name'))
ActsAsRevisionable::RevisionRecord.should_receive(:find).with(:first, :conditions => {:revisionable_type => 'TestRevisionableRecord', :revisionable_id => 1}, :order => "revision DESC").and_return(revision)
ActsAsRevisionable::RevisionRecord.last_revision(TestRevisionableRecord, 1).should == revision
revisionable = TestRevisionableRecord.create!(:name => 'name')
revisions = 2.times.map { ActsAsRevisionable::RevisionRecord.create!(revisionable) }
found_revision = ActsAsRevisionable::RevisionRecord.last_revision(TestRevisionableRecord, revisionable.id)
expect(found_revision).to eq revisions.last
end

it "should handle module namespaces" do
Expand Down
3 changes: 2 additions & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

require 'active_record'
require 'sqlite3'
require 'pry'
require 'pry-byebug'
require File.expand_path('../../lib/acts_as_revisionable', __FILE__)

ActiveRecord::ActiveRecordError
ActiveRecord::Base.logger = Logger.new(StringIO.new)

puts "Testing with ActiveRecord #{ActiveRecord::VERSION::STRING}"
Expand Down

0 comments on commit 6cb9c6b

Please sign in to comment.