audited 4.2.1 → 4.4.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +20 -9
  3. data/Appraisals +11 -6
  4. data/CHANGELOG.md +190 -0
  5. data/Gemfile +1 -13
  6. data/README.md +65 -34
  7. data/Rakefile +3 -18
  8. data/gemfiles/rails40.gemfile +1 -5
  9. data/gemfiles/rails41.gemfile +1 -5
  10. data/gemfiles/rails42.gemfile +1 -5
  11. data/gemfiles/rails50.gemfile +7 -0
  12. data/gemfiles/rails51.gemfile +7 -0
  13. data/lib/audited/audit.rb +126 -56
  14. data/lib/audited/auditor.rb +76 -44
  15. data/lib/audited/rspec_matchers.rb +5 -1
  16. data/lib/audited/sweeper.rb +24 -46
  17. data/lib/audited/version.rb +1 -1
  18. data/lib/audited-rspec.rb +4 -0
  19. data/lib/audited.rb +16 -2
  20. data/lib/generators/audited/install_generator.rb +24 -0
  21. data/lib/generators/audited/migration.rb +15 -0
  22. data/lib/generators/audited/migration_helper.rb +9 -0
  23. data/lib/generators/audited/templates/add_association_to_audits.rb +11 -0
  24. data/lib/generators/audited/templates/add_comment_to_audits.rb +9 -0
  25. data/lib/generators/audited/templates/add_remote_address_to_audits.rb +10 -0
  26. data/lib/generators/audited/templates/add_request_uuid_to_audits.rb +10 -0
  27. data/lib/generators/audited/templates/install.rb +30 -0
  28. data/lib/generators/audited/templates/rename_association_to_associated.rb +23 -0
  29. data/lib/generators/audited/templates/rename_changes_to_audited_changes.rb +9 -0
  30. data/lib/generators/audited/templates/rename_parent_to_association.rb +11 -0
  31. data/lib/generators/audited/upgrade_generator.rb +59 -0
  32. data/spec/audited/audit_spec.rb +246 -0
  33. data/spec/audited/auditor_spec.rb +648 -0
  34. data/spec/audited/sweeper_spec.rb +108 -0
  35. data/spec/audited_spec_helpers.rb +6 -22
  36. data/spec/rails_app/config/environments/test.rb +7 -4
  37. data/spec/rails_app/config/initializers/secret_token.rb +1 -1
  38. data/spec/rails_app/config/routes.rb +1 -4
  39. data/spec/spec_helper.rb +7 -9
  40. data/spec/support/active_record/models.rb +24 -13
  41. data/spec/support/active_record/postgres/1_change_audited_changes_type_to_json.rb +12 -0
  42. data/spec/support/active_record/postgres/2_change_audited_changes_type_to_jsonb.rb +12 -0
  43. data/spec/support/active_record/schema.rb +37 -12
  44. data/test/db/version_1.rb +4 -4
  45. data/test/db/version_2.rb +4 -4
  46. data/test/db/version_3.rb +4 -4
  47. data/test/db/version_4.rb +4 -4
  48. data/test/db/version_5.rb +2 -2
  49. data/test/db/version_6.rb +2 -2
  50. data/test/install_generator_test.rb +31 -3
  51. data/test/upgrade_generator_test.rb +25 -10
  52. metadata +69 -43
  53. data/CHANGELOG +0 -34
  54. data/lib/audited/active_record/version.rb +0 -5
  55. data/lib/audited/mongo_mapper/version.rb +0 -5
  56. data/spec/support/mongo_mapper/connection.rb +0 -4
  57. data/spec/support/mongo_mapper/models.rb +0 -214
