notifiably_audited 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +8 -8
  2. data/Appraisals +11 -0
  3. data/CHANGELOG +34 -0
  4. data/LICENSE +19 -0
  5. data/audited-activerecord.gemspec +21 -0
  6. data/audited-mongo_mapper.gemspec +21 -0
  7. data/audited.gemspec +26 -0
  8. data/gemfiles/rails30.gemfile +7 -0
  9. data/gemfiles/rails31.gemfile +7 -0
  10. data/gemfiles/rails32.gemfile +7 -0
  11. data/lib/audited.rb +15 -0
  12. data/lib/audited/audit.rb +102 -0
  13. data/lib/audited/auditor.rb +270 -0
  14. data/lib/audited/rspec_matchers.rb +173 -0
  15. data/lib/audited/sweeper.rb +51 -0
  16. data/notifiably_audited.gemspec +11 -18
  17. data/spec/audited_spec_helpers.rb +31 -0
  18. data/spec/rails_app/config/application.rb +5 -0
  19. data/spec/rails_app/config/database.yml +24 -0
  20. data/spec/rails_app/config/environment.rb +5 -0
  21. data/spec/rails_app/config/environments/development.rb +19 -0
  22. data/spec/rails_app/config/environments/production.rb +33 -0
  23. data/spec/rails_app/config/environments/test.rb +33 -0
  24. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  25. data/spec/rails_app/config/initializers/inflections.rb +2 -0
  26. data/spec/rails_app/config/initializers/secret_token.rb +2 -0
  27. data/spec/rails_app/config/routes.rb +6 -0
  28. data/spec/spec_helper.rb +23 -0
  29. data/spec/support/active_record/models.rb +84 -0
  30. data/spec/support/active_record/schema.rb +54 -0
  31. data/spec/support/mongo_mapper/connection.rb +4 -0
  32. data/spec/support/mongo_mapper/models.rb +210 -0
  33. data/test/db/version_1.rb +17 -0
  34. data/test/db/version_2.rb +18 -0
  35. data/test/db/version_3.rb +19 -0
  36. data/test/db/version_4.rb +20 -0
  37. data/test/db/version_5.rb +18 -0
  38. data/test/install_generator_test.rb +17 -0
  39. data/test/test_helper.rb +19 -0
  40. data/test/upgrade_generator_test.rb +65 -0
  41. metadata +56 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ODcwNjQ2YTAzNzU1ZmRmNTM5MWRlYjIyMWJkMjA5YzZhMzljMTU2Ng==
4
+ MjE4ZTc3NjA5YWUxYzdlM2YwODc3N2UwMTM0NDk1NGQwNzFjNzk0OQ==
5
5
  data.tar.gz: !binary |-
6
- NGJiZWFlNTFkZDEzOWIxOTMyYjZlYzgyZjE4MTI2NTU3N2JhMzYzNg==
6
+ NGVlN2YzNjMxMWI1MGMxZWQwYzgzMmQzOTI5MGJlNzk0NmIxZGY5MA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NjUxZDhlZDNlZTY2OWVmNzI3MTY0ZTczN2E3ZGY3MjdkMzk5NTc3MWNkOTBj
10
- NDUxNjA5NzQwNjNhOTBkZGMxNTZhYzAzMjBhMTNlNDZmZTMyZmM4YzVlN2Ew
11
- MTM2YTRkYzIyZWI4MDljMmIxMDRhZTY3ZWY4ZDZiNjhiZjU4YmE=
9
+ YTVlM2QwMzliMDViNDQxNmM2NDIxOTQ1NDYxMzFhMzQyYWNjYTQ2OWU3NGE0
10
+ YzhkZmYwYmY3MTZmYWM0MTFmNDVkN2M3N2ZlNzIyNDE4MmUxZmYyYTY3MmUx
11
+ OTIwMjUxZjY4MGUwYjgzNzc2MDQzNzAxNTUxOTcxMDY3M2UyZDY=
12
12
  data.tar.gz: !binary |-
