velocity_audited 5.1.3
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/.github/workflows/ci.yml +115 -0
- data/.gitignore +17 -0
- data/.standard.yml +5 -0
- data/.yardopts +3 -0
- data/Appraisals +44 -0
- data/CHANGELOG.md +419 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +433 -0
- data/Rakefile +18 -0
- data/gemfiles/rails50.gemfile +10 -0
- data/gemfiles/rails51.gemfile +10 -0
- data/gemfiles/rails52.gemfile +10 -0
- data/gemfiles/rails60.gemfile +10 -0
- data/gemfiles/rails61.gemfile +10 -0
- data/gemfiles/rails70.gemfile +10 -0
- data/lib/audited/audit.rb +198 -0
- data/lib/audited/auditor.rb +476 -0
- data/lib/audited/railtie.rb +16 -0
- data/lib/audited/rspec_matchers.rb +228 -0
- data/lib/audited/sweeper.rb +67 -0
- data/lib/audited/version.rb +5 -0
- data/lib/audited-rspec.rb +6 -0
- data/lib/audited.rb +49 -0
- data/lib/generators/audited/install_generator.rb +27 -0
- data/lib/generators/audited/migration.rb +17 -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/install.rb +32 -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 +70 -0
- data/lib/velocity_audited.rb +5 -0
- data/spec/audited/audit_spec.rb +357 -0
- data/spec/audited/auditor_spec.rb +1097 -0
- data/spec/audited/rspec_matchers_spec.rb +69 -0
- data/spec/audited/sweeper_spec.rb +133 -0
- data/spec/audited_spec.rb +18 -0
- data/spec/audited_spec_helpers.rb +32 -0
- data/spec/rails_app/app/assets/config/manifest.js +2 -0
- data/spec/rails_app/config/application.rb +13 -0
- data/spec/rails_app/config/database.yml +26 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/test.rb +47 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +2 -0
- data/spec/rails_app/config/initializers/secret_token.rb +3 -0
- data/spec/rails_app/config/routes.rb +3 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/active_record/models.rb +151 -0
- data/spec/support/active_record/postgres/1_change_audited_changes_type_to_json.rb +11 -0
- data/spec/support/active_record/postgres/2_change_audited_changes_type_to_jsonb.rb +11 -0
- data/spec/support/active_record/schema.rb +90 -0
- data/test/db/version_1.rb +17 -0
- data/test/db/version_2.rb +18 -0
- data/test/db/version_3.rb +18 -0
- data/test/db/version_4.rb +19 -0
- data/test/db/version_5.rb +17 -0
- data/test/db/version_6.rb +19 -0
- data/test/install_generator_test.rb +62 -0
- data/test/test_helper.rb +18 -0
- data/test/upgrade_generator_test.rb +97 -0
- metadata +260 -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,67 @@
|
|
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
|
+
origin: :origin,
|
10
|
+
user_agent: :user_agent,
|
11
|
+
ip: :ip,
|
12
|
+
uid: :uid,
|
13
|
+
client: :client
|
14
|
+
}
|
15
|
+
|
16
|
+
delegate :store, to: ::Audited
|
17
|
+
|
18
|
+
def around(controller)
|
19
|
+
self.controller = controller
|
20
|
+
STORED_DATA.each { |k, m| store[k] = send(m) }
|
21
|
+
yield
|
22
|
+
ensure
|
23
|
+
self.controller = nil
|
24
|
+
STORED_DATA.keys.each { |k| store.delete(k) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def current_user
|
28
|
+
lambda { controller.send(Audited.current_user_method) if controller.respond_to?(Audited.current_user_method, true) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def remote_ip
|
32
|
+
controller.try(:request).try(:remote_ip)
|
33
|
+
end
|
34
|
+
|
35
|
+
def request_uuid
|
36
|
+
controller.try(:request).try(:uuid)
|
37
|
+
end
|
38
|
+
|
39
|
+
def origin
|
40
|
+
controller.try(:request).try(:headers)['Origin']
|
41
|
+
end
|
42
|
+
|
43
|
+
def user_agent
|
44
|
+
controller.try(:request).try(:headers)['User-Agent']
|
45
|
+
end
|
46
|
+
|
47
|
+
def ip
|
48
|
+
controller.try(:request).try(:headers)['HTTP_X_FORWARDED_FOR']
|
49
|
+
end
|
50
|
+
|
51
|
+
def uid
|
52
|
+
controller.try(:request).try(:headers)['uid']
|
53
|
+
end
|
54
|
+
|
55
|
+
def client
|
56
|
+
controller.try(:request).try(:headers)['client']
|
57
|
+
end
|
58
|
+
|
59
|
+
def controller
|
60
|
+
store[:current_controller]
|
61
|
+
end
|
62
|
+
|
63
|
+
def controller=(value)
|
64
|
+
store[:current_controller] = value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/audited.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
|
5
|
+
module Audited
|
6
|
+
class << self
|
7
|
+
attr_accessor \
|
8
|
+
:auditing_enabled,
|
9
|
+
:current_user_method,
|
10
|
+
:ignored_attributes,
|
11
|
+
:max_audits,
|
12
|
+
:store_synthesized_enums
|
13
|
+
attr_writer :audit_class
|
14
|
+
|
15
|
+
def audit_class
|
16
|
+
@audit_class ||= Audit
|
17
|
+
end
|
18
|
+
|
19
|
+
def store
|
20
|
+
current_store_value = Thread.current.thread_variable_get(:audited_store)
|
21
|
+
|
22
|
+
if current_store_value.nil?
|
23
|
+
Thread.current.thread_variable_set(:audited_store, {})
|
24
|
+
else
|
25
|
+
current_store_value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def config
|
30
|
+
yield(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@ignored_attributes = %w[lock_version created_at updated_at created_on updated_on]
|
35
|
+
|
36
|
+
@current_user_method = :current_user
|
37
|
+
@auditing_enabled = true
|
38
|
+
@store_synthesized_enums = false
|
39
|
+
end
|
40
|
+
|
41
|
+
require "audited/auditor"
|
42
|
+
|
43
|
+
ActiveSupport.on_load :active_record do
|
44
|
+
require "audited/audit"
|
45
|
+
include Audited::Auditor
|
46
|
+
end
|
47
|
+
|
48
|
+
require "audited/sweeper"
|
49
|
+
require "audited/railtie"
|
@@ -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,17 @@
|
|
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 ::ActiveRecord::Base.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
|
+
end
|
16
|
+
end
|
17
|
+
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,32 @@
|
|
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 :associated_id, :integer
|
9
|
+
t.column :associated_type, :string
|
10
|
+
t.column :user_id, :<%= options[:audited_user_id_column_type] %>
|
11
|
+
t.column :user_type, :string
|
12
|
+
t.column :username, :string
|
13
|
+
t.column :action, :string
|
14
|
+
t.column :audited_changes, :<%= options[:audited_changes_column_type] %>
|
15
|
+
t.column :version, :integer, :default => 0
|
16
|
+
t.column :comment, :string
|
17
|
+
t.column :remote_address, :string
|
18
|
+
t.column :request_uuid, :string
|
19
|
+
t.column :created_at, :datetime
|
20
|
+
end
|
21
|
+
|
22
|
+
add_index :audits, [:auditable_type, :auditable_id, :version], :name => 'auditable_index'
|
23
|
+
add_index :audits, [:associated_type, :associated_id], :name => 'associated_index'
|
24
|
+
add_index :audits, [:user_id, :user_type], :name => 'user_index'
|
25
|
+
add_index :audits, :request_uuid
|
26
|
+
add_index :audits, :created_at
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.down
|
30
|
+
drop_table :audits
|
31
|
+
end
|
32
|
+
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
|
@@ -0,0 +1,70 @@
|
|
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 UpgradeGenerator < Rails::Generators::Base
|
13
|
+
include Rails::Generators::Migration
|
14
|
+
include Audited::Generators::MigrationHelper
|
15
|
+
extend Audited::Generators::Migration
|
16
|
+
|
17
|
+
source_root File.expand_path("../templates", __FILE__)
|
18
|
+
|
19
|
+
def copy_templates
|
20
|
+
migrations_to_be_applied do |m|
|
21
|
+
migration_template "#{m}.rb", "db/migrate/#{m}.rb"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def migrations_to_be_applied
|
28
|
+
Audited::Audit.reset_column_information
|
29
|
+
columns = Audited::Audit.columns.map(&:name)
|
30
|
+
indexes = Audited::Audit.connection.indexes(Audited::Audit.table_name)
|
31
|
+
|
32
|
+
yield :add_comment_to_audits unless columns.include?("comment")
|
33
|
+
|
34
|
+
if columns.include?("changes")
|
35
|
+
yield :rename_changes_to_audited_changes
|
36
|
+
end
|
37
|
+
|
38
|
+
unless columns.include?("remote_address")
|
39
|
+
yield :add_remote_address_to_audits
|
40
|
+
end
|
41
|
+
|
42
|
+
unless columns.include?("request_uuid")
|
43
|
+
yield :add_request_uuid_to_audits
|
44
|
+
end
|
45
|
+
|
46
|
+
unless columns.include?("association_id")
|
47
|
+
if columns.include?("auditable_parent_id")
|
48
|
+
yield :rename_parent_to_association
|
49
|
+
else
|
50
|
+
unless columns.include?("associated_id")
|
51
|
+
yield :add_association_to_audits
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if columns.include?("association_id")
|
57
|
+
yield :rename_association_to_associated
|
58
|
+
end
|
59
|
+
|
60
|
+
if indexes.any? { |i| i.columns == %w[associated_id associated_type] }
|
61
|
+
yield :revert_polymorphic_indexes_order
|
62
|
+
end
|
63
|
+
|
64
|
+
if indexes.any? { |i| i.columns == %w[auditable_type auditable_id] }
|
65
|
+
yield :add_version_to_auditable_index
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|