tangledwires-audited 6.0.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.
- checksums.yaml +7 -0
- data/Appraisals +37 -0
- data/CHANGELOG.md +539 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +447 -0
- data/Rakefile +16 -0
- data/audited.gemspec +38 -0
- data/lib/audited/audit.rb +204 -0
- data/lib/audited/audit_associate.rb +8 -0
- data/lib/audited/auditor.rb +564 -0
- data/lib/audited/railtie.rb +16 -0
- data/lib/audited/rspec_matchers.rb +228 -0
- data/lib/audited/sweeper.rb +42 -0
- data/lib/audited/version.rb +5 -0
- data/lib/audited-rspec.rb +6 -0
- data/lib/audited.rb +60 -0
- data/lib/generators/audited/install_generator.rb +27 -0
- data/lib/generators/audited/migration.rb +25 -0
- data/lib/generators/audited/migration_helper.rb +11 -0
- data/lib/generators/audited/templates/add_association_to_audits.rb +13 -0
- data/lib/generators/audited/templates/add_comment_to_audits.rb +11 -0
- data/lib/generators/audited/templates/add_remote_address_to_audits.rb +12 -0
- data/lib/generators/audited/templates/add_request_uuid_to_audits.rb +12 -0
- data/lib/generators/audited/templates/add_version_to_auditable_index.rb +23 -0
- data/lib/generators/audited/templates/create_audit_associates.rb +26 -0
- data/lib/generators/audited/templates/install.rb +39 -0
- data/lib/generators/audited/templates/rename_association_to_associated.rb +25 -0
- data/lib/generators/audited/templates/rename_changes_to_audited_changes.rb +11 -0
- data/lib/generators/audited/templates/rename_parent_to_association.rb +13 -0
- data/lib/generators/audited/templates/revert_polymorphic_indexes_order.rb +22 -0
- data/lib/generators/audited/upgrade_generator.rb +74 -0
- data/shell.nix +8 -0
- metadata +241 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Audited
|
|
4
|
+
module RspecMatchers
|
|
5
|
+
# Ensure that the model is audited.
|
|
6
|
+
#
|
|
7
|
+
# Options:
|
|
8
|
+
# * <tt>associated_with</tt> - tests that the audit makes use of the associated_with option
|
|
9
|
+
# * <tt>only</tt> - tests that the audit makes use of the only option *Overrides <tt>except</tt> option*
|
|
10
|
+
# * <tt>except</tt> - tests that the audit makes use of the except option
|
|
11
|
+
# * <tt>requires_comment</tt> - if specified, then the audit must require comments through the <tt>audit_comment</tt> attribute
|
|
12
|
+
# * <tt>on</tt> - tests that the audit makes use of the on option with specified parameters
|
|
13
|
+
#
|
|
14
|
+
# Example:
|
|
15
|
+
# it { should be_audited }
|
|
16
|
+
# it { should be_audited.associated_with(:user) }
|
|
17
|
+
# it { should be_audited.only(:field_name) }
|
|
18
|
+
# it { should be_audited.except(:password) }
|
|
19
|
+
# it { should be_audited.requires_comment }
|
|
20
|
+
# it { should be_audited.on(:create).associated_with(:user).except(:password) }
|
|
21
|
+
#
|
|
22
|
+
def be_audited
|
|
23
|
+
AuditMatcher.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Ensure that the model has associated audits
|
|
27
|
+
#
|
|
28
|
+
# Example:
|
|
29
|
+
# it { should have_associated_audits }
|
|
30
|
+
#
|
|
31
|
+
def have_associated_audits
|
|
32
|
+
AssociatedAuditMatcher.new
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class AuditMatcher # :nodoc:
|
|
36
|
+
def initialize
|
|
37
|
+
@options = {}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def associated_with(model)
|
|
41
|
+
@options[:associated_with] = model
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def only(*fields)
|
|
46
|
+
@options[:only] = fields.flatten.map(&:to_s)
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def except(*fields)
|
|
51
|
+
@options[:except] = fields.flatten.map(&:to_s)
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def requires_comment
|
|
56
|
+
@options[:comment_required] = true
|
|
57
|
+
self
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def on(*actions)
|
|
61
|
+
@options[:on] = actions.flatten.map(&:to_sym)
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def matches?(subject)
|
|
66
|
+
@subject = subject
|
|
67
|
+
auditing_enabled? && required_checks_for_options_satisfied?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def failure_message
|
|
71
|
+
"Expected #{@expectation}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def negative_failure_message
|
|
75
|
+
"Did not expect #{@expectation}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
alias_method :failure_message_when_negated, :negative_failure_message
|
|
79
|
+
|
|
80
|
+
def description
|
|
81
|
+
description = "audited"
|
|
82
|
+
description += " associated with #{@options[:associated_with]}" if @options.key?(:associated_with)
|
|
83
|
+
description += " only => #{@options[:only].join ", "}" if @options.key?(:only)
|
|
84
|
+
description += " except => #{@options[:except].join(", ")}" if @options.key?(:except)
|
|
85
|
+
description += " requires audit_comment" if @options.key?(:comment_required)
|
|
86
|
+
|
|
87
|
+
description
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
protected
|
|
91
|
+
|
|
92
|
+
def expects(message)
|
|
93
|
+
@expectation = message
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def auditing_enabled?
|
|
97
|
+
expects "#{model_class} to be audited"
|
|
98
|
+
model_class.respond_to?(:auditing_enabled) && model_class.auditing_enabled
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def model_class
|
|
102
|
+
@subject.class
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def associated_with_model?
|
|
106
|
+
expects "#{model_class} to record audits to associated model #{@options[:associated_with]}"
|
|
107
|
+
model_class.audit_associated_with == @options[:associated_with]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def records_changes_to_specified_fields?
|
|
111
|
+
ignored_fields = build_ignored_fields_from_options
|
|
112
|
+
|
|
113
|
+
expects "non audited columns (#{model_class.non_audited_columns.inspect}) to match (#{ignored_fields})"
|
|
114
|
+
model_class.non_audited_columns.to_set == ignored_fields.to_set
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def comment_required_valid?
|
|
118
|
+
expects "to require audit_comment before #{model_class.audited_options[:on]} when comment required"
|
|
119
|
+
validate_callbacks_include_presence_of_comment? && destroy_callbacks_include_comment_required?
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def only_audit_on_designated_callbacks?
|
|
123
|
+
{
|
|
124
|
+
create: [:after, :audit_create],
|
|
125
|
+
update: [:before, :audit_update],
|
|
126
|
+
destroy: [:before, :audit_destroy]
|
|
127
|
+
}.map do |(action, kind_callback)|
|
|
128
|
+
kind, callback = kind_callback
|
|
129
|
+
callbacks_for(action, kind: kind).include?(callback) if @options[:on].include?(action)
|
|
130
|
+
end.compact.all?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def validate_callbacks_include_presence_of_comment?
|
|
134
|
+
if @options[:comment_required] && audited_on_create_or_update?
|
|
135
|
+
callbacks_for(:validate).include?(:presence_of_audit_comment)
|
|
136
|
+
else
|
|
137
|
+
true
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def audited_on_create_or_update?
|
|
142
|
+
model_class.audited_options[:on].include?(:create) || model_class.audited_options[:on].include?(:update)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def destroy_callbacks_include_comment_required?
|
|
146
|
+
if @options[:comment_required] && model_class.audited_options[:on].include?(:destroy)
|
|
147
|
+
callbacks_for(:destroy).include?(:require_comment)
|
|
148
|
+
else
|
|
149
|
+
true
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def requires_comment_before_callbacks?
|
|
154
|
+
[:create, :update, :destroy].map do |action|
|
|
155
|
+
if @options[:comment_required] && model_class.audited_options[:on].include?(action)
|
|
156
|
+
callbacks_for(action).include?(:require_comment)
|
|
157
|
+
end
|
|
158
|
+
end.compact.all?
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def callbacks_for(action, kind: :before)
|
|
162
|
+
model_class.send("_#{action}_callbacks").select { |cb| cb.kind == kind }.map(&:filter)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def build_ignored_fields_from_options
|
|
166
|
+
default_ignored_attributes = model_class.default_ignored_attributes
|
|
167
|
+
|
|
168
|
+
if @options[:only].present?
|
|
169
|
+
(default_ignored_attributes | model_class.column_names) - @options[:only]
|
|
170
|
+
elsif @options[:except].present?
|
|
171
|
+
default_ignored_attributes | @options[:except]
|
|
172
|
+
else
|
|
173
|
+
default_ignored_attributes
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def required_checks_for_options_satisfied?
|
|
178
|
+
{
|
|
179
|
+
only: :records_changes_to_specified_fields?,
|
|
180
|
+
except: :records_changes_to_specified_fields?,
|
|
181
|
+
comment_required: :comment_required_valid?,
|
|
182
|
+
associated_with: :associated_with_model?,
|
|
183
|
+
on: :only_audit_on_designated_callbacks?
|
|
184
|
+
}.map do |(option, check)|
|
|
185
|
+
send(check) if @options[option].present?
|
|
186
|
+
end.compact.all?
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
class AssociatedAuditMatcher # :nodoc:
|
|
191
|
+
def matches?(subject)
|
|
192
|
+
@subject = subject
|
|
193
|
+
|
|
194
|
+
association_exists?
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def failure_message
|
|
198
|
+
"Expected #{model_class} to have associated audits"
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def negative_failure_message
|
|
202
|
+
"Expected #{model_class} to not have associated audits"
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
alias_method :failure_message_when_negated, :negative_failure_message
|
|
206
|
+
|
|
207
|
+
def description
|
|
208
|
+
"has associated audits"
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
protected
|
|
212
|
+
|
|
213
|
+
def model_class
|
|
214
|
+
@subject.class
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def reflection
|
|
218
|
+
model_class.reflect_on_association(:associated_audits)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def association_exists?
|
|
222
|
+
!reflection.nil? &&
|
|
223
|
+
reflection.macro == :has_many &&
|
|
224
|
+
reflection.options[:class_name] == Audited.audit_class.name
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Audited
|
|
4
|
+
class Sweeper
|
|
5
|
+
STORED_DATA = {
|
|
6
|
+
current_remote_address: :remote_ip,
|
|
7
|
+
current_request_uuid: :request_uuid,
|
|
8
|
+
current_user: :current_user
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
delegate :store, to: ::Audited
|
|
12
|
+
|
|
13
|
+
def around(controller)
|
|
14
|
+
self.controller = controller
|
|
15
|
+
STORED_DATA.each { |k, m| store[k] = send(m) }
|
|
16
|
+
yield
|
|
17
|
+
ensure
|
|
18
|
+
self.controller = nil
|
|
19
|
+
STORED_DATA.keys.each { |k| store.delete(k) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def current_user
|
|
23
|
+
lambda { controller.send(Audited.current_user_method) if controller.respond_to?(Audited.current_user_method, true) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def remote_ip
|
|
27
|
+
controller.try(:request).try(:remote_ip)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def request_uuid
|
|
31
|
+
controller.try(:request).try(:uuid)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def controller
|
|
35
|
+
store[:current_controller]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def controller=(value)
|
|
39
|
+
store[:current_controller] = value
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/audited.rb
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_record"
|
|
4
|
+
|
|
5
|
+
module Audited
|
|
6
|
+
# Wrapper around ActiveSupport::CurrentAttributes
|
|
7
|
+
class RequestStore < ActiveSupport::CurrentAttributes
|
|
8
|
+
attribute :audited_store
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
attr_accessor \
|
|
13
|
+
:auditing_enabled,
|
|
14
|
+
:current_user_method,
|
|
15
|
+
:ignored_attributes,
|
|
16
|
+
:ignored_default_callbacks,
|
|
17
|
+
:max_audits,
|
|
18
|
+
:store_synthesized_enums
|
|
19
|
+
attr_writer :audit_class
|
|
20
|
+
|
|
21
|
+
def audit_class
|
|
22
|
+
# The audit_class is set as String in the initializer. It can not be constantized during initialization and must
|
|
23
|
+
# be constantized at runtime. See https://github.com/collectiveidea/audited/issues/608
|
|
24
|
+
@audit_class = @audit_class.safe_constantize if @audit_class.is_a?(String)
|
|
25
|
+
@audit_class ||= Audited::Audit
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# remove audit_model in next major version it was only shortly present in 5.1.0
|
|
29
|
+
alias_method :audit_model, :audit_class
|
|
30
|
+
deprecate audit_model: "use Audited.audit_class instead of Audited.audit_model. This method will be removed.",
|
|
31
|
+
deprecator: ActiveSupport::Deprecation.new('6.0.0', 'Audited')
|
|
32
|
+
|
|
33
|
+
def store
|
|
34
|
+
RequestStore.audited_store ||= {}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def config
|
|
38
|
+
yield(self)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
@ignored_attributes = %w[lock_version created_at updated_at created_on updated_on]
|
|
43
|
+
@ignored_default_callbacks = []
|
|
44
|
+
|
|
45
|
+
@current_user_method = :current_user
|
|
46
|
+
@auditing_enabled = true
|
|
47
|
+
@store_synthesized_enums = false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
require 'audited/auditor'
|
|
51
|
+
require 'audited/audit'
|
|
52
|
+
require 'audited/audit_associate'
|
|
53
|
+
|
|
54
|
+
ActiveSupport.on_load :active_record do
|
|
55
|
+
require "audited/audit"
|
|
56
|
+
include Audited::Auditor
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
require "audited/sweeper"
|
|
60
|
+
require "audited/railtie" if Audited.const_defined?(:Rails)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/migration"
|
|
5
|
+
require "active_record"
|
|
6
|
+
require "rails/generators/active_record"
|
|
7
|
+
require "generators/audited/migration"
|
|
8
|
+
require "generators/audited/migration_helper"
|
|
9
|
+
|
|
10
|
+
module Audited
|
|
11
|
+
module Generators
|
|
12
|
+
class InstallGenerator < Rails::Generators::Base
|
|
13
|
+
include Rails::Generators::Migration
|
|
14
|
+
include Audited::Generators::MigrationHelper
|
|
15
|
+
extend Audited::Generators::Migration
|
|
16
|
+
|
|
17
|
+
class_option :audited_changes_column_type, type: :string, default: "text", required: false
|
|
18
|
+
class_option :audited_user_id_column_type, type: :string, default: "integer", required: false
|
|
19
|
+
|
|
20
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
21
|
+
|
|
22
|
+
def copy_migration
|
|
23
|
+
migration_template "install.rb", "db/migrate/install_audited.rb"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Audited
|
|
4
|
+
module Generators
|
|
5
|
+
module Migration
|
|
6
|
+
# Implement the required interface for Rails::Generators::Migration.
|
|
7
|
+
def next_migration_number(dirname) # :nodoc:
|
|
8
|
+
next_migration_number = current_migration_number(dirname) + 1
|
|
9
|
+
if timestamped_migrations?
|
|
10
|
+
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
|
|
11
|
+
else
|
|
12
|
+
"%.3d" % next_migration_number
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def timestamped_migrations?
|
|
19
|
+
(::ActiveRecord.version >= Gem::Version.new("7.0")) ?
|
|
20
|
+
::ActiveRecord.timestamped_migrations :
|
|
21
|
+
::ActiveRecord::Base.timestamped_migrations
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
|
4
|
+
def self.up
|
|
5
|
+
add_column :audits, :association_id, :integer
|
|
6
|
+
add_column :audits, :association_type, :string
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.down
|
|
10
|
+
remove_column :audits, :association_type
|
|
11
|
+
remove_column :audits, :association_id
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
|
4
|
+
def self.up
|
|
5
|
+
add_column :audits, :request_uuid, :string
|
|
6
|
+
add_index :audits, :request_uuid
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.down
|
|
10
|
+
remove_column :audits, :request_uuid
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
|
4
|
+
def self.up
|
|
5
|
+
if index_exists?(:audits, [:auditable_type, :auditable_id], name: index_name)
|
|
6
|
+
remove_index :audits, name: index_name
|
|
7
|
+
add_index :audits, [:auditable_type, :auditable_id, :version], name: index_name
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.down
|
|
12
|
+
if index_exists?(:audits, [:auditable_type, :auditable_id, :version], name: index_name)
|
|
13
|
+
remove_index :audits, name: index_name
|
|
14
|
+
add_index :audits, [:auditable_type, :auditable_id], name: index_name
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def index_name
|
|
21
|
+
'auditable_index'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
|
2
|
+
def self.up
|
|
3
|
+
create_table :audited_audit_associates, :force => true do |t|
|
|
4
|
+
t.column :audit_id, :integer
|
|
5
|
+
t.column :associated_id, :integer
|
|
6
|
+
t.column :associated_type, :string
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
execute <<-SQL
|
|
10
|
+
INSERT INTO audited_audit_associates (audit_id, associated_id, associated_type)
|
|
11
|
+
SELECT id, associated_id, associated_type
|
|
12
|
+
FROM audits
|
|
13
|
+
SQL
|
|
14
|
+
|
|
15
|
+
add_index :audited_audit_associates, :audit_id, :name => 'index_audited_audit_associates_on_audit_id'
|
|
16
|
+
add_index :audited_audit_associates, [:associated_type, :associated_id], :name => 'index_audited_audit_associates_on_associated'
|
|
17
|
+
|
|
18
|
+
remove_index :audits, name: 'associated_index'
|
|
19
|
+
remove_column :audits, :associated_id
|
|
20
|
+
remove_column :audits, :associated_type
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.down
|
|
24
|
+
raise ActiveRecord::IrreversibleMigration
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
|
4
|
+
def self.up
|
|
5
|
+
create_table :audits, :force => true do |t|
|
|
6
|
+
t.column :auditable_id, :integer
|
|
7
|
+
t.column :auditable_type, :string
|
|
8
|
+
t.column :user_id, :<%= options[:audited_user_id_column_type] %>
|
|
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_type, :auditable_id, :version], :name => 'auditable_index'
|
|
21
|
+
add_index :audits, [:user_id, :user_type], :name => 'user_index'
|
|
22
|
+
add_index :audits, :request_uuid
|
|
23
|
+
add_index :audits, :created_at
|
|
24
|
+
|
|
25
|
+
create_table :audited_audit_associates, :force => true do |t|
|
|
26
|
+
t.column :audit_id, :integer
|
|
27
|
+
t.column :associated_id, :integer
|
|
28
|
+
t.column :associated_type, :string
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
add_index :audited_audit_associates, :audit_id, :name => 'index_audited_audit_associates_on_audit_id'
|
|
32
|
+
add_index :audited_audit_associates, [:associated_type, :associated_id], :name => 'index_audited_audit_associates_on_associated'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.down
|
|
36
|
+
drop_table :audited_audit_associates
|
|
37
|
+
drop_table :audits
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
|
4
|
+
def self.up
|
|
5
|
+
if index_exists? :audits, [:association_id, :association_type], :name => 'association_index'
|
|
6
|
+
remove_index :audits, :name => 'association_index'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
rename_column :audits, :association_id, :associated_id
|
|
10
|
+
rename_column :audits, :association_type, :associated_type
|
|
11
|
+
|
|
12
|
+
add_index :audits, [:associated_id, :associated_type], :name => 'associated_index'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.down
|
|
16
|
+
if index_exists? :audits, [:associated_id, :associated_type], :name => 'associated_index'
|
|
17
|
+
remove_index :audits, :name => 'associated_index'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
rename_column :audits, :associated_type, :association_type
|
|
21
|
+
rename_column :audits, :associated_id, :association_id
|
|
22
|
+
|
|
23
|
+
add_index :audits, [:association_id, :association_type], :name => 'association_index'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
|
4
|
+
def self.up
|
|
5
|
+
rename_column :audits, :auditable_parent_id, :association_id
|
|
6
|
+
rename_column :audits, :auditable_parent_type, :association_type
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.down
|
|
10
|
+
rename_column :audits, :association_type, :auditable_parent_type
|
|
11
|
+
rename_column :audits, :association_id, :auditable_parent_id
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
|
4
|
+
def self.up
|
|
5
|
+
fix_index_order_for [:associated_id, :associated_type], 'associated_index'
|
|
6
|
+
fix_index_order_for [:auditable_id, :auditable_type], 'auditable_index'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.down
|
|
10
|
+
fix_index_order_for [:associated_type, :associated_id], 'associated_index'
|
|
11
|
+
fix_index_order_for [:auditable_type, :auditable_id], 'auditable_index'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def fix_index_order_for(columns, index_name)
|
|
17
|
+
if index_exists? :audits, columns, name: index_name
|
|
18
|
+
remove_index :audits, name: index_name
|
|
19
|
+
add_index :audits, columns.reverse, name: index_name
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|