13
- OTdlMGIxMmM0YTNiMDkxN2QwZWFjMjVkODc1M2Q1YmQ4ZWVmMWFkZjkyMmY5
14
- ZmY5OWVlOTU5NGI5ZTE2MjU1MmM5MjJlNjg2MjJjNmQxMzFjOWUzMTUyYTFh
15
- ZWI0ZTE5N2I5MWE3YjhmYzhlN2I0ZWY4YWJjODI4MmY3Mzc0NTY=
13
+ MmYxOTczOWU2ZGRkM2IxYzFlNWIzMjhhZTRiZTIyNzJlOWY0MDA4NDU0NmVl
14
+ NzQ4NWU2NWQxYjcxNWEyYThlNWRlYjc5ZTFhMGU3ZjVmY2UyNDg1MmVmY2Qx
15
+ OTE2ZDI2YjkxNzBjMjcxOGQ4MzNlZjNjZWY1ZWNmNDE2YWNjOGM=
data/Appraisals ADDED
@@ -0,0 +1,11 @@
1
+ appraise 'rails30' do
2
+ gem 'rails', '~> 3.0.0'
3
+ end
4
+
5
+ appraise 'rails31' do
6
+ gem 'rails', '~> 3.1.0'
7
+ end
8
+
9
+ appraise 'rails32' do
10
+ gem 'rails', '~> 3.2.0'
11
+ end
data/CHANGELOG ADDED
@@ -0,0 +1,34 @@
1
+ Audited ChangeLog
2
+ -------------------------------------------------------------------------------
3
+ * 2012-04-10 - Add Audit scopes for creates, updates and destroys [chriswfx]
4
+ * 2011-10-25 - Made ignored_attributes configurable [senny]
5
+ * 2011-09-09 - Rails 3.x support
6
+ Support for associated audits
7
+ Support for remote IP address storage
8
+ Plenty of bug fixes and refactoring
9
+ [kennethkalmer, ineu, PatrickMa, jrozner, dwarburton, bsiggelkow, dgm]
10
+ * 2009-01-27 - Store old and new values for updates, and store all attributes on destroy.
11
+ Refactored revisioning methods to work as expected
12
+ * 2008-10-10 - changed to make it work in development mode
13
+ * 2008-09-24 - Add ability to record parent record of the record being audited
14
+ [Kenneth Kalmer]
15
+ * 2008-04-19 - refactored to make compatible with dirty tracking in edge rails
16
+ and to stop storing both old and new values in a single audit
17
+ * 2008-04-18 - Fix NoMethodError when trying to access the :previous revision
18
+ on a model that doesn't have previous revisions [Alex Soto]
19
+ * 2008-03-21 - added #changed_attributes to get access to the changes before a
20
+ save [Chris Parker]
21
+ * 2007-12-16 - Added #revision_at for retrieving a revision from a specific
22
+ time [Jacob Atzen]
23
+ * 2007-12-16 - Fix error when getting revision from audit with no changes
24
+ [Geoffrey Wiseman]
25
+ * 2007-12-16 - Remove dependency on acts_as_list
26
+ * 2007-06-17 - Added support getting previous revisions
27
+ * 2006-11-17 - Replaced use of singleton User.current_user with cache sweeper
28
+ implementation for auditing the user that made the change
29
+ * 2006-11-17 - added migration generator
30
+ * 2006-08-14 - incorporated changes from Michael Schuerig to write_attribute
31
+ that saves the new value after every change and not just the
32
+ first, and performs proper type-casting before doing comparisons
33
+ * 2006-08-14 - The "changes" are now saved as a serialized hash
34
+ * 2006-07-21 - initial version
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright © 2010 Brandon Keepers - Collective Idea
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'audited-activerecord'
5
+ gem.version = '3.0.0'
6
+
7
+ gem.authors = ['Brandon Keepers', 'Kenneth Kalmer', 'Daniel Morrison', 'Brian Ryckbost', 'Steve Richert', 'Ryan Glover']
8
+ gem.email = 'info@collectiveidea.com'
9
+ gem.description = 'Log all changes to your ActiveRecord models'
10
+ gem.summary = gem.description
11
+ gem.homepage = 'https://github.com/collectiveidea/audited'
12
+ gem.license = 'MIT'
13
+
14
+ gem.add_dependency 'audited', gem.version
15
+ gem.add_dependency 'activerecord', '~> 3.0'
16
+
17
+ gem.files = `git ls-files lib`.split($\).grep(/(active_?record|generators)/)
18
+ gem.files << 'LICENSE'
19
+ gem.require_paths = ['lib']
20
+ end
21
+
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'audited-mongo_mapper'
5
+ gem.version = '3.0.0'
6
+
7
+ gem.authors = ['Brandon Keepers', 'Kenneth Kalmer', 'Daniel Morrison', 'Brian Ryckbost', 'Steve Richert', 'Ryan Glover']
8
+ gem.email = 'info@collectiveidea.com'
9
+ gem.description = 'Log all changes to your MongoMapper models'
10
+ gem.summary = gem.description
11
+ gem.homepage = 'https://github.com/collectiveidea/audited'
12
+ gem.license = 'MIT'
13
+
14
+ gem.add_dependency 'audited', gem.version
15
+ gem.add_dependency 'mongo_mapper', '~> 0.11'
16
+
17
+ gem.files = `git ls-files lib`.split($\).grep(/mongo_mapper/)
18
+ gem.files << 'LICENSE'
19
+ gem.require_paths = ['lib']
20
+ end
21
+
data/audited.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'notifiably_audited'
5
+ gem.version = '0.0.2'
6
+
7
+ gem.authors = ['Brandon Keepers', 'Kenneth Kalmer', 'Daniel Morrison', 'Brian Ryckbost', 'Steve Richert', 'Ryan Glover']
8
+ gem.email = 'info@collectiveidea.com'
9
+ gem.description = 'Log all changes to your models'
10
+ gem.summary = gem.description
11
+ gem.homepage = 'https://github.com/collectiveidea/audited'
12
+ gem.license = 'MIT'
13
+
14
+ gem.add_development_dependency 'activerecord', '~> 3.0'
15
+ gem.add_development_dependency 'appraisal', '~> 0.4'
16
+ gem.add_development_dependency 'bson_ext', '~> 1.6'
17
+ gem.add_development_dependency 'mongo_mapper', '~> 0.11'
18
+ gem.add_development_dependency 'rails', '~> 3.0'
19
+ gem.add_development_dependency 'rspec-rails', '~> 2.0'
20
+ gem.add_development_dependency 'sqlite3', '~> 1.0'
21
+
22
+ gem.files = `git ls-files`.split($\).reject{|f| f =~ /(lib\/audited\-|adapters|generators)/ }
23
+ gem.test_files = gem.files.grep(/^spec\//)
24
+ gem.require_paths = ['lib']
25
+ end
26
+
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 3.0.0"
6
+
7
+ gemspec :name=>"audited", :path=>"../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 3.1.0"
6
+
7
+ gemspec :name=>"audited", :path=>"../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 3.2.0"
6
+
7
+ gemspec :name=>"audited", :path=>"../"
data/lib/audited.rb ADDED
@@ -0,0 +1,15 @@
1
+ module Audited
2
+ VERSION = '3.0.0'
3
+
4
+ class << self
5
+ attr_accessor :ignored_attributes, :current_user_method, :audit_class
6
+
7
+ def store
8
+ Thread.current[:audited_store] ||= {}
9
+ end
10
+ end
11
+
12
+ @ignored_attributes = %w(lock_version created_at updated_at created_on updated_on)
13
+
14
+ @current_user_method = :current_user
15
+ end
@@ -0,0 +1,102 @@
1
+ module Audited
2
+ module Audit
3
+ def self.included(klass)
4
+ klass.extend(ClassMethods)
5
+ klass.setup_audit
6
+ end
7
+
8
+ module ClassMethods
9
+ def setup_audit
10
+ belongs_to :auditable, :polymorphic => true
11
+ belongs_to :user, :polymorphic => true
12
+ belongs_to :associated, :polymorphic => true
13
+
14
+ before_create :set_version_number, :set_audit_user
15
+
16
+ cattr_accessor :audited_class_names
17
+ self.audited_class_names = Set.new
18
+
19
+ attr_accessible :action, :audited_changes, :comment, :associated
20
+ end
21
+
22
+ # Returns the list of classes that are being audited
23
+ def audited_classes
24
+ audited_class_names.map(&:constantize)
25
+ end
26
+
27
+ # All audits made during the block called will be recorded as made
28
+ # by +user+. This method is hopefully threadsafe, making it ideal
29
+ # for background operations that require audit information.
30
+ def as_user(user, &block)
31
+ Thread.current[:audited_user] = user
32
+ yield
33
+ ensure
34
+ Thread.current[:audited_user] = nil
35
+ end
36
+
37
+ # @private
38
+ def reconstruct_attributes(audits)
39
+ attributes = {}
40
+ result = audits.collect do |audit|
41
+ attributes.merge!(audit.new_attributes).merge!(:version => audit.version)
42
+ yield attributes if block_given?
43
+ end
44
+ block_given? ? result : attributes
45
+ end
46
+
47
+ # @private
48
+ def assign_revision_attributes(record, attributes)
49
+ attributes.each do |attr, val|
50
+ record = record.dup if record.frozen?
51
+
52
+ if record.respond_to?("#{attr}=")
53
+ record.attributes.has_key?(attr.to_s) ?
54
+ record[attr] = val :
55
+ record.send("#{attr}=", val)
56
+ end
57
+ end
58
+ record
59
+ end
60
+ end
61
+
62
+ # Return an instance of what the object looked like at this revision. If
63
+ # the object has been destroyed, this will be a new record.
64
+ def revision
65
+ clazz = auditable_type.constantize
66
+ (clazz.find_by_id(auditable_id) || clazz.new).tap do |m|
67
+ self.class.assign_revision_attributes(m, self.class.reconstruct_attributes(ancestors).merge({ :version => version }))
68
+ end
69
+ end
70
+
71
+ # Returns a hash of the changed attributes with the new values
72
+ def new_attributes
73
+ (audited_changes || {}).inject({}.with_indifferent_access) do |attrs,(attr,values)|
74
+ attrs[attr] = values.is_a?(Array) ? values.last : values
75
+ attrs
76
+ end
77
+ end
78
+
79
+ # Returns a hash of the changed attributes with the old values
80
+ def old_attributes
81
+ (audited_changes || {}).inject({}.with_indifferent_access) do |attrs,(attr,values)|
82
+ attrs[attr] = Array(values).first
83
+
84
+ attrs
85
+ end
86
+ end
87
+
88
+ private
89
+ def set_version_number
90
+ max = self.class.where(
91
+ :auditable_id => auditable_id,
92
+ :auditable_type => auditable_type
93
+ ).order(:version.desc).first.try(:version) || 0
94
+ self.version = max + 1
95
+ end
96
+
97
+ def set_audit_user
98
+ self.user = Thread.current[:audited_user] if Thread.current[:audited_user]
99
+ nil # prevent stopping callback chains
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,270 @@
1
+ module Audited
2
+ # Specify this act if you want changes to your model to be saved in an
3
+ # audit table. This assumes there is an audits table ready.
4
+ #
5
+ # class User < ActiveRecord::Base
6
+ # audited
7
+ # end
8
+ #
9
+ # To store an audit comment set model.audit_comment to your comment before
10
+ # a create, update or destroy operation.
11
+ #
12
+ # See <tt>Audited::Adapters::ActiveRecord::Auditor::ClassMethods#audited</tt>
13
+ # for configuration options
14
+ module Auditor #:nodoc:
15
+ extend ActiveSupport::Concern
16
+
17
+ CALLBACKS = [:audit_create, :audit_update, :audit_destroy]
18
+
19
+ module ClassMethods
20
+ # == Configuration options
21
+ #
22
+ #
23
+ # * +only+ - Only audit the given attributes
24
+ # * +except+ - Excludes fields from being saved in the audit log.
25
+ # By default, Audited will audit all but these fields:
26
+ #
27
+ # [self.primary_key, inheritance_column, 'lock_version', 'created_at', 'updated_at']
28
+ # You can add to those by passing one or an array of fields to skip.
29
+ #
30
+ # class User < ActiveRecord::Base
31
+ # audited :except => :password
32
+ # end
33
+ # * +protect+ - If your model uses +attr_protected+, set this to false to prevent Rails from
34
+ # raising an error. If you declare +attr_accessible+ before calling +audited+, it
35
+ # will automatically default to false. You only need to explicitly set this if you are
36
+ # calling +attr_accessible+ after.
37
+ #
38
+ # * +require_comment+ - Ensures that audit_comment is supplied before
39
+ # any create, update or destroy operation.
40
+ #
41
+ # class User < ActiveRecord::Base
42
+ # audited :protect => false
43
+ # attr_accessible :name
44
+ # end
45
+ #
46
+ def audited(options = {})
47
+ # don't allow multiple calls
48
+ return if self.included_modules.include?(Audited::Auditor::AuditedInstanceMethods)
49
+
50
+ class_attribute :non_audited_columns, :instance_writer => false
51
+ class_attribute :auditing_enabled, :instance_writer => false
52
+ class_attribute :audit_associated_with, :instance_writer => false
53
+
54
+ if options[:only]
55
+ except = self.column_names - options[:only].flatten.map(&:to_s)
56
+ else
57
+ except = default_ignored_attributes + Audited.ignored_attributes
58
+ except |= Array(options[:except]).collect(&:to_s) if options[:except]
59
+ end
60
+ self.non_audited_columns = except
61
+ self.audit_associated_with = options[:associated_with]
62
+
63
+ if options[:comment_required]
64
+ validates_presence_of :audit_comment, :if => :auditing_enabled
65
+ before_destroy :require_comment
66
+ end
67
+
68
+ attr_accessor :audit_comment
69
+ unless options[:allow_mass_assignment]
70
+ attr_accessible :audit_comment
71
+ end
72
+
73
+ has_many :audits, :as => :auditable, :class_name => Audited.audit_class.name
74
+ Audited.audit_class.audited_class_names << self.to_s
75
+
76
+ after_create :audit_create if !options[:on] || (options[:on] && options[:on].include?(:create))
77
+ before_update :audit_update if !options[:on] || (options[:on] && options[:on].include?(:update))
78
+ before_destroy :audit_destroy if !options[:on] || (options[:on] && options[:on].include?(:destroy))
79
+
80
+ # Define and set an after_audit callback. This might be useful if you want
81
+ # to notify a party after the audit has been created.
82
+ define_callbacks :audit
83
+ set_callback :audit, :after, :after_audit, :if => lambda { self.respond_to?(:after_audit) }
84
+
85
+ attr_accessor :version
86
+
87
+ extend Audited::Auditor::AuditedClassMethods
88
+ include Audited::Auditor::AuditedInstanceMethods
89
+
90
+ self.auditing_enabled = true
91
+ end
92
+
93
+ def has_associated_audits
94
+ has_many :associated_audits, :as => :associated, :class_name => Audited.audit_class.name
95
+ end
96
+ end
97
+
98
+ module AuditedInstanceMethods
99
+ # Temporarily turns off auditing while saving.
100
+ def save_without_auditing
101
+ without_auditing { save }
102
+ end
103
+
104
+ # Executes the block with the auditing callbacks disabled.
105
+ #
106
+ # @foo.without_auditing do
107
+ # @foo.save
108
+ # end
109
+ #
110
+ def without_auditing(&block)
111
+ self.class.without_auditing(&block)
112
+ end
113
+
114
+ # Gets an array of the revisions available
115
+ #
116
+ # user.revisions.each do |revision|
117
+ # user.name
118
+ # user.version
119
+ # end
120
+ #
121
+ def revisions(from_version = 1)
122
+ audits = self.audits.from_version(from_version)
123
+ return [] if audits.empty?
124
+ revisions = []
125
+ audits.each do |audit|
126
+ revisions << audit.revision
127
+ end
128
+ revisions
129
+ end
130
+
131
+ # Get a specific revision specified by the version number, or +:previous+
132
+ def revision(version)
133
+ revision_with Audited.audit_class.reconstruct_attributes(audits_to(version))
134
+ end
135
+
136
+ # Find the oldest revision recorded prior to the date/time provided.
137
+ def revision_at(date_or_time)
138
+ audits = self.audits.up_until(date_or_time)
139
+ revision_with Audited.audit_class.reconstruct_attributes(audits) unless audits.empty?
140
+ end
141
+
142
+ # List of attributes that are audited.
143
+ def audited_attributes
144
+ attributes.except(*non_audited_columns)
145
+ end
146
+
147
+ protected
148
+
149
+ def revision_with(attributes)
150
+ self.dup.tap do |revision|
151
+ revision.id = id
152
+ revision.send :instance_variable_set, '@attributes', self.attributes
153
+ revision.send :instance_variable_set, '@new_record', self.destroyed?
154
+ revision.send :instance_variable_set, '@persisted', !self.destroyed?
155
+ revision.send :instance_variable_set, '@readonly', false
156
+ revision.send :instance_variable_set, '@destroyed', false
157
+ revision.send :instance_variable_set, '@_destroyed', false
158
+ revision.send :instance_variable_set, '@marked_for_destruction', false
159
+ Audited.audit_class.assign_revision_attributes(revision, attributes)
160
+
161
+ # Remove any association proxies so that they will be recreated
162
+ # and reference the correct object for this revision. The only way
163
+ # to determine if an instance variable is a proxy object is to
164
+ # see if it responds to certain methods, as it forwards almost
165
+ # everything to its target.
166
+ for ivar in revision.instance_variables
167
+ proxy = revision.instance_variable_get ivar
168
+ if !proxy.nil? and proxy.respond_to? :proxy_respond_to?
169
+ revision.instance_variable_set ivar, nil
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ private
176
+
177
+ def audited_changes
178
+ changed_attributes.except(*non_audited_columns).inject({}) do |changes,(attr, old_value)|
179
+ changes[attr] = [old_value, self[attr]]
180
+ changes
181
+ end
182
+ end
183
+
184
+ def audits_to(version = nil)
185
+ if version == :previous
186
+ version = if self.version
187
+ self.version - 1
188
+ else
189
+ previous = audits.descending.offset(1).first
190
+ previous ? previous.version : 1
191
+ end
192
+ end
193
+ audits.to_version(version)
194
+ end
195
+
196
+ def audit_create
197
+ write_audit(:action => 'create', :audited_changes => audited_attributes,
198
+ :comment => audit_comment)
199
+ end
200
+
201
+ def audit_update
202
+ unless (changes = audited_changes).empty? && audit_comment.blank?
203
+ write_audit(:action => 'update', :audited_changes => changes,
204
+ :comment => audit_comment)
205
+ end
206
+ end
207
+
208
+ def audit_destroy
209
+ write_audit(:action => 'destroy', :audited_changes => audited_attributes,
210
+ :comment => audit_comment)
211
+ end
212
+
213
+ def write_audit(attrs)
214
+ attrs[:associated] = self.send(audit_associated_with) unless audit_associated_with.nil?
215
+ self.audit_comment = nil
216
+ run_callbacks(:audit) { self.audits.create(attrs) } if auditing_enabled
217
+ end
218
+
219
+ def require_comment
220
+ if auditing_enabled && audit_comment.blank?
221
+ errors.add(:audit_comment, "Comment required before destruction")
222
+ return false
223
+ end
224
+ end
225
+
226
+ CALLBACKS.each do |attr_name|
227
+ alias_method "#{attr_name}_callback".to_sym, attr_name
228
+ end
229
+
230
+ def empty_callback #:nodoc:
231
+ end
232
+
233
+ end # InstanceMethods
234
+
235
+ module AuditedClassMethods
236
+ # Returns an array of columns that are audited. See non_audited_columns
237
+ def audited_columns
238
+ self.columns.select { |c| !non_audited_columns.include?(c.name) }
239
+ end
240
+
241
+ # Executes the block with auditing disabled.
242
+ #
243
+ # Foo.without_auditing do
244
+ # @foo.save
245
+ # end
246
+ #
247
+ def without_auditing(&block)
248
+ auditing_was_enabled = auditing_enabled
249
+ disable_auditing
250
+ block.call.tap { enable_auditing if auditing_was_enabled }
251
+ end
252
+
253
+ def disable_auditing
254
+ self.auditing_enabled = false
255
+ end
256
+
257
+ def enable_auditing
258
+ self.auditing_enabled = true
259
+ end
260
+
261
+ # All audit operations during the block are recorded as being
262
+ # made by +user+. This is not model specific, the method is a
263
+ # convenience wrapper around
264
+ # @see Audit#as_user.
265
+ def audit_as( user, &block )
266
+ Audited.audit_class.as_user( user, &block )
267
+ end
268
+ end
269
+ end
270
+ end