audited 4.2.2 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of audited might be problematic. Click here for more details.

Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +10 -9
  3. data/Appraisals +10 -6
  4. data/Gemfile +1 -13
  5. data/README.md +46 -33
  6. data/Rakefile +3 -18
  7. data/gemfiles/rails40.gemfile +1 -5
  8. data/gemfiles/rails41.gemfile +1 -5
  9. data/gemfiles/rails42.gemfile +1 -5
  10. data/gemfiles/rails50.gemfile +8 -0
  11. data/lib/audited-rspec.rb +4 -0
  12. data/lib/audited.rb +15 -2
  13. data/lib/audited/audit.rb +97 -57
  14. data/lib/audited/auditor.rb +73 -45
  15. data/lib/audited/rspec_matchers.rb +6 -2
  16. data/lib/audited/sweeper.rb +12 -23
  17. data/lib/audited/version.rb +1 -1
  18. data/lib/generators/audited/install_generator.rb +20 -0
  19. data/lib/generators/audited/migration.rb +15 -0
  20. data/lib/generators/audited/templates/add_association_to_audits.rb +11 -0
  21. data/lib/generators/audited/templates/add_comment_to_audits.rb +9 -0
  22. data/lib/generators/audited/templates/add_remote_address_to_audits.rb +10 -0
  23. data/lib/generators/audited/templates/add_request_uuid_to_audits.rb +10 -0
  24. data/lib/generators/audited/templates/install.rb +30 -0
  25. data/lib/generators/audited/templates/rename_association_to_associated.rb +23 -0
  26. data/lib/generators/audited/templates/rename_changes_to_audited_changes.rb +9 -0
  27. data/lib/generators/audited/templates/rename_parent_to_association.rb +11 -0
  28. data/lib/generators/audited/upgrade_generator.rb +57 -0
  29. data/spec/audited/audit_spec.rb +199 -0
  30. data/spec/audited/auditor_spec.rb +607 -0
  31. data/spec/audited/sweeper_spec.rb +106 -0
  32. data/spec/audited_spec_helpers.rb +6 -22
  33. data/spec/rails_app/config/environments/test.rb +7 -4
  34. data/spec/rails_app/config/initializers/secret_token.rb +1 -1
  35. data/spec/rails_app/config/routes.rb +1 -4
  36. data/spec/spec_helper.rb +7 -9
  37. data/spec/support/active_record/models.rb +20 -13
  38. data/spec/support/active_record/schema.rb +36 -12
  39. data/test/db/version_1.rb +4 -4
  40. data/test/db/version_2.rb +4 -4
  41. data/test/db/version_3.rb +4 -4
  42. data/test/db/version_4.rb +4 -4
  43. data/test/db/version_5.rb +2 -2
  44. data/test/db/version_6.rb +2 -2
  45. data/test/install_generator_test.rb +1 -1
  46. data/test/upgrade_generator_test.rb +10 -10
  47. metadata +73 -37
  48. data/lib/audited/active_record/version.rb +0 -5
  49. data/lib/audited/mongo_mapper/version.rb +0 -5
  50. data/spec/support/mongo_mapper/connection.rb +0 -4
  51. data/spec/support/mongo_mapper/models.rb +0 -214
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b493b49e84fe7edd29005249133f20334596e2e8
4
- data.tar.gz: 84c47a86e7fb39785ae343825abde3319f1b1fff
3
+ metadata.gz: b04e8c93cc4b6351654924948b663708433e8a01
4
+ data.tar.gz: ce147cca64fde0e62e06bd7235851a907ec4ccb5
5
5
  SHA512:
