audited 4.3.0 → 4.4.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +10 -0
  3. data/Appraisals +3 -2
  4. data/CHANGELOG.md +190 -0
  5. data/README.md +27 -9
  6. data/gemfiles/rails50.gemfile +0 -1
  7. data/gemfiles/rails51.gemfile +7 -0
  8. data/lib/audited.rb +6 -5
  9. data/lib/audited/audit.rb +38 -8
  10. data/lib/audited/auditor.rb +11 -7
  11. data/lib/audited/rspec_matchers.rb +1 -1
  12. data/lib/audited/sweeper.rb +18 -29
  13. data/lib/audited/version.rb +1 -1
  14. data/lib/generators/audited/install_generator.rb +4 -0
  15. data/lib/generators/audited/migration_helper.rb +9 -0
  16. data/lib/generators/audited/templates/add_association_to_audits.rb +1 -1
  17. data/lib/generators/audited/templates/add_comment_to_audits.rb +1 -1
  18. data/lib/generators/audited/templates/add_remote_address_to_audits.rb +1 -1
  19. data/lib/generators/audited/templates/add_request_uuid_to_audits.rb +1 -1
  20. data/lib/generators/audited/templates/install.rb +2 -2
  21. data/lib/generators/audited/templates/rename_association_to_associated.rb +1 -1
  22. data/lib/generators/audited/templates/rename_changes_to_audited_changes.rb +1 -1
  23. data/lib/generators/audited/templates/rename_parent_to_association.rb +1 -1
  24. data/lib/generators/audited/upgrade_generator.rb +2 -0
  25. data/spec/audited/audit_spec.rb +49 -2
  26. data/spec/audited/auditor_spec.rb +43 -2
  27. data/spec/audited/sweeper_spec.rb +8 -6
  28. data/spec/support/active_record/models.rb +1 -0
  29. data/spec/support/active_record/postgres/1_change_audited_changes_type_to_json.rb +12 -0
  30. data/spec/support/active_record/postgres/2_change_audited_changes_type_to_jsonb.rb +12 -0
  31. data/test/install_generator_test.rb +31 -3
  32. data/test/upgrade_generator_test.rb +16 -1
  33. metadata +7 -17
  34. data/CHANGELOG +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b04e8c93cc4b6351654924948b663708433e8a01
4
- data.tar.gz: ce147cca64fde0e62e06bd7235851a907ec4ccb5
3
+ metadata.gz: b999065ad92a0fbe08d0739e44dfe966d35ee57f
4
+ data.tar.gz: 258e418fcfaf0b839c209435dda7ab509c25b52b
5
5
  SHA512:
6
- metadata.gz: f9fc970bef11b25088cc77123857efad07fb319b84a82696e6f195cce0b9cb1c90cac1a3b0330e57143a61b8a38d9973b13a9618421adeebae030f8f483a9f42
7
- data.tar.gz: a7841493a1889185d35c323287d98fd9ca332a91fa18f5d751fcff77508baafb5a2e1a5056eeeb1d6e5fd8efe3d9f13aa407403c85038303642dbd631c7817f9
6
+ metadata.gz: acb5cb634042722106e7b120791723d879677992628572c830c03d2594e472857127d09714ed6d61cd3aaec635b82e4396441d94a900556e01bc795ad1ed05a1
7
+ data.tar.gz: 41fff32c9c1b060f040007a038d2840c6e7c43f528dbc9924d3ac1727fee692c10e959bc43bbe2c25882c2cfad9f69b54d8bb21836a3786a1c11928ed10a5770
@@ -4,22 +4,32 @@ rvm:
4
4
  - 2.1
5
5
  - 2.2.4
6
6
  - 2.3.1
7
+ - 2.4.1
7
8
  - ruby-head
8
9
  env:
9
10
  - DB=SQLITE
10
11
  - DB=POSTGRES
11
12
  - DB=MYSQL
13
+ addons:
14
+ postgresql: "9.4"
12
15
  gemfile:
13
16
  - gemfiles/rails40.gemfile
14
17
  - gemfiles/rails41.gemfile
15
18
  - gemfiles/rails42.gemfile
16
19
  - gemfiles/rails50.gemfile
20
+ - gemfiles/rails51.gemfile
17
21
  matrix:
18
22
  allow_failures:
19
23
  - rvm: ruby-head
20
24
  exclude:
21
25
  - rvm: 2.1
22
26
  gemfile: gemfiles/rails50.gemfile
27
+ - rvm: 2.1
28
+ gemfile: gemfiles/rails51.gemfile
29
+ - rvm: 2.4.1
30
+ gemfile: gemfiles/rails40.gemfile
31
+ - rvm: 2.4.1
32
+ gemfile: gemfiles/rails41.gemfile
23
33
  fast_finish: true
24
34
  branches:
25
35
  only:
data/Appraisals CHANGED
@@ -16,7 +16,8 @@ end
16
16
 
17
17
  appraise 'rails50' do
18
18
  gem 'rails', '~> 5.0.0'
19
+ end
19
20
 
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'
21
+ appraise 'rails51' do
22
+ gem 'rails', '>= 5.1.0.rc1', '< 5.2'
22
23
  end
