diff --git a/CHANGELOG.md b/CHANGELOG.md index d0eb5c4b6..d7ab56c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ Breaking changes Added -- None +- Limit number of audits stored + [#405](https://github.com/collectiveidea/audited/pull/405) Changed diff --git a/README.md b/README.md index 3d8abc1da..79a33fccb 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Audited supports and is [tested against](http://travis-ci.org/collectiveidea/aud * 2.2.8 * 2.3.5 * 2.4.2 +* 2.5.0 Audited may work just fine with a Ruby version not listed above, but we can't guarantee that it will. If you'd like to maintain a Ruby that isn't listed, please let us know with a [pull request](https://github.com/collectiveidea/audited/pulls). @@ -141,6 +142,33 @@ class User < ActiveRecord::Base end ``` +### Limiting stored audits + +You can limit the number of audits stored for your model. To configure limiting for all audited models, put the following in an initializer: + +```ruby +Audited.max_audits = 10 # keep only 10 latest audits +``` + +or customize per model: + +```ruby +class User < ActiveRecord::Base + audited max_audits: 2 +end +``` + +Whenever a user is updated or destroyed, extra audits are merged into newer ones and destroyed. + +```ruby +user = User.create!(name: "Steve") +user.audits.count # => 1 +user.update_attributes!(name: "Ryan") +user.audits.count # => 2 +user.destroy +user.audits.count # => 2 +``` + ### Current User Tracking If you're using Audited in a Rails application, all audited changes made within a request will automatically be attributed to the current user. By default, Audited uses the `current_user` method in your controller. diff --git a/lib/audited.rb b/lib/audited.rb index eaf34cb99..de04a09e4 100644 --- a/lib/audited.rb +++ b/lib/audited.rb @@ -4,6 +4,7 @@ module Audited class << self attr_accessor :ignored_attributes, :current_user_method attr_writer :audit_class + attr_reader :max_audits def audit_class @audit_class ||= Audit @@ -13,6 +14,10 @@ def store Thread.current[:audited_store] ||= {} end + def max_audits=(value) + @max_audits = Integer(value).abs + end + def config yield(self) end diff --git a/lib/audited/auditor.rb b/lib/audited/auditor.rb index 019f6a246..93c157995 100644 --- a/lib/audited/auditor.rb +++ b/lib/audited/auditor.rb @@ -33,6 +33,7 @@ module ClassMethods # # * +require_comment+ - Ensures that audit_comment is supplied before # any create, update or destroy operation. + # * +max_audits+ - Limits the number of stored audits. # def audited(options = {}) # don't allow multiple calls @@ -43,14 +44,14 @@ def audited(options = {}) class_attribute :audit_associated_with, instance_writer: false class_attribute :audited_options, instance_writer: false - class_attribute :keep_audits, instance_writer: false + class_attribute :max_audits, instance_writer: false attr_accessor :version, :audit_comment self.audited_options = options normalize_audited_options self.audit_associated_with = audited_options[:associated_with] - self.keep_audits = audited_options[:keep_audits] + self.max_audits = audited_options[:max_audits] || Audited.max_audits if audited_options[:comment_required] validates_presence_of :audit_comment, if: :auditing_enabled @@ -206,13 +207,7 @@ def audit_create def audit_update unless (changes = audited_changes).empty? && audit_comment.blank? write_audit(action: 'update', audited_changes: changes, - comment: audit_comment) do - if keep_audits && audits.count > keep_audits.to_i - first_audit, second_audit = audits.limit(2) - second_audit.merge!(first_audit) - first_audit.destroy! - end - end + comment: audit_comment) end end @@ -228,12 +223,20 @@ def write_audit(attrs) if auditing_enabled run_callbacks(:audit) { audit = audits.create(attrs) - yield if block_given? + reduce_audits_if_needed if attrs[:action] != 'create' audit } end end + def reduce_audits_if_needed + if max_audits && audits.count > max_audits + first_audit, second_audit = audits.limit(2) + second_audit.merge!(first_audit) + first_audit.destroy! + end + end + def require_comment if auditing_enabled && audit_comment.blank? errors.add(:audit_comment, "Comment required before destruction") @@ -327,6 +330,7 @@ def normalize_audited_options audited_options[:on] = [:create, :update, :destroy] if audited_options[:on].empty? audited_options[:only] = Array.wrap(audited_options[:only]).map(&:to_s) audited_options[:except] = Array.wrap(audited_options[:except]).map(&:to_s) + audited_options[:max_audits] = Integer(audited_options[:max_audits]).abs if audited_options[:max_audits].present? end end end diff --git a/spec/audited/auditor_spec.rb b/spec/audited/auditor_spec.rb index 501a2211a..35e7c4024 100644 --- a/spec/audited/auditor_spec.rb +++ b/spec/audited/auditor_spec.rb @@ -329,32 +329,42 @@ def non_column_attr=(val) end end - describe "keep_audits" do - it "should be nil by default" do - expect(Models::ActiveRecord::User.keep_audits).to be_nil + describe "max_audits" do + it "should respect global setting" do + expect(Models::ActiveRecord::User.max_audits).to eq(10) + end + + it "should respect per model setting" do + previous_max_audits = Models::ActiveRecord::User.max_audits + begin + Models::ActiveRecord::User.max_audits = 5 + expect(Models::ActiveRecord::User.max_audits).to eq(5) + ensure + Models::ActiveRecord::User.max_audits = previous_max_audits + end end it "should delete old audits when keeped amount exceeded" do - previous_keep_audits = Models::ActiveRecord::User.keep_audits + previous_max_audits = Models::ActiveRecord::User.max_audits begin - Models::ActiveRecord::User.keep_audits = 2 + Models::ActiveRecord::User.max_audits = 2 user = create_versions(2) user.update(name: "John") expect(user.audits.pluck(:version)).to eq([2, 3]) ensure - Models::ActiveRecord::User.keep_audits = previous_keep_audits + Models::ActiveRecord::User.max_audits = previous_max_audits end end it "should not delete old audits when keeped amount not exceeded" do - previous_keep_audits = Models::ActiveRecord::User.keep_audits + previous_max_audits = Models::ActiveRecord::User.max_audits begin - Models::ActiveRecord::User.keep_audits = 3 + Models::ActiveRecord::User.max_audits = 3 user = create_versions(2) user.update(name: "John") expect(user.audits.pluck(:version)).to eq([1, 2, 3]) ensure - Models::ActiveRecord::User.keep_audits = previous_keep_audits + Models::ActiveRecord::User.max_audits = previous_max_audits end end end diff --git a/spec/rails_app/config/initializers/audited.rb b/spec/rails_app/config/initializers/audited.rb new file mode 100644 index 000000000..bd1743340 --- /dev/null +++ b/spec/rails_app/config/initializers/audited.rb @@ -0,0 +1,5 @@ +require 'audited' + +Audited.config do |config| + config.max_audits = 10 +end