6
- metadata.gz: 838eae030f0694a67ffaa526ada7b090e9a6d2e424aa1a12e72b43d8135870f43454a28bbe5d529feb40921112ea841f0e72e5763e537479768314c9c0386d2e
7
- data.tar.gz: 1b74fcc9292dc3a85064ad6ad13a4689bbb79186e57fbbafe4a45ffaf1ceb8ab625a364abb60735f6177c3d0c53354156a5b960e2f8cab7ac3a70f42e5ef6f8b
6
+ metadata.gz: f9fc970bef11b25088cc77123857efad07fb319b84a82696e6f195cce0b9cb1c90cac1a3b0330e57143a61b8a38d9973b13a9618421adeebae030f8f483a9f42
7
+ data.tar.gz: a7841493a1889185d35c323287d98fd9ca332a91fa18f5d751fcff77508baafb5a2e1a5056eeeb1d6e5fd8efe3d9f13aa407403c85038303642dbd631c7817f9
@@ -1,28 +1,29 @@
1
1
  language: ruby
2
- services: mongodb
2
+ cache: bundler
3
3
  rvm:
4
- - 2.0
5
4
  - 2.1
6
- - 2.2
7
- - jruby-head
5
+ - 2.2.4
6
+ - 2.3.1
7
+ - ruby-head
8
8
  env:
9
9
  - DB=SQLITE
10
10
  - DB=POSTGRES
11
11
  - DB=MYSQL
12
- before_script:
13
- - mysql -e 'create database audited_test;'
14
- - psql -c 'create database audited_test;' -U postgres
15
12
  gemfile:
16
13
  - gemfiles/rails40.gemfile
17
14
  - gemfiles/rails41.gemfile
18
15
  - gemfiles/rails42.gemfile
16
+ - gemfiles/rails50.gemfile
19
17
  matrix:
20
18
  allow_failures:
21
- - rvm: jruby-head
19
+ - rvm: ruby-head
20
+ exclude:
21
+ - rvm: 2.1
22
+ gemfile: gemfiles/rails50.gemfile
23
+ fast_finish: true
22
24
  branches:
23
25
  only:
24
26
  - master
25
- - 4.2-stable
26
27
  sudo: false
27
28
  notifications:
28
29
  webhooks:
data/Appraisals CHANGED
@@ -1,18 +1,22 @@
1
1
  appraise 'rails40' do
2
2
  gem 'rails', '~> 4.0.0'
3
- gem 'rails-observers'
3
+ gem 'protected_attributes'
4
4
  gem 'test-unit'
5
- gem 'mysql2', '~> 0.3.0'
6
5
  end
7
6
 
8
7
  appraise 'rails41' do
9
8
  gem 'rails', '~> 4.1.0'
10
- gem 'rails-observers'
11
- gem 'mysql2', '~> 0.3.0'
9
+ gem 'protected_attributes'
12
10
  end
13
11
 
14
12
  appraise 'rails42' do
15
13
  gem 'rails', '~> 4.2.0'
16
- gem 'rails-observers'
17
- gem 'mysql2', '~> 0.4.0'
14
+ gem 'protected_attributes'
15
+ end
16
+
17
+ appraise 'rails50' do
18
+ gem 'rails', '~> 5.0.0'
19
+
20
+ # The following needs to point to Github until the release of 0.1.3
21
+ gem 'rails-observers', github: 'rails/rails-observers', branch: 'master'
18
22
  end
