From 087840932da0ccde7774e41c88560ed16f42ddfd Mon Sep 17 00:00:00 2001 From: Ben Atkins Date: Wed, 31 Jul 2013 13:46:25 -0400 Subject: [PATCH] Changing the :clean_versions! method on the Cleaner module so that it accepts an options hash as an argument, and additional configurable options. --- lib/paper_trail/cleaner.rb | 34 +++++---- test/unit/cleaner_test.rb | 147 ++++++++++++++++++++++++++++--------- 2 files changed, 131 insertions(+), 50 deletions(-) diff --git a/lib/paper_trail/cleaner.rb b/lib/paper_trail/cleaner.rb index 106566a6..408ebf75 100644 --- a/lib/paper_trail/cleaner.rb +++ b/lib/paper_trail/cleaner.rb @@ -1,14 +1,19 @@ module PaperTrail module Cleaner - - # Deletes all versions that were created on a given date except the last `keep_int` versions. - # `date` argument should receive either an object of type `Date` or `:all` (which will clean versions on all dates). - def clean_versions!(keep_int = 1, date = :all) - version_hash = gather_versions(date) - version_hash.each do |item_id, versions| - grouping_by_date = versions.group_by { |v| v.created_at.to_date } # now group the versions by date - grouping_by_date.each do |date, versions| - versions.pop(keep_int) + # Destroys all but the most recent version(s) for items on a given date (or on all dates). Useful for deleting drafts. + # + # Options: + # :keeping An `integer` indicating the number of versions to be kept for each item per date. + # Defaults to `1`. + # :date Should either be a `Date` object specifying which date to destroy versions for or `:all`, + # which will specify that all dates should be cleaned. Defaults to `:all`. + # :item_id The `id` for the item to be cleaned on, or `nil`, which causes all items to be cleaned. + # Defaults to `nil`. + def clean_versions!(options = {}) + options = {:keeping => 1, :date => :all}.merge(options) + gather_versions(options[:item_id], options[:date]).each do |item_id, versions| + versions.group_by { |v| v.created_at.to_date }.each do |date, versions| # now group the versions by date and iterate through those + versions.pop(options[:keeping]) # remove the number of versions we wish to keep from the collection of versions prior to destruction versions.map(&:destroy) end end @@ -16,12 +21,13 @@ module PaperTrail private - # Returns a hash of versions in this format: {:item_id => PaperTrail::Version} - def gather_versions(date) - raise "`date` argument must receive a Timestamp or `:all`" unless date == :all || date.respond_to?(:to_time) - versions = date == :all ? Version.all : Version.between(date, date+1.day) + # Returns a hash of versions grouped by the `item_id` attribute formatted like this: {:item_id => PaperTrail::Version}. + # If `item_id` or `date` is set, versions will be narrowed to those pointing at items with those ids that were created on specified date. + def gather_versions(item_id = nil, date = :all) + raise "`date` argument must receive a Timestamp or `:all`" unless date == :all || date.respond_to?(:to_date) + versions = item_id ? PaperTrail::Version.where(:item_id => item_id) : PaperTrail::Version + versions = date == :all ? versions.all : versions.between(date.to_date, date.to_date + 1.day) versions.group_by(&:item_id) end - end end diff --git a/test/unit/cleaner_test.rb b/test/unit/cleaner_test.rb index 8c61d9bb..8f07b26f 100644 --- a/test/unit/cleaner_test.rb +++ b/test/unit/cleaner_test.rb @@ -3,27 +3,23 @@ require 'test_helper' class PaperTrailCleanerTest < ActiveSupport::TestCase setup do - @animal = Animal.create :name => 'Animal' - @animal.update_attributes :name => 'Animal from the Muppets' - @animal.update_attributes :name => 'Animal Muppet' - - @dog = Dog.create :name => 'Snoopy' - @dog.update_attributes :name => 'Scooby' - @dog.update_attributes :name => 'Scooby Doo' - - @cat = Cat.create :name => 'Garfield' - @cat.update_attributes :name => 'Garfield (I hate Mondays)' - @cat.update_attributes :name => 'Garfield The Cat' - @animals = [@animal, @dog, @cat] + @animals = [@animal = Animal.new, @dog = Dog.new, @cat = Cat.new] + @animals.each do |animal| + 3.times { animal.update_attribute(:name, Faker::Name.name) } + end end test 'Baseline' do assert_equal 9, PaperTrail::Version.count + @animals.each { |animal| assert_equal 3, animal.versions.size } end - context 'Cleaner' do - context '`clean_versions!` method' do + context '`clean_versions!` method' do + should 'be extended by `PaperTrail` module' do + assert_respond_to PaperTrail, :clean_versions! + end + context 'No options provided' do should 'removes extra versions for each item' do PaperTrail.clean_versions! assert_equal 3, PaperTrail::Version.count @@ -35,34 +31,113 @@ class PaperTrailCleanerTest < ActiveSupport::TestCase PaperTrail.clean_versions! assert_equal most_recent_version_names, @animals.map { |animal| animal.versions.last.reify.name } end + end - context '`keep_int` argument' do - should 'modifies the number of versions ommitted from destruction' do - PaperTrail.clean_versions!(2) - assert_equal 6, PaperTrail::Version.all.count - @animals.each { |animal| assert_equal 2, animal.versions.size } + context '`:keeping` option' do + should 'modifies the number of versions ommitted from destruction' do + PaperTrail.clean_versions!(:keeping => 2) + assert_equal 6, PaperTrail::Version.all.count + @animals.each { |animal| assert_equal 2, animal.versions.size } + end + end + + context '`:date` option' do + setup do + @animal.versions.each { |ver| ver.update_attribute(:created_at, ver.created_at - 1.day) } + @date = @animal.versions.first.created_at.to_date + @animal.update_attribute(:name, Faker::Name.name) + end + + should 'restrict the versions destroyed to those that were created on the date provided' do + assert_equal 10, PaperTrail::Version.count + assert_equal 4, @animal.versions.size + assert_equal 3, @animal.versions_between(@date, @date + 1.day).size + PaperTrail.clean_versions!(:date => @date) + assert_equal 8, PaperTrail::Version.count + assert_equal 2, @animal.versions(true).size + assert_equal @date, @animal.versions.first.created_at.to_date + assert_not_same @date, @animal.versions.last.created_at.to_date + end + end + + context '`:item_id` option' do + context 'single ID received' do + should 'restrict the versions destroyed to the versions for the Item with that ID' do + PaperTrail.clean_versions!(:item_id => @animal.id) + assert_equal 1, @animal.versions.size + assert_equal 7, PaperTrail::Version.count end end - context '`date` argument' do - setup do - @animal.versions.each { |ver| ver.update_attribute(:created_at, ver.created_at - 1.day) } - @date = @animal.versions.first.created_at.to_date - @animal.update_attribute(:name, 'Muppet') - end - - should 'restrict the version destroyed to those that were created on the date provided' do - assert_equal 10, PaperTrail::Version.count - assert_equal 4, @animal.versions.size - assert_equal 3, @animal.versions_between(@date, @date + 1.day).size - PaperTrail.clean_versions!(1, @date) - assert_equal 8, PaperTrail::Version.count - assert_equal 2, @animal.versions(true).size - assert_equal @date, @animal.versions.first.created_at.to_date - assert_equal @date + 1.day, @animal.versions.last.created_at.to_date + context "collection of ID's received" do + should "restrict the versions destroyed to the versions for the Item with those ID's" do + PaperTrail.clean_versions!(:item_id => [@animal.id, @dog.id]) + assert_equal 1, @animal.versions.size + assert_equal 1, @dog.versions.size + assert_equal 5, PaperTrail::Version.count end end end - end + context 'options combinations' do # additional tests to cover combinations of options + context '`:date`' do + setup do + [@animal, @dog].each do |animal| + animal.versions.each { |ver| ver.update_attribute(:created_at, ver.created_at - 1.day) } + animal.update_attribute(:name, Faker::Name.name) + end + @date = @animal.versions.first.created_at.to_date + end + + should 'Baseline' do + assert_equal 11, PaperTrail::Version.count + [@animal, @dog].each do |animal| + assert_equal 4, animal.versions.size + assert_equal 3, animal.versions.between(@date, @date+1.day).size + end + end + + context 'and `:keeping`' do + should 'restrict cleaning properly' do + PaperTrail.clean_versions!(:date => @date, :keeping => 2) + [@animal, @dog].each do |animal| + animal.versions.reload # reload the association to pick up the destructions made by the `Cleaner` + assert_equal 3, animal.versions.size + assert_equal 2, animal.versions.between(@date, @date+1.day).size + end + assert_equal 9, PaperTrail::Version.count # ensure that the versions for the `@cat` instance wasn't touched + end + end + + context 'and `:item_id`' do + should 'restrict cleaning properly' do + PaperTrail.clean_versions!(:date => @date, :item_id => @dog.id) + @dog.versions.reload # reload the association to pick up the destructions made by the `Cleaner` + assert_equal 2, @dog.versions.size + assert_equal 1, @dog.versions.between(@date, @date+1.day).size + assert_equal 9, PaperTrail::Version.count # ensure the versions for other animals besides `@animal` weren't touched + end + end + + context ', `:item_id`, and `:keeping`' do + should 'restrict cleaning properly' do + PaperTrail.clean_versions!(:date => @date, :item_id => @dog.id, :keeping => 2) + @dog.versions.reload # reload the association to pick up the destructions made by the `Cleaner` + assert_equal 3, @dog.versions.size + assert_equal 2, @dog.versions.between(@date, @date+1.day).size + assert_equal 10, PaperTrail::Version.count # ensure the versions for other animals besides `@animal` weren't touched + end + end + end + + context '`:keeping` and `:item_id`' do + should 'restrict cleaning properly' do + PaperTrail.clean_versions!(:keeping => 2, :item_id => @animal.id) + assert_equal 2, @animal.versions.size + assert_equal 8, PaperTrail::Version.count # ensure the versions for other animals besides `@animal` weren't touched + end + end + end + + end # clean_versions! method end