@@ -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
@@ -0,0 +1,11 @@
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
+ def self.up
3
+ add_column :audits, :association_id, :integer
4
+ add_column :audits, :association_type, :string
5
+ end
6
+
7
+ def self.down
8
+ remove_column :audits, :association_type
9
+ remove_column :audits, :association_id
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
+ def self.up
3
+ add_column :audits, :comment, :string
4
+ end
5
+
6
+ def self.down
7
+ remove_column :audits, :comment
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
+ def self.up
3
+ add_column :audits, :remote_address, :string
4
+ end
5
+
6
+ def self.down
7
+ remove_column :audits, :remote_address
8
+ end
9
+ end
10
+
@@ -0,0 +1,10 @@
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
+ def self.up
3
+ add_column :audits, :request_uuid, :string
4
+ add_index :audits, :request_uuid
5
+ end
6
+
7
+ def self.down
8
+ remove_column :audits, :request_uuid
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
+ def self.up
3
+ create_table :audits, :force => true do |t|
4
+ t.column :auditable_id, :integer
5
+ t.column :auditable_type, :string
6
+ t.column :associated_id, :integer
7
+ t.column :associated_type, :string
8
+ t.column :user_id, :integer
9
+ t.column :user_type, :string
10
+ t.column :username, :string
11
+ t.column :action, :string
12
+ t.column :audited_changes, :<%= options[:audited_changes_column_type] %>
13
+ t.column :version, :integer, :default => 0
14
+ t.column :comment, :string
15
+ t.column :remote_address, :string
16
+ t.column :request_uuid, :string
17
+ t.column :created_at, :datetime
18
+ end
19
+
20
+ add_index :audits, [:auditable_id, :auditable_type], :name => 'auditable_index'
21
+ add_index :audits, [:associated_id, :associated_type], :name => 'associated_index'
22
+ add_index :audits, [:user_id, :user_type], :name => 'user_index'
23
+ add_index :audits, :request_uuid
24
+ add_index :audits, :created_at
25
+ end
26
+
27
+ def self.down
28
+ drop_table :audits
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
+ def self.up
3
+ if index_exists? :audits, [:association_id, :association_type], :name => 'association_index'
4
+ remove_index :audits, :name => 'association_index'
5
+ end
6
+
7
+ rename_column :audits, :association_id, :associated_id
8
+ rename_column :audits, :association_type, :associated_type
9
+
10
+ add_index :audits, [:associated_id, :associated_type], :name => 'associated_index'
11
+ end
12
+
13
+ def self.down
14
+ if index_exists? :audits, [:associated_id, :associated_type], :name => 'associated_index'
15
+ remove_index :audits, :name => 'associated_index'
16
+ end
17
+
18
+ rename_column :audits, :associated_type, :association_type
19
+ rename_column :audits, :associated_id, :association_id
20
+
21
+ add_index :audits, [:association_id, :association_type], :name => 'association_index'
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
+ def self.up
3
+ rename_column :audits, :changes, :audited_changes
4
+ end
5
+
6
+ def self.down
7
+ rename_column :audits, :audited_changes, :changes
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ class <%= migration_class_name %> < <%= migration_parent %>
2
+ def self.up
3
+ rename_column :audits, :auditable_parent_id, :association_id
4
+ rename_column :audits, :auditable_parent_type, :association_type
5
+ end
6
+
7
+ def self.down
8
+ rename_column :audits, :association_type, :auditable_parent_type
9
+ rename_column :audits, :association_id, :auditable_parent_id
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+ require 'active_record'
4
+ require 'rails/generators/active_record'
5
+ require 'generators/audited/migration'
6
+ require 'generators/audited/migration_helper'
7
+
8
+ module Audited
9
+ module Generators
10
+ class UpgradeGenerator < Rails::Generators::Base
11
+ include Rails::Generators::Migration
12
+ include Audited::Generators::MigrationHelper
13
+ extend Audited::Generators::Migration
14
+
15
+ source_root File.expand_path("../templates", __FILE__)
16
+
17
+ def copy_templates
18
+ migrations_to_be_applied do |m|
19
+ migration_template "#{m}.rb", "db/migrate/#{m}.rb"
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def migrations_to_be_applied
26
+ Audited::Audit.reset_column_information
27
+ columns = Audited::Audit.columns.map(&:name)
28
+
29
+ yield :add_comment_to_audits unless columns.include?('comment')
30
+
31
+ if columns.include?('changes')
32
+ yield :rename_changes_to_audited_changes
33
+ end
34
+
35
+ unless columns.include?('remote_address')
36
+ yield :add_remote_address_to_audits
37
+ end
38
+
39
+ unless columns.include?('request_uuid')
40
+ yield :add_request_uuid_to_audits
41
+ end
42
+
43
+ unless columns.include?('association_id')
44
+ if columns.include?('auditable_parent_id')
45
+ yield :rename_parent_to_association
46
+ else
47
+ unless columns.include?('associated_id')
48
+ yield :add_association_to_audits
49
+ end
50
+ end
51
+ end
52
+
53
+ if columns.include?('association_id')
54
+ yield :rename_association_to_associated
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,246 @@
1
+ require "spec_helper"
2
+
3
+ describe Audited::Audit do
4
+ let(:user) { Models::ActiveRecord::User.new name: "Testing" }
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
+
54
+ describe "user=" do
55
+
56
+ it "should be able to set the user to a model object" do
57
+ subject.user = user
58
+ expect(subject.user).to eq(user)
59
+ end
60
+
61
+ it "should be able to set the user to nil" do
62
+ subject.user_id = 1
63
+ subject.user_type = 'Models::ActiveRecord::User'
64
+ subject.username = 'joe'
65
+
66
+ subject.user = nil
67
+
68
+ expect(subject.user).to be_nil
69
+ expect(subject.user_id).to be_nil
70
+ expect(subject.user_type).to be_nil
71
+ expect(subject.username).to be_nil
72
+ end
73
+
74
+ it "should be able to set the user to a string" do
75
+ subject.user = 'test'
76
+ expect(subject.user).to eq('test')
77
+ end
78
+
79
+ it "should clear model when setting to a string" do
80
+ subject.user = user
81
+ subject.user = 'testing'
82
+ expect(subject.user_id).to be_nil
83
+ expect(subject.user_type).to be_nil
84
+ end
85
+
86
+ it "should clear the username when setting to a model" do
87
+ subject.username = 'test'
88
+ subject.user = user
89
+ expect(subject.username).to be_nil
90
+ end
91
+
92
+ end
93
+
94
+ describe "revision" do
95
+
96
+ it "should recreate attributes" do
97
+ user = Models::ActiveRecord::User.create name: "1"
98
+ 5.times {|i| user.update_attribute :name, (i + 2).to_s }
99
+
100
+ user.audits.each do |audit|
101
+ expect(audit.revision.name).to eq(audit.version.to_s)
102
+ end
103
+ end
104
+
105
+ it "should set protected attributes" do
106
+ u = Models::ActiveRecord::User.create(name: "Brandon")
107
+ u.update_attribute :logins, 1
108
+ u.update_attribute :logins, 2
109
+
110
+ expect(u.audits[2].revision.logins).to eq(2)
111
+ expect(u.audits[1].revision.logins).to eq(1)
112
+ expect(u.audits[0].revision.logins).to eq(0)
113
+ end
114
+
115
+ it "should bypass attribute assignment wrappers" do
116
+ u = Models::ActiveRecord::User.create(name: "<Joe>")
117
+ expect(u.audits.first.revision.name).to eq("&lt;Joe&gt;")
118
+ end
119
+
120
+ it "should work for deleted records" do
121
+ user = Models::ActiveRecord::User.create name: "1"
122
+ user.destroy
123
+ revision = user.audits.last.revision
124
+ expect(revision.name).to eq(user.name)
125
+ expect(revision).to be_a_new_record
126
+ end
127
+ end
128
+
129
+ it "should set the version number on create" do
130
+ user = Models::ActiveRecord::User.create! name: "Set Version Number"
131
+ expect(user.audits.first.version).to eq(1)
132
+ user.update_attribute :name, "Set to 2"
133
+ expect(user.audits.reload.first.version).to eq(1)
134
+ expect(user.audits.reload.last.version).to eq(2)
135
+ user.destroy
136
+ expect(Audited::Audit.where(auditable_type: "Models::ActiveRecord::User", auditable_id: user.id).last.version).to eq(3)
137
+ end
138
+
139
+ it "should set the request uuid on create" do
140
+ user = Models::ActiveRecord::User.create! name: "Set Request UUID"
141
+ expect(user.audits.reload.first.request_uuid).not_to be_blank
142
+ end
143
+
144
+ describe "reconstruct_attributes" do
145
+ it "should work with the old way of storing just the new value" do
146
+ audits = Audited::Audit.reconstruct_attributes([Audited::Audit.new(audited_changes: {"attribute" => "value"})])
147
+ expect(audits["attribute"]).to eq("value")
148
+ end
149
+ end
150
+
151
+ describe "audited_classes" do
152
+ class Models::ActiveRecord::CustomUser < ::ActiveRecord::Base
153
+ end
154
+ class Models::ActiveRecord::CustomUserSubclass < Models::ActiveRecord::CustomUser
155
+ audited
156
+ end
157
+
158
+ it "should include audited classes" do
159
+ expect(Audited::Audit.audited_classes).to include(Models::ActiveRecord::User)
160
+ end
161
+
162
+ it "should include subclasses" do
163
+ expect(Audited::Audit.audited_classes).to include(Models::ActiveRecord::CustomUserSubclass)
164
+ end
165
+ end
166
+
167
+ describe "new_attributes" do
168
+ it "should return a hash of the new values" do
169
+ new_attributes = Audited::Audit.new(audited_changes: {a: [1, 2], b: [3, 4]}).new_attributes
170
+ expect(new_attributes).to eq({"a" => 2, "b" => 4})
171
+ end
172
+ end
173
+
174
+ describe "old_attributes" do
175
+ it "should return a hash of the old values" do
176
+ old_attributes = Audited::Audit.new(audited_changes: {a: [1, 2], b: [3, 4]}).old_attributes
177
+ expect(old_attributes).to eq({"a" => 1, "b" => 3})
178
+ end
179
+ end
180
+
181
+ describe "as_user" do
182
+ it "should record user objects" do
183
+ Audited::Audit.as_user(user) do
184
+ company = Models::ActiveRecord::Company.create name: "The auditors"
185
+ company.name = "The Auditors, Inc"
186
+ company.save
187
+
188
+ company.audits.each do |audit|
189
+ expect(audit.user).to eq(user)
190
+ end
191
+ end
192
+ end
193
+
194
+ it "should record usernames" do
195
+ Audited::Audit.as_user(user.name) do
196
+ company = Models::ActiveRecord::Company.create name: "The auditors"
197
+ company.name = "The Auditors, Inc"
198
+ company.save
199
+
200
+ company.audits.each do |audit|
201
+ expect(audit.username).to eq(user.name)
202
+ end
203
+ end
204
+ end
205
+
206
+ it "should be thread safe" do
207
+ begin
208
+ expect(user.save).to eq(true)
209
+
210
+ t1 = Thread.new do
211
+ Audited::Audit.as_user(user) do
212
+ sleep 1
213
+ expect(Models::ActiveRecord::Company.create(name: "The Auditors, Inc").audits.first.user).to eq(user)
214
+ end
215
+ end
216
+
217
+ t2 = Thread.new do
218
+ Audited::Audit.as_user(user.name) do
219
+ expect(Models::ActiveRecord::Company.create(name: "The Competing Auditors, LLC").audits.first.username).to eq(user.name)
220
+ sleep 0.5
221
+ end
222
+ end
223
+
224
+ t1.join
225
+ t2.join
226
+ end
227
+ end if ActiveRecord::Base.connection.adapter_name != 'SQLite'
228
+
229
+ it "should return the value from the yield block" do
230
+ result = Audited::Audit.as_user('foo') do
231
+ 42
232
+ end
233
+ expect(result).to eq(42)
234
+ end
235
+
236
+ it "should reset audited_user when the yield block raises an exception" do
237
+ expect {
238
+ Audited::Audit.as_user('foo') do
239
+ raise StandardError.new('expected')
240
+ end
241
+ }.to raise_exception('expected')
242
+ expect(Audited.store[:audited_user]).to be_nil
243
+ end
244
+
245
+ end
246
+ end