data/Gemfile CHANGED
@@ -1,15 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gemspec :name => 'audited'
4
- # JRuby support for the test ENV
5
- unless defined?(JRUBY_VERSION)
6
- gem 'sqlite3', '~> 1.2'
7
- gem 'mysql2', '~> 0.3'
8
- gem 'pg', '~> 0.17'
9
- gem 'bson_ext', '~> 1.6'
10
- else
11
- gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3'
12
- gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3'
13
- gem 'activerecord-jdbcmysql-adapter', '~> 1.3'
14
- gem 'bson', '~> 1.6'
15
- end
3
+ gemspec name: "audited"
data/README.md CHANGED
@@ -1,37 +1,37 @@
1
- Audited [![Build Status](https://secure.travis-ci.org/collectiveidea/audited.png)](http://travis-ci.org/collectiveidea/audited) [![Dependency Status](https://gemnasium.com/collectiveidea/audited.png)](https://gemnasium.com/collectiveidea/audited)[![Code Climate](https://codeclimate.com/github/collectiveidea/audited.png)](https://codeclimate.com/github/collectiveidea/audited)
1
+ Audited [![Build Status](https://secure.travis-ci.org/collectiveidea/audited.svg)](http://travis-ci.org/collectiveidea/audited) [![Dependency Status](https://gemnasium.com/collectiveidea/audited.svg)](https://gemnasium.com/collectiveidea/audited)[![Code Climate](https://codeclimate.com/github/collectiveidea/audited.svg)](https://codeclimate.com/github/collectiveidea/audited) [![Security](https://hakiri.io/github/collectiveidea/audited/master.svg)](https://hakiri.io/github/collectiveidea/audited/master)
2
2
  =======
3
3
 
4
- **Audited** (previously acts_as_audited) is an ORM extension that logs all changes to your models. Audited also allows you to record who made those changes, save comments and associate models related to the changes.
4
+ **Audited** (previously acts_as_audited) is an ORM extension that logs all changes to your models. Audited can also record who made those changes, save comments and associate models related to the changes.
5
5
 
6
- Audited currently (4.x) works with Rails 4.2. For Rails 3, use gem version 3.0 or see the [3.0-stable branch](https://github.com/collectiveidea/audited/tree/3.0-stable).
6
+ Audited currently (4.x) works with Rails 5.0 and 4.2. It may work with 4.1 and 4.0, but this is not guaranteed.
7
+
8
+ For Rails 3, use gem version 3.0 or see the [3.0-stable branch](https://github.com/collectiveidea/audited/tree/3.0-stable).
7
9
 
8
10
  ## Supported Rubies
9
11
 
10
12
  Audited supports and is [tested against](http://travis-ci.org/collectiveidea/audited) the following Ruby versions:
11
13
 
12
- * 2.0.0
13
14
  * 2.1.5
14
- * 2.2.0
15
+ * 2.2.4
16
+ * 2.3.1
15
17
 
16
18
  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).
17
19
 
18
20
  ## Supported ORMs
19
21
 
20
- In a previous life, Audited was ActiveRecord-only. Audited will now audit models for the following backends:
21
-
22
- * ActiveRecord
23
- * MongoMapper
22
+ Audited is currently ActiveRecord-only. In a previous life, Audited worked with MongoMapper. Use the [4.2-stable branch](https://github.com/collectiveidea/audited/tree/4.2-stable) if you need MongoMapper.
24
23
 
25
24
  ## Installation
26
25
 
27
- The installation process depends on what ORM your app is using.
28
-
29
- ### ActiveRecord
26
+ Add the gem to your Gemfile:
30
27
 
31
- Add the appropriate gem to your Gemfile:
28
+ ```ruby
29
+ gem "audited", "~> 4.3"
30
+ ```
32
31
 
32
+ If you are using rails 5.0, you would also need the following line in your Gemfile.
33
33
  ```ruby
34
- gem "audited-activerecord", "~> 4.0"
34
+ gem "rails-observers", github: 'rails/rails-observers'
35
35
  ```
36
36
 
37
37
  Then, from your Rails app directory, create the `audits` table:
@@ -52,11 +52,6 @@ $ rake db:migrate
52
52
 
53
53
  Upgrading will only make changes if changes are needed.
54
54
 
55
- ### MongoMapper
56
-
57
- ```ruby
58
- gem "audited-mongo_mapper", "~> 4.0"
59
- ```
60
55
 
61
56
  ## Usage
62
57
 
@@ -88,6 +83,15 @@ audit.action # => "update"
88
83
  audit.audited_changes # => {"name"=>["Steve", "Ryan"]}
89
84
  ```
90
85
 
86
+ You can get previous versions of a record by index or date, or list all
87
+ revisions.
88
+
89
+ ```ruby
90
+ user.revisions
91
+ user.revision(1)
92
+ user.revision_at(Date.parse("2016-01-01"))
93
+ ```
94
+
91
95
  ### Specifying columns
92
96
 
93
97
  By default, a new audit is created for any attribute changes. You can, however, limit the columns to be considered.
@@ -143,7 +147,7 @@ end
143
147
 
144
148
  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.
145
149
 
146
- ```
150
+ ```ruby
147
151
  class PostsController < ApplicationController
148
152
  def create
149
153
  current_user # => #<User name: "Steve">
@@ -162,12 +166,28 @@ Audited.current_user_method = :authenticated_user
162
166
  Outside of a request, Audited can still record the user with the `as_user` method:
163
167
 
164
168
  ```ruby
165
- Audited.audit_class.as_user(User.find(1)) do
169
+ Audited::Audit.as_user(User.find(1)) do
166
170
  post.update_attribute!(title: "Hello, world!")
167
171
  end
168
172
  post.audits.last.user # => #<User id: 1>
169
173
  ```
170
174
 
175
+ #### Custom Auditor
176
+
177
+ You might need to use a custom auditor from time to time. It can be done by simply passing in a string:
178
+
179
+ ```ruby
180
+ class ApplicationController < ActionController::Base
181
+ def authenticated_user
182
+ if current_user
183
+ current_user
184
+ else
185
+ 'Elon Musk'
186
+ end
187
+ end
188
+ end
189
+ ```
190
+
171
191
  ### Associated Audits
172
192
 
173
193
  Sometimes it's useful to associate an audit with a model other than the one being changed. For instance, given the following models:
@@ -240,15 +260,12 @@ User.auditing_enabled = false
240
260
 
241
261
  ## Gotchas
242
262
 
243
- ### Using attr_protected or strong_parameters
263
+ ### Using attr_protected with Rails 4.x
244
264
 
245
- Audited assumes you are using `attr_accessible`. If you're using
246
- `attr_protected` or `strong_parameters`, you'll have to take an extra step or
247
- two.
265
+ If you're using the `protected_attributes` gem with Rails 4.0, 4.1 or 4.2 (the gem isn't supported in Rails 5.0 or higher), you'll have to take an extra couple of steps to get `audited` working.
248
266
 
249
-
250
- If you're using `strong_parameters` with Rails 3.x, be sure to add `allow_mass_assignment: true` to your `audited` call; otherwise Audited will
251
- interfere with `strong_parameters` and none of your `save` calls will work.
267
+ First be sure to add `allow_mass_assignment: true` to your `audited` call; otherwise Audited will
268
+ interfere with `protected_attributes` and none of your `save` calls will work.
252
269
 
253
270
  ```ruby
254
271
  class User < ActiveRecord::Base
@@ -256,7 +273,7 @@ class User < ActiveRecord::Base
256
273
  end
257
274
  ```
258
275
 
259
- If using `attr_protected`, add `allow_mass_assignment: true`, and also be sure to add `audit_ids` to the list of protected attributes to prevent data loss.
276
+ Second, be sure to add `audit_ids` to the list of protected attributes to prevent data loss.
260
277
 
261
278
  ```ruby
262
279
  class User < ActiveRecord::Base
@@ -265,10 +282,6 @@ class User < ActiveRecord::Base
265
282
  end
266
283
  ```
267
284
 
268
- ### MongoMapper Embedded Documents
269
-
270
- Currently, Audited does not track changes on embedded documents. Audited works by tracking a model's [dirty changes](http://api.rubyonrails.org/classes/ActiveModel/Dirty.html) but changes to embedded documents don't appear in dirty tracking.
271
-
272
285
  ## Support
273
286
 
274
287
  You can find documentation at: http://rdoc.info/github/collectiveidea/audited
data/Rakefile CHANGED
@@ -5,24 +5,9 @@ require 'rspec/core/rake_task'
5
5
  require 'rake/testtask'
6
6
  require 'appraisal'
7
7
 
8
- Bundler::GemHelper.install_tasks(:name => 'audited')
9
- Bundler::GemHelper.install_tasks(:name => 'audited-activerecord')
10
- Bundler::GemHelper.install_tasks(:name => 'audited-mongo_mapper')
8
+ Bundler::GemHelper.install_tasks(name: 'audited')
11
9
 
12
- ADAPTERS = %w(active_record mongo_mapper)
13
-
14
- ADAPTERS.each do |adapter|
15
- desc "Run RSpec code examples for #{adapter} adapter"
16
- RSpec::Core::RakeTask.new(adapter) do |t|
17
- t.pattern = "spec/audited/adapters/#{adapter}/**/*_spec.rb"
18
- end
19
- end
20
-
21
- task :spec do
22
- ADAPTERS.each do |adapter|
23
- Rake::Task[adapter].invoke
24
- end
25
- end
10
+ RSpec::Core::RakeTask.new(:spec)
26
11
 
27
12
  Rake::TestTask.new do |t|
28
13
  t.libs << "test"
@@ -30,4 +15,4 @@ Rake::TestTask.new do |t|
30
15
  t.verbose = true
31
16
  end
32
17
 
33
- task :default => [:spec, :test]
18
+ task default: [:spec, :test]
@@ -2,12 +2,8 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", "~> 1.2"
6
- gem "mysql2", "~> 0.3.0"
7
- gem "pg", "~> 0.17"
8
- gem "bson_ext", "~> 1.6"
9
5
  gem "rails", "~> 4.0.0"
10
- gem "rails-observers"
6
+ gem "protected_attributes"
11
7
  gem "test-unit"
12
8
 
13
9
  gemspec :name => "audited", :path => "../"
@@ -2,11 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", "~> 1.2"
6
- gem "mysql2", "~> 0.3.0"
7
- gem "pg", "~> 0.17"
8
- gem "bson_ext", "~> 1.6"
9
5
  gem "rails", "~> 4.1.0"
10
- gem "rails-observers"
6
+ gem "protected_attributes"
11
7
 
12
8
  gemspec :name => "audited", :path => "../"
@@ -2,11 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", "~> 1.2"
6
- gem "mysql2", "~> 0.4.0"
7
- gem "pg", "~> 0.17"
8
- gem "bson_ext", "~> 1.6"
9
5
  gem "rails", "~> 4.2.0"
10
- gem "rails-observers"
6
+ gem "protected_attributes"
11
7
 
12
8
  gemspec :name => "audited", :path => "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 5.0.0"
6
+ gem "rails-observers", :github => "rails/rails-observers", :branch => "master"
7
+
8
+ gemspec :name => "audited", :path => "../"
@@ -0,0 +1,4 @@
1
+ require 'audited/rspec_matchers'
2
+ module RSpec::Matchers
3
+ include Audited::RspecMatchers
4
+ end
@@ -1,9 +1,15 @@
1
1
  require 'rails/observers/active_model/active_model'
2
-
2
+ require 'active_record'
3
3
 
4
4
  module Audited
5
5
  class << self
6
- attr_accessor :ignored_attributes, :current_user_method, :audit_class
6
+ attr_accessor :ignored_attributes, :current_user_method
7
+
8
+ # Deprecate audit_class accessors in preperation of their removal
9
+ def audit_class
10
+ Audited::Audit
11
+ end
12
+ deprecate audit_class: "Audited.audit_class is now always Audited::Audit. This method will be removed."
7
13
 
8
14
  def store
9
15
  Thread.current[:audited_store] ||= {}
@@ -14,3 +20,10 @@ module Audited
14
20
 
15
21
  @current_user_method = :current_user
16
22
  end
23
+
24
+ require 'audited/auditor'
25
+ require 'audited/audit'
26
+
27
+ ::ActiveRecord::Base.send :include, Audited::Auditor
28
+
29
+ require 'audited/sweeper'
@@ -1,60 +1,44 @@
1
- module Audited
2
- module Audit
3
- def self.included(klass)
4
- klass.extend(ClassMethods)
5
- klass.setup_audit
6
- end
1
+ require 'set'
7
2
 
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
3
+ module Audited
4
+ # Audit saves the changes to ActiveRecord models. It has the following attributes:
5
+ #
6
+ # * <tt>auditable</tt>: the ActiveRecord model that was changed
7
+ # * <tt>user</tt>: the user that performed the change; a string or an ActiveRecord model
8
+ # * <tt>action</tt>: one of create, update, or delete
9
+ # * <tt>audited_changes</tt>: a serialized hash of all the changes
10
+ # * <tt>comment</tt>: a comment set with the audit
11
+ # * <tt>version</tt>: the version of the model
12
+ # * <tt>request_uuid</tt>: a uuid based that allows audits from the same controller request
13
+ # * <tt>created_at</tt>: Time that the change was performed
14
+ #
15
+ class Audit < ::ActiveRecord::Base
16
+ include ActiveModel::Observing
13
17
 
14
- before_create :set_version_number, :set_audit_user, :set_request_uuid
18
+ belongs_to :auditable, polymorphic: true
19
+ belongs_to :user, polymorphic: true
20
+ belongs_to :associated, polymorphic: true
15
21
 
16
- cattr_accessor :audited_class_names
17
- self.audited_class_names = Set.new
18
- end
22
+ before_create :set_version_number, :set_audit_user, :set_request_uuid
19
23
 
20
- # Returns the list of classes that are being audited
21
- def audited_classes
22
- audited_class_names.map(&:constantize)
23
- end
24
-
25
- # All audits made during the block called will be recorded as made
26
- # by +user+. This method is hopefully threadsafe, making it ideal
27
- # for background operations that require audit information.
28
- def as_user(user, &block)
29
- Thread.current[:audited_user] = user
30
- yield
31
- ensure
32
- Thread.current[:audited_user] = nil
33
- end
24
+ cattr_accessor :audited_class_names
25
+ self.audited_class_names = Set.new
34
26
 
35
- # @private
36
- def reconstruct_attributes(audits)
37
- attributes = {}
38
- result = audits.collect do |audit|
39
- attributes.merge!(audit.new_attributes).merge!(:version => audit.version)
40
- yield attributes if block_given?
41
- end
42
- block_given? ? result : attributes
43
- end
27
+ serialize :audited_changes
44
28
 
45
- # @private
46
- def assign_revision_attributes(record, attributes)
47
- attributes.each do |attr, val|
48
- record = record.dup if record.frozen?
29
+ scope :ascending, ->{ reorder(version: :asc) }
30
+ scope :descending, ->{ reorder(version: :desc)}
31
+ scope :creates, ->{ where(action: 'create')}
32
+ scope :updates, ->{ where(action: 'update')}
33
+ scope :destroys, ->{ where(action: 'destroy')}
49
34
 
50
- if record.respond_to?("#{attr}=")
51
- record.attributes.has_key?(attr.to_s) ?
52
- record[attr] = val :
53
- record.send("#{attr}=", val)
54
- end
55
- end
56
- record
57
- end
35
+ scope :up_until, ->(date_or_time){ where("created_at <= ?", date_or_time) }
36
+ scope :from_version, ->(version){ where('version >= ?', version) }
37
+ scope :to_version, ->(version){ where('version <= ?', version) }
38
+ scope :auditable_finder, ->(auditable_id, auditable_type){ where(auditable_id: auditable_id, auditable_type: auditable_type)}
39
+ # Return all audits older than the current one.
40
+ def ancestors
41
+ self.class.ascending.auditable_finder(auditable_id, auditable_type).to_version(version)
58
42
  end
59
43
 
60
44
  # Return an instance of what the object looked like at this revision. If
@@ -62,13 +46,13 @@ module Audited
62
46
  def revision
63
47
  clazz = auditable_type.constantize
64
48
  (clazz.find_by_id(auditable_id) || clazz.new).tap do |m|
65
- self.class.assign_revision_attributes(m, self.class.reconstruct_attributes(ancestors).merge({ :version => version }))
49
+ self.class.assign_revision_attributes(m, self.class.reconstruct_attributes(ancestors).merge(version: version))
66
50
  end
67
51
  end
68
52
 
69
53
  # Returns a hash of the changed attributes with the new values
70
54
  def new_attributes
71
- (audited_changes || {}).inject({}.with_indifferent_access) do |attrs,(attr,values)|
55
+ (audited_changes || {}).inject({}.with_indifferent_access) do |attrs, (attr, values)|
72
56
  attrs[attr] = values.is_a?(Array) ? values.last : values
73
57
  attrs
74
58
  end
@@ -76,19 +60,75 @@ module Audited
76
60
 
77
61
  # Returns a hash of the changed attributes with the old values
78
62
  def old_attributes
79
- (audited_changes || {}).inject({}.with_indifferent_access) do |attrs,(attr,values)|
63
+ (audited_changes || {}).inject({}.with_indifferent_access) do |attrs, (attr, values)|
80
64
  attrs[attr] = Array(values).first
81
65
 
82
66
  attrs
83
67
  end
84
68
  end
85
69
 
70
+ # Allows user to be set to either a string or an ActiveRecord object
71
+ # @private
72
+ def user_as_string=(user)
73
+ # reset both either way
74
+ self.user_as_model = self.username = nil
75
+ user.is_a?(::ActiveRecord::Base) ?
76
+ self.user_as_model = user :
77
+ self.username = user
78
+ end
79
+ alias_method :user_as_model=, :user=
80
+ alias_method :user=, :user_as_string=
81
+
82
+ # @private
83
+ def user_as_string
84
+ user_as_model || username
85
+ end
86
+ alias_method :user_as_model, :user
87
+ alias_method :user, :user_as_string
88
+
89
+ # Returns the list of classes that are being audited
90
+ def self.audited_classes
91
+ audited_class_names.map(&:constantize)
92
+ end
93
+
94
+ # All audits made during the block called will be recorded as made
95
+ # by +user+. This method is hopefully threadsafe, making it ideal
96
+ # for background operations that require audit information.
97
+ def self.as_user(user, &block)
98
+ Thread.current[:audited_user] = user
99
+ yield
100
+ ensure
101
+ Thread.current[:audited_user] = nil
102
+ end
103
+
104
+ # @private
105
+ def self.reconstruct_attributes(audits)
106
+ attributes = {}
107
+ result = audits.collect do |audit|
108
+ attributes.merge!(audit.new_attributes)[:version] = audit.version
109
+ yield attributes if block_given?
110
+ end
111
+ block_given? ? result : attributes
112
+ end
113
+
114
+ # @private
115
+ def self.assign_revision_attributes(record, attributes)
116
+ attributes.each do |attr, val|
117
+ record = record.dup if record.frozen?
118
+
119
+ if record.respond_to?("#{attr}=")
120
+ record.attributes.key?(attr.to_s) ?
121
+ record[attr] = val :
122
+ record.send("#{attr}=", val)
123
+ end
124
+ end
125
+ record
126
+ end
127
+
86
128
  private
129
+
87
130
  def set_version_number
88
- max = self.class.where(
89
- :auditable_id => auditable_id,
90
- :auditable_type => auditable_type
91
- ).order(:version.desc).first.try(:version) || 0
131
+ max = self.class.auditable_finder(auditable_id, auditable_type).descending.first.try(:version) || 0
92
132
  self.version = max + 1
93
133
  end
94
134