@@ -0,0 +1,190 @@
1
+ # Audited ChangeLog
2
+
3
+ ## Unreleased
4
+
5
+ Breaking changes
6
+
7
+ - None
8
+
9
+ Added
10
+
11
+ - None
12
+
13
+ Changed
14
+
15
+ - None
16
+
17
+ Fixed
18
+
19
+ - None
20
+
21
+ ## 4.4.0 (2017-03-29)
22
+
23
+ Breaking changes
24
+
25
+ - None
26
+
27
+ Added
28
+
29
+ - Support for `audited_changes` to be a `json` or `jsonb` column in PostgreSQL
30
+ [#216](https://github.com/collectiveidea/audited/issues/216)
31
+ - Allow `Audited::Audit` to be subclassed by configuring `Audited.audit_class`
32
+ [#314](https://github.com/collectiveidea/audited/issues/314)
33
+ - Support for Ruby on Rails 5.1
34
+ [#329](https://github.com/collectiveidea/audited/issues/329)
35
+ - Support for Ruby 2.4
36
+ [#329](https://github.com/collectiveidea/audited/issues/329)
37
+
38
+ Changed
39
+
40
+ - Remove rails-observer dependency
41
+ [#325](https://github.com/collectiveidea/audited/issues/325)
42
+ - Undeprecated `Audited.audit_class` reader
43
+ [#314](https://github.com/collectiveidea/audited/issues/314)
44
+
45
+ Fixed
46
+
47
+ - SQL error in Rails Conditional GET (304 caching)
48
+ [#295](https://github.com/collectiveidea/audited/pull/295)
49
+ - Fix missing non_audited_columns= configuration setter
50
+ [#320](https://github.com/collectiveidea/audited/issues/320)
51
+ - Fix migration generators to specify AR migration version
52
+ [#329](https://github.com/collectiveidea/audited/issues/329)
53
+
54
+ ## 4.3.0 (2016-09-17)
55
+
56
+ Breaking changes
57
+
58
+ - None
59
+
60
+ Added
61
+
62
+ - Support singular arguments for options: `on` and `only`
63
+
64
+ Fixed
65
+
66
+ - Fix auditing instance attributes if "only" option specified
67
+ - Allow private / protected callback declarations
68
+ - Do not eagerly connect to database
69
+
70
+ ## 4.2.2 (2016-08-01)
71
+
72
+ - Correct auditing_enabled for STI models
73
+ - Properly set table name for mongomapper
74
+
75
+ ## 4.2.1 (2016-07-29)
76
+
77
+ - Fix bug when only: is a single field.
78
+ - update gemspec to use mongomapper 0.13
79
+ - sweeper need not run observer for mongomapper
80
+ - Make temporary disabling of auditing threadsafe
81
+ - Centralize `Audited.store` as thread safe variable store
82
+
83
+ ## 4.2.0 (2015-03-31)
84
+
85
+ Not yet documented.
86
+
87
+ ## 4.0.0 (2014-09-04)
88
+
89
+ Not yet documented.
90
+
91
+ ## 4.0.0.rc1 (2014-07-30)
92
+
93
+ Not yet documented.
94
+
95
+ ## 3.0.0 (2012-09-25)
96
+
97
+ Not yet documented.
98
+
99
+ ## 3.0.0.rc2 (2012-07-09)
100
+
101
+ Not yet documented.
102
+
103
+ ## 3.0.0.rc1 (2012-04-25)
104
+
105
+ Not yet documented.
106
+
107
+ ## 2012-04-10
108
+
109
+ - Add Audit scopes for creates, updates and destroys [chriswfx]
110
+
111
+ ## 2011-10-25
112
+
113
+ - Made ignored_attributes configurable [senny]
114
+
115
+ ## 2011-09-09
116
+
117
+ - Rails 3.x support
118
+ - Support for associated audits
119
+ - Support for remote IP address storage
120
+ - Plenty of bug fixes and refactoring
121
+ - [kennethkalmer, ineu, PatrickMa, jrozner, dwarburton, bsiggelkow, dgm]
122
+
123
+ ## 2009-01-27
124
+
125
+ - Store old and new values for updates, and store all attributes on destroy.
126
+ - Refactored revisioning methods to work as expected
127
+
128
+ ## 2008-10-10
129
+
130
+ - changed to make it work in development mode
131
+
132
+ ## 2008-09-24
133
+
134
+ - Add ability to record parent record of the record being audited [Kenneth Kalmer]
135
+
136
+ ## 2008-04-19
137
+
138
+ - refactored to make compatible with dirty tracking in edge rails
139
+ and to stop storing both old and new values in a single audit
140
+
141
+ ## 2008-04-18
142
+
143
+ - Fix NoMethodError when trying to access the :previous revision
144
+ on a model that doesn't have previous revisions [Alex Soto]
145
+
146
+ ## 2008-03-21
147
+
148
+ - added #changed_attributes to get access to the changes before a
149
+ save [Chris Parker]
150
+
151
+ ## 2007-12-16
152
+
153
+ - Added #revision_at for retrieving a revision from a specific
154
+ time [Jacob Atzen]
155
+
156
+ ## 2007-12-16
157
+
158
+ - Fix error when getting revision from audit with no changes
159
+ [Geoffrey Wiseman]
160
+
161
+ ## 2007-12-16
162
+
163
+ - Remove dependency on acts_as_list
164
+
165
+ ## 2007-06-17
166
+
167
+ - Added support getting previous revisions
168
+
169
+ ## 2006-11-17
170
+
171
+ - Replaced use of singleton User.current_user with cache sweeper
172
+ implementation for auditing the user that made the change
173
+
174
+ ## 2006-11-17
175
+
176
+ - added migration generator
177
+
178
+ ## 2006-08-14
179
+
180
+ - incorporated changes from Michael Schuerig to write_attribute
181
+ that saves the new value after every change and not just the
182
+ first, and performs proper type-casting before doing comparisons
183
+
184
+ ## 2006-08-14
185
+
186
+ - The "changes" are now saved as a serialized hash
187
+
188
+ ## 2006-07-21
189
+
190
+ - initial version
data/README.md CHANGED
@@ -3,7 +3,7 @@ Audited [![Build Status](https://secure.travis-ci.org/collectiveidea/audited.svg
3
3
 
4
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 5.0 and 4.2. It may work with 4.1 and 4.0, but this is not guaranteed.
6
+ Audited currently (4.x) works with Rails 5.1, 5.0 and 4.2. It may work with 4.1 and 4.0, but this is not guaranteed.
7
7
 
8
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).
9
9
 
@@ -14,6 +14,7 @@ Audited supports and is [tested against](http://travis-ci.org/collectiveidea/aud
14
14
  * 2.1.5
15
15
  * 2.2.4
16
16
  * 2.3.1
17
+ * 2.4.1
17
18
 
18
19
  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).
19
20
 
@@ -26,12 +27,7 @@ Audited is currently ActiveRecord-only. In a previous life, Audited worked with
26
27
  Add the gem to your Gemfile:
27
28
 
28
29
  ```ruby
29
- gem "audited", "~> 4.3"
30
- ```
31
-
32
- If you are using rails 5.0, you would also need the following line in your Gemfile.
33
- ```ruby
34
- gem "rails-observers", github: 'rails/rails-observers'
30
+ gem "audited", "~> 4.4"
35
31
  ```
36
32
 
37
33
  Then, from your Rails app directory, create the `audits` table:
@@ -41,6 +37,8 @@ $ rails generate audited:install
41
37
  $ rake db:migrate
42
38
  ```
43
39
 
40
+ If you're using PostgreSQL, then you can use `rails generate audited:install --audited-changes-column-type jsonb` (or `json`) to store audit changes natively with its JSON column types.
41
+
44
42
  #### Upgrading
45
43
 
46
44
  If you're already using Audited (or acts_as_audited), your `audits` table may require additional columns. After every upgrade, please run:
@@ -166,7 +164,7 @@ Audited.current_user_method = :authenticated_user
166
164
  Outside of a request, Audited can still record the user with the `as_user` method:
167
165
 
168
166
  ```ruby
169
- Audited::Audit.as_user(User.find(1)) do
167
+ Audited.audit_class.as_user(User.find(1)) do
170
168
  post.update_attribute!(title: "Hello, world!")
171
169
  end
172
170
  post.audits.last.user # => #<User id: 1>
@@ -217,7 +215,7 @@ class Company < ActiveRecord::Base
217
215
  end
218
216
  ```
219
217
 
220
- Now, when a audit is created for a user, that user's company is also saved alongside the audit. This makes it much easier (and faster) to access audits indirectly related to a company.
218
+ Now, when an audit is created for a user, that user's company is also saved alongside the audit. This makes it much easier (and faster) to access audits indirectly related to a company.
221
219
 
222
220
  ```ruby
223
221
  company = Company.create!(name: "Collective Idea")
@@ -258,6 +256,26 @@ To disable auditing on an entire model:
258
256
  User.auditing_enabled = false
259
257
  ```
260
258
 
259
+ ### Custom `Audit` model
260
+
261
+ If you want to extend or modify the audit model, create a new class that
262
+ inherits from `Audited::Audit`:
263
+ ```ruby
264
+ class CustomAudit < Audited::Audit
265
+ def some_custom_behavior
266
+ "Hiya!"
267
+ end
268
+ end
269
+ ```
270
+ Then set it in an initializer:
271
+ ```ruby
272
+ # config/initializers/audited.rb
273
+
274
+ Audited.config do |config|
275
+ config.audit_class = CustomAudit
276
+ end
277
+ ```
278
+
261
279
  ## Gotchas
262
280
 
263
281
  ### Using attr_protected with Rails 4.x
@@ -3,6 +3,5 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~> 5.0.0"
6
- gem "rails-observers", :github => "rails/rails-observers", :branch => "master"
7
6
 
8
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", ">= 5.1.0.rc1", "< 5.2"
6
+
7
+ gemspec :name => "audited", :path => "../"
@@ -1,19 +1,20 @@
1
- require 'rails/observers/active_model/active_model'
2
1
  require 'active_record'
3
2
 
4
3
  module Audited
5
4
  class << self
6
- attr_accessor :ignored_attributes, :current_user_method
5
+ attr_accessor :ignored_attributes, :current_user_method, :audit_class
7
6
 
8
- # Deprecate audit_class accessors in preperation of their removal
9
7
  def audit_class
10
- Audited::Audit
8
+ @audit_class || Audit
11
9
  end
12
- deprecate audit_class: "Audited.audit_class is now always Audited::Audit. This method will be removed."
13
10
 
14
11
  def store
15
12
  Thread.current[:audited_store] ||= {}
16
13
  end
14
+
15
+ def config
16
+ yield(self)
17
+ end
17
18
  end
18
19
 
19
20
  @ignored_attributes = %w(lock_version created_at updated_at created_on updated_on)
@@ -6,25 +6,44 @@ module Audited
6
6
  # * <tt>auditable</tt>: the ActiveRecord model that was changed
7
7
  # * <tt>user</tt>: the user that performed the change; a string or an ActiveRecord model
8
8
  # * <tt>action</tt>: one of create, update, or delete
9
- # * <tt>audited_changes</tt>: a serialized hash of all the changes
9
+ # * <tt>audited_changes</tt>: a hash of all the changes
10
10
  # * <tt>comment</tt>: a comment set with the audit
11
11
  # * <tt>version</tt>: the version of the model
12
12
  # * <tt>request_uuid</tt>: a uuid based that allows audits from the same controller request
13
13
  # * <tt>created_at</tt>: Time that the change was performed
14
14
  #
15
- class Audit < ::ActiveRecord::Base
16
- include ActiveModel::Observing
17
15
 
16
+ class YAMLIfTextColumnType
17
+ class << self
18
+ def load(obj)
19
+ if Audited.audit_class.columns_hash["audited_changes"].sql_type == "text"
20
+ ActiveRecord::Coders::YAMLColumn.new(Object).load(obj)
21
+ else
22
+ obj
23
+ end
24
+ end
25
+
26
+ def dump(obj)
27
+ if Audited.audit_class.columns_hash["audited_changes"].sql_type == "text"
28
+ ActiveRecord::Coders::YAMLColumn.new(Object).dump(obj)
29
+ else
30
+ obj
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ class Audit < ::ActiveRecord::Base
18
37
  belongs_to :auditable, polymorphic: true
19
38
  belongs_to :user, polymorphic: true
20
39
  belongs_to :associated, polymorphic: true
21
40
 
22
- before_create :set_version_number, :set_audit_user, :set_request_uuid
41
+ before_create :set_version_number, :set_audit_user, :set_request_uuid, :set_remote_address
23
42
 
24
43
  cattr_accessor :audited_class_names
25
44
  self.audited_class_names = Set.new
26
45
 
27
- serialize :audited_changes
46
+ serialize :audited_changes, YAMLIfTextColumnType
28
47
 
29
48
  scope :ascending, ->{ reorder(version: :asc) }
30
49
  scope :descending, ->{ reorder(version: :desc)}
@@ -95,10 +114,10 @@ module Audited
95
114
  # by +user+. This method is hopefully threadsafe, making it ideal
96
115
  # for background operations that require audit information.
97
116
  def self.as_user(user, &block)
98
- Thread.current[:audited_user] = user
117
+ ::Audited.store[:audited_user] = user
99
118
  yield
100
119
  ensure
101
- Thread.current[:audited_user] = nil
120
+ ::Audited.store[:audited_user] = nil
102
121
  end
103
122
 
104
123
  # @private
@@ -125,6 +144,11 @@ module Audited
125
144
  record
126
145
  end
127
146
 
147
+ # use created_at as timestamp cache key
148
+ def self.collection_cache_key(collection = all, timestamp_column = :created_at)
149
+ super(collection, :created_at)
150
+ end
151
+
128
152
  private
129
153
 
130
154
  def set_version_number
@@ -133,12 +157,18 @@ module Audited
133
157
  end
134
158
 
135
159
  def set_audit_user
136
- self.user = Thread.current[:audited_user] if Thread.current[:audited_user]
160
+ self.user ||= ::Audited.store[:audited_user] # from .as_user
161
+ self.user ||= ::Audited.store[:current_user] # from Sweeper
137
162
  nil # prevent stopping callback chains
138
163
  end
139
164
 
140
165
  def set_request_uuid
166
+ self.request_uuid ||= ::Audited.store[:current_request_uuid]
141
167
  self.request_uuid ||= SecureRandom.uuid
142
168
  end
169
+
170
+ def set_remote_address
171
+ self.remote_address ||= ::Audited.store[:current_remote_address]
172
+ end
143
173
  end
144
174
  end
@@ -51,8 +51,8 @@ module Audited
51
51
 
52
52
  attr_accessor :audit_comment
53
53
 
54
- has_many :audits, -> { order(version: :asc) }, as: :auditable, class_name: Audit.name
55
- Audit.audited_class_names << to_s
54
+ has_many :audits, -> { order(version: :asc) }, as: :auditable, class_name: Audited.audit_class.name
55
+ Audited.audit_class.audited_class_names << to_s
56
56
 
57
57
  on = Array(options[:on])
58
58
  after_create :audit_create if on.empty? || on.include?(:create)
@@ -75,7 +75,7 @@ module Audited
75
75
  end
76
76
 
77
77
  def has_associated_audits
78
- has_many :associated_audits, as: :associated, class_name: Audit.name
78
+ has_many :associated_audits, as: :associated, class_name: Audited.audit_class.name
79
79
  end
80
80
 
81
81
  def default_ignored_attributes
@@ -118,13 +118,13 @@ module Audited
118
118
 
119
119
  # Get a specific revision specified by the version number, or +:previous+
120
120
  def revision(version)
121
- revision_with Audit.reconstruct_attributes(audits_to(version))
121
+ revision_with Audited.audit_class.reconstruct_attributes(audits_to(version))
122
122
  end
123
123
 
124
124
  # Find the oldest revision recorded prior to the date/time provided.
125
125
  def revision_at(date_or_time)
126
126
  audits = self.audits.up_until(date_or_time)
127
- revision_with Audit.reconstruct_attributes(audits) unless audits.empty?
127
+ revision_with Audited.audit_class.reconstruct_attributes(audits) unless audits.empty?
128
128
  end
129
129
 
130
130
  # List of attributes that are audited.
@@ -152,7 +152,7 @@ module Audited
152
152
  revision.send :instance_variable_set, '@destroyed', false
153
153
  revision.send :instance_variable_set, '@_destroyed', false
154
154
  revision.send :instance_variable_set, '@marked_for_destruction', false
155
- Audit.assign_revision_attributes(revision, attributes)
155
+ Audited.audit_class.assign_revision_attributes(revision, attributes)
156
156
 
157
157
  # Remove any association proxies so that they will be recreated
158
158
  # and reference the correct object for this revision. The only way
@@ -267,6 +267,10 @@ module Audited
267
267
  end
268
268
  end
269
269
 
270
+ def non_audited_columns=(columns)
271
+ @non_audited_columns = columns
272
+ end
273
+
270
274
  # Executes the block with auditing disabled.
271
275
  #
272
276
  # Foo.without_auditing do
@@ -294,7 +298,7 @@ module Audited
294
298
  # convenience wrapper around
295
299
  # @see Audit#as_user.
296
300
  def audit_as(user, &block)
297
- Audit.as_user(user, &block)
301
+ Audited.audit_class.as_user(user, &block)
298
302
  end
299
303
 
300
304
  def auditing_enabled
@@ -170,7 +170,7 @@ module Audited
170
170
  def association_exists?
171
171
  !reflection.nil? &&
172
172
  reflection.macro == :has_many &&
173
- reflection.options[:class_name] == Audit.name
173
+ reflection.options[:class_name] == Audited.audit_class.name
174
174
  end
175
175
  end
176
176
  end
@@ -1,60 +1,49 @@
1
- require "rails/observers/activerecord/active_record"
2
- require "rails/observers/action_controller/caching"
3
-
4
1
  module Audited
5
- class Sweeper < ActionController::Caching::Sweeper
6
- observe Audited::Audit
2
+ class Sweeper
3
+ STORED_DATA = {
4
+ current_remote_address: :remote_ip,
5
+ current_request_uuid: :request_uuid,
6
+ current_user: :current_user
7
+ }
8
+
9
+ delegate :store, to: ::Audited
7
10
 
8
11
  def around(controller)
9
12
  self.controller = controller
13
+ STORED_DATA.each { |k,m| store[k] = send(m) }
10
14
  yield
11
15
  ensure
12
16
  self.controller = nil
13
- end
14
-
15
- def before_create(audit)
16
- audit.user ||= current_user
17
- audit.remote_address = controller.try(:request).try(:remote_ip)
18
- audit.request_uuid = request_uuid if request_uuid
17
+ STORED_DATA.keys.each { |k| store.delete(k) }
19
18
  end
20
19
 
21
20
  def current_user
22
21
  controller.send(Audited.current_user_method) if controller.respond_to?(Audited.current_user_method, true)
23
22
  end
24
23
 
25
- def request_uuid
26
- controller.try(:request).try(:uuid)
27
- end
28
-
29
- def add_observer!(klass)
30
- super
31
- define_callback(klass)
24
+ def remote_ip
25
+ controller.try(:request).try(:remote_ip)
32
26
  end
33
27
 
34
- def define_callback(klass)
35
- observer = self
36
- callback_meth = :_notify_audited_sweeper
37
- klass.send(:define_method, callback_meth) do
38
- observer.update(:before_create, self)
39
- end
40
- klass.send(:before_create, callback_meth)
28
+ def request_uuid
29
+ controller.try(:request).try(:uuid)
41
30
  end
42
31
 
43
32
  def controller
44
- ::Audited.store[:current_controller]
33
+ store[:current_controller]
45
34
  end
46
35
 
47
36
  def controller=(value)
48
- ::Audited.store[:current_controller] = value
37
+ store[:current_controller] = value
49
38
  end
50
39
  end
51
40
  end
52
41
 
53
42
  ActiveSupport.on_load(:action_controller) do
54
43
  if defined?(ActionController::Base)
55
- ActionController::Base.around_action Audited::Sweeper.instance
44
+ ActionController::Base.around_action Audited::Sweeper.new
56
45
  end
57
46
  if defined?(ActionController::API)
58
- ActionController::API.around_action Audited::Sweeper.instance
47
+ ActionController::API.around_action Audited::Sweeper.new
59
48
  end
60
49
  end
@@ -1,3 +1,3 @@
1
1
  module Audited
2
- VERSION = "4.3.0"
2
+ VERSION = "4.4.0"
3
3
  end
@@ -3,13 +3,17 @@ require 'rails/generators/migration'
3
3
  require 'active_record'
4
4
  require 'rails/generators/active_record'
5
5
  require 'generators/audited/migration'
6
+ require 'generators/audited/migration_helper'
6
7
 
7
8
  module Audited
8
9
  module Generators
9
10
  class InstallGenerator < Rails::Generators::Base
10
11
  include Rails::Generators::Migration
12
+ include Audited::Generators::MigrationHelper
11
13
  extend Audited::Generators::Migration
12
14
 
15
+ class_option :audited_changes_column_type, type: :string, default: "text", required: false
16
+
13
17
  source_root File.expand_path("../templates", __FILE__)
14
18
 
15
19
  def copy_migration
@@ -0,0 +1,9 @@
1
+ module Audited
2
+ module Generators
3
+ module MigrationHelper
4
+ def migration_parent
5
+ Rails::VERSION::MAJOR == 4 ? 'ActiveRecord::Migration' : "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,4 +1,4 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
2
  def self.up
3
3
  add_column :audits, :association_id, :integer
4
4
  add_column :audits, :association_type, :string
@@ -1,4 +1,4 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
2
  def self.up
3
3
  add_column :audits, :comment, :string
4
4
  end
@@ -1,4 +1,4 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
2
  def self.up
3
3
  add_column :audits, :remote_address, :string
4
4
  end
@@ -1,4 +1,4 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
2
  def self.up
3
3
  add_column :audits, :request_uuid, :string
4
4
  add_index :audits, :request_uuid
@@ -1,4 +1,4 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
2
  def self.up
3
3
  create_table :audits, :force => true do |t|
4
4
  t.column :auditable_id, :integer
@@ -9,7 +9,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration
9
9
  t.column :user_type, :string
10
10
  t.column :username, :string
11
11
  t.column :action, :string
12
- t.column :audited_changes, :text
12
+ t.column :audited_changes, :<%= options[:audited_changes_column_type] %>
13
13
  t.column :version, :integer, :default => 0
14
14
  t.column :comment, :string
15
15
  t.column :remote_address, :string
@@ -1,4 +1,4 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
2
  def self.up
3
3
  if index_exists? :audits, [:association_id, :association_type], :name => 'association_index'
4
4
  remove_index :audits, :name => 'association_index'
@@ -1,4 +1,4 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
2
  def self.up
3
3
  rename_column :audits, :changes, :audited_changes
4
4
  end
@@ -1,4 +1,4 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
2
  def self.up
3
3
  rename_column :audits, :auditable_parent_id, :association_id
4
4
  rename_column :audits, :auditable_parent_type, :association_type
@@ -3,11 +3,13 @@ require 'rails/generators/migration'
3
3
  require 'active_record'
4
4
  require 'rails/generators/active_record'
5
5
  require 'generators/audited/migration'
6
+ require 'generators/audited/migration_helper'
6
7
 
7
8
  module Audited
8
9
  module Generators
9
10
  class UpgradeGenerator < Rails::Generators::Base
10
11
  include Rails::Generators::Migration
12
+ include Audited::Generators::MigrationHelper
11
13
  extend Audited::Generators::Migration
12
14
 
13
15
  source_root File.expand_path("../templates", __FILE__)
@@ -3,6 +3,54 @@ require "spec_helper"
3
3
  describe Audited::Audit do
4
4
  let(:user) { Models::ActiveRecord::User.new name: "Testing" }
5
5
 
6
+ describe "audit class" do
7
+ around(:example) do |example|
8
+ original_audit_class = Audited.audit_class
9
+
10
+ class CustomAudit < Audited::Audit
11
+ def custom_method
12
+ "I'm custom!"
13
+ end
14
+ end
15
+
16
+ class TempModel < ::ActiveRecord::Base
17
+ self.table_name = :companies
18
+ end
19
+
20
+ example.run
21
+
22
+ Audited.config { |config| config.audit_class = original_audit_class }
23
+ Audited::Audit.audited_class_names.delete("TempModel")
24
+ Object.send(:remove_const, :TempModel)
25
+ Object.send(:remove_const, :CustomAudit)
26
+ end
27
+
28
+ context "when a custom audit class is configured" do
29
+ it "should be used in place of #{described_class}" do
30
+ Audited.config { |config| config.audit_class = CustomAudit }
31
+ TempModel.audited
32
+
33
+ record = TempModel.create
34
+
35
+ audit = record.audits.first
36
+ expect(audit).to be_a CustomAudit
37
+ expect(audit.custom_method).to eq "I'm custom!"
38
+ end
39
+ end
40
+
41
+ context "when a custom audit class is not configured" do
42
+ it "should default to #{described_class}" do
43
+ TempModel.audited
44
+
45
+ record = TempModel.create
46
+
47
+ audit = record.audits.first
48
+ expect(audit).to be_a Audited::Audit
49
+ expect(audit.respond_to?(:custom_method)).to be false
50
+ end
51
+ end
52
+ end
53
+
6
54
  describe "user=" do
7
55
 
8
56
  it "should be able to set the user to a model object" do
@@ -191,9 +239,8 @@ describe Audited::Audit do
191
239
  raise StandardError.new('expected')
192
240
  end
193
241
  }.to raise_exception('expected')
194
- expect(Thread.current[:audited_user]).to be_nil
242
+ expect(Audited.store[:audited_user]).to be_nil
195
243
  end
196
244
 
197
245
  end
198
-
199
246
  end
@@ -17,7 +17,7 @@ describe Audited::Auditor do
17
17
  end
18
18
  end
19
19
 
20
- it "should be configurable which attributes are not audited" do
20
+ it "should be configurable which attributes are not audited via ignored_attributes" do
21
21
  Audited.ignored_attributes = ['delta', 'top_secret', 'created_at']
22
22
  class Secret < ::ActiveRecord::Base
23
23
  audited
@@ -26,6 +26,15 @@ describe Audited::Auditor do
26
26
  expect(Secret.non_audited_columns).to include('delta', 'top_secret', 'created_at')
27
27
  end
28
28
 
29
+ it "should be configurable which attributes are not audited via non_audited_columns=" do
30
+ class Secret2 < ::ActiveRecord::Base
31
+ audited
32
+ self.non_audited_columns = ['delta', 'top_secret', 'created_at']
33
+ end
34
+
35
+ expect(Secret2.non_audited_columns).to include('delta', 'top_secret', 'created_at')
36
+ end
37
+
29
38
  it "should not save non-audited columns" do
30
39
  expect(create_user.audits.first.audited_changes.keys.any? { |col| ['created_at', 'updated_at', 'password'].include?( col ) }).to eq(false)
31
40
  end
@@ -48,6 +57,38 @@ describe Audited::Auditor do
48
57
  user.save!
49
58
  expect(user.audits.last.audited_changes.keys).to eq(%w{password})
50
59
  end
60
+
61
+ if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' && Rails.version >= "4.2.0.0" # Postgres json and jsonb support was added in Rails 4.2
62
+ describe "'json' and 'jsonb' audited_changes column type" do
63
+ let(:migrations_path) { SPEC_ROOT.join("support/active_record/postgres") }
64
+
65
+ after do
66
+ ActiveRecord::Migrator.rollback([migrations_path])
67
+ end
68
+
69
+ it "should work if column type is 'json'" do
70
+ ActiveRecord::Migrator.up([migrations_path], 1)
71
+ Audited::Audit.reset_column_information
72
+ expect(Audited::Audit.columns_hash["audited_changes"].sql_type).to eq("json")
73
+
74
+ user = Models::ActiveRecord::User.create
75
+ user.name = "new name"
76
+ user.save!
77
+ expect(user.audits.last.audited_changes).to eq({"name" => [nil, "new name"]})
78
+ end
79
+
80
+ it "should work if column type is 'jsonb'" do
81
+ ActiveRecord::Migrator.up([migrations_path], 2)
82
+ Audited::Audit.reset_column_information
83
+ expect(Audited::Audit.columns_hash["audited_changes"].sql_type).to eq("jsonb")
84
+
85
+ user = Models::ActiveRecord::User.create
86
+ user.name = "new name"
87
+ user.save!
88
+ expect(user.audits.last.audited_changes).to eq({"name" => [nil, "new name"]})
89
+ end
90
+ end
91
+ end
51
92
  end
52
93
 
53
94
  describe :new do
@@ -601,7 +642,7 @@ describe Audited::Auditor do
601
642
  Models::ActiveRecord::Company.auditing_enabled = false
602
643
  company.update_attributes name: 'STI auditors'
603
644
  Models::ActiveRecord::Company.auditing_enabled = true
604
- }.to_not change( Audited.audit_class, :count )
645
+ }.to_not change( Audited::Audit, :count )
605
646
  end
606
647
  end
607
648
  end
@@ -76,7 +76,7 @@ describe AuditsController do
76
76
  controller.send(:current_user=, user)
77
77
 
78
78
  expect {
79
- put :update, id: 123
79
+ put :update, Rails::VERSION::MAJOR == 4 ? {id: 123} : {params: {id: 123}}
80
80
  }.to_not change( Audited::Audit, :count )
81
81
  end
82
82
  end
@@ -86,21 +86,23 @@ end
86
86
  describe Audited::Sweeper do
87
87
 
88
88
  it "should be thread-safe" do
89
+ instance = Audited::Sweeper.new
90
+
89
91
  t1 = Thread.new do
90
92
  sleep 0.5
91
- Audited::Sweeper.instance.controller = 'thread1 controller instance'
92
- expect(Audited::Sweeper.instance.controller).to eq('thread1 controller instance')
93
+ instance.controller = 'thread1 controller instance'
94
+ expect(instance.controller).to eq('thread1 controller instance')
93
95
  end
94
96
 
95
97
  t2 = Thread.new do
96
- Audited::Sweeper.instance.controller = 'thread2 controller instance'
98
+ instance.controller = 'thread2 controller instance'
97
99
  sleep 1
98
- expect(Audited::Sweeper.instance.controller).to eq('thread2 controller instance')
100
+ expect(instance.controller).to eq('thread2 controller instance')
99
101
  end
100
102
 
101
103
  t1.join; t2.join
102
104
 
103
- expect(Audited::Sweeper.instance.controller).to be_nil
105
+ expect(instance.controller).to be_nil
104
106
  end
105
107
 
106
108
  end
@@ -15,6 +15,7 @@ module Models
15
15
 
16
16
  class UserOnlyPassword < ::ActiveRecord::Base
17
17
  self.table_name = :users
18
+ attribute :non_column_attr if Rails.version >= '5.1'
18
19
  audited allow_mass_assignment: true, only: :password
19
20
  end
20
21
 
@@ -0,0 +1,12 @@
1
+ parent = Rails::VERSION::MAJOR == 4 ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
2
+ class ChangeAuditedChangesTypeToJson < parent
3
+ def self.up
4
+ remove_column :audits, :audited_changes
5
+ add_column :audits, :audited_changes, :json
6
+ end
7
+
8
+ def self.down
9
+ remove_column :audits, :audited_changes
10
+ add_column :audits, :audited_changes, :text
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ parent = Rails::VERSION::MAJOR == 4 ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
2
+ class ChangeAuditedChangesTypeToJsonb < parent
3
+ def self.up
4
+ remove_column :audits, :audited_changes
5
+ add_column :audits, :audited_changes, :jsonb
6
+ end
7
+
8
+ def self.down
9
+ remove_column :audits, :audited_changes
10
+ add_column :audits, :audited_changes, :text
11
+ end
12
+ end
@@ -7,11 +7,39 @@ class InstallGeneratorTest < Rails::Generators::TestCase
7
7
  setup :prepare_destination
8
8
  tests Audited::Generators::InstallGenerator
9
9
 
10
- test "should generate a migration" do
11
- run_generator %w(install)
10
+ test "generate migration with 'text' type for audited_changes column" do
11
+ run_generator
12
12
 
13
13
  assert_migration "db/migrate/install_audited.rb" do |content|
14
- assert_match(/class InstallAudited/, content)
14
+ assert_includes(content, 'class InstallAudited')
15
+ assert_includes(content, 't.column :audited_changes, :text')
16
+ end
17
+ end
18
+
19
+ test "generate migration with 'jsonb' type for audited_changes column" do
20
+ run_generator %w(--audited-changes-column-type jsonb)
21
+
22
+ assert_migration "db/migrate/install_audited.rb" do |content|
23
+ assert_includes(content, 'class InstallAudited')
24
+ assert_includes(content, 't.column :audited_changes, :jsonb')
25
+ end
26
+ end
27
+
28
+ test "generate migration with 'json' type for audited_changes column" do
29
+ run_generator %w(--audited-changes-column-type json)
30
+
31
+ assert_migration "db/migrate/install_audited.rb" do |content|
32
+ assert_includes(content, 'class InstallAudited')
33
+ assert_includes(content, 't.column :audited_changes, :json')
34
+ end
35
+ end
36
+
37
+ test "generate migration with correct AR migration parent" do
38
+ run_generator
39
+
40
+ assert_migration "db/migrate/install_audited.rb" do |content|
41
+ parent = Rails::VERSION::MAJOR == 4 ? 'ActiveRecord::Migration' : "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
42
+ assert_includes(content, "class InstallAudited < #{parent}\n")
15
43
  end
16
44
  end
17
45
  end
@@ -6,7 +6,11 @@ class UpgradeGeneratorTest < Rails::Generators::TestCase
6
6
  destination File.expand_path('../../tmp', __FILE__)
7
7
  setup :prepare_destination
8
8
  tests Audited::Generators::UpgradeGenerator
9
- self.use_transactional_fixtures = false
9
+ if Rails::VERSION::MAJOR == 4
10
+ self.use_transactional_fixtures = false
11
+ else
12
+ self.use_transactional_tests = false
13
+ end
10
14
 
11
15
  test "should add 'comment' to audits table" do
12
16
  load_schema 1
@@ -74,4 +78,15 @@ class UpgradeGeneratorTest < Rails::Generators::TestCase
74
78
  assert_match(/add_index :audits, :request_uuid/, content)
75
79
  end
76
80
  end
81
+
82
+ test "generate migration with correct AR migration parent" do
83
+ load_schema 1
84
+
85
+ run_generator %w(upgrade)
86
+
87
+ assert_migration "db/migrate/add_comment_to_audits.rb" do |content|
88
+ parent = Rails::VERSION::MAJOR == 4 ? 'ActiveRecord::Migration' : "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
89
+ assert_includes(content, "class AddCommentToAudits < #{parent}\n")
90
+ end
91
+ end
77
92
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: audited
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.0
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Keepers
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2016-09-17 00:00:00.000000000 Z
16
+ date: 2017-03-29 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: activerecord
@@ -35,20 +35,6 @@ dependencies:
35
35
  - - "<"
36
36
  - !ruby/object:Gem::Version
37
37
  version: '5.1'
38
- - !ruby/object:Gem::Dependency
39
- name: rails-observers
40
- requirement: !ruby/object:Gem::Requirement
41
- requirements:
42
- - - "~>"
43
- - !ruby/object:Gem::Version
44
- version: 0.1.2
45
- type: :runtime
46
- prerelease: false
47
- version_requirements: !ruby/object:Gem::Requirement
48
- requirements:
49
- - - "~>"
50
- - !ruby/object:Gem::Version
51
- version: 0.1.2
52
38
  - !ruby/object:Gem::Dependency
53
39
  name: appraisal
54
40
  requirement: !ruby/object:Gem::Requirement
@@ -149,7 +135,7 @@ files:
149
135
  - ".travis.yml"
150
136
  - ".yardopts"
151
137
  - Appraisals
152
- - CHANGELOG
138
+ - CHANGELOG.md
153
139
  - Gemfile
154
140
  - LICENSE
155
141
  - README.md
@@ -158,6 +144,7 @@ files:
158
144
  - gemfiles/rails41.gemfile
159
145
  - gemfiles/rails42.gemfile
160
146
  - gemfiles/rails50.gemfile
147
+ - gemfiles/rails51.gemfile
161
148
  - lib/audited-rspec.rb
162
149
  - lib/audited.rb
163
150
  - lib/audited/audit.rb
@@ -167,6 +154,7 @@ files:
167
154
  - lib/audited/version.rb
168
155
  - lib/generators/audited/install_generator.rb
169
156
  - lib/generators/audited/migration.rb
157
+ - lib/generators/audited/migration_helper.rb
170
158
  - lib/generators/audited/templates/add_association_to_audits.rb
171
159
  - lib/generators/audited/templates/add_comment_to_audits.rb
172
160
  - lib/generators/audited/templates/add_remote_address_to_audits.rb
@@ -192,6 +180,8 @@ files:
192
180
  - spec/rails_app/config/routes.rb
193
181
  - spec/spec_helper.rb
194
182
  - spec/support/active_record/models.rb
183
+ - spec/support/active_record/postgres/1_change_audited_changes_type_to_json.rb
184
+ - spec/support/active_record/postgres/2_change_audited_changes_type_to_jsonb.rb
195
185
  - spec/support/active_record/schema.rb
196
186
  - test/db/version_1.rb
197
187
  - test/db/version_2.rb
data/CHANGELOG DELETED
@@ -1,34 +0,0 @@
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