models_auditor 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +37 -0
- data/app/assets/javascripts/models_auditor/application.js +13 -0
- data/app/assets/stylesheets/models_auditor/application.css +64 -0
- data/app/controllers/models_auditor/audit_base_controller.rb +5 -0
- data/app/controllers/models_auditor/audit_controller.rb +121 -0
- data/app/helpers/models_auditor/application_helper.rb +4 -0
- data/app/models/models_auditor/audit_record.rb +17 -0
- data/app/models/models_auditor/audit_request.rb +12 -0
- data/app/views/layouts/models_auditor/application.html.erb +12 -0
- data/app/views/models_auditor/audit/_record.html.erb +39 -0
- data/app/views/models_auditor/audit/_request.html.erb +25 -0
- data/app/views/models_auditor/audit/index.html.erb +3 -0
- data/config/config_option_descriptions.yml +23 -0
- data/config/routes.rb +5 -0
- data/lib/generators/models_auditor/db_config/USAGE +7 -0
- data/lib/generators/models_auditor/db_config/db_config_generator.rb +23 -0
- data/lib/generators/models_auditor/install/USAGE +8 -0
- data/lib/generators/models_auditor/install/install_generator.rb +25 -0
- data/lib/generators/models_auditor/install/templates/initializer.rb.erb +13 -0
- data/lib/generators/models_auditor/migrations/USAGE +8 -0
- data/lib/generators/models_auditor/migrations/migrations_generator.rb +15 -0
- data/lib/generators/models_auditor/migrations/templates/create_audit_records.rb.erb +18 -0
- data/lib/generators/models_auditor/migrations/templates/create_audit_requests.rb.erb +12 -0
- data/lib/generators/models_auditor/migrations_helper.rb +24 -0
- data/lib/models_auditor.rb +37 -0
- data/lib/models_auditor/audit.rb +202 -0
- data/lib/models_auditor/config.rb +118 -0
- data/lib/models_auditor/controller.rb +67 -0
- data/lib/models_auditor/engine.rb +5 -0
- data/lib/models_auditor/version.rb +3 -0
- data/lib/tasks/models_auditor_tasks.rake +75 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/integration/navigation_test.rb +8 -0
- data/test/models_auditor_test.rb +7 -0
- data/test/test_helper.rb +21 -0
- metadata +249 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module ModelsAuditor
|
3
|
+
class DbConfigGenerator < Rails::Generators::Base
|
4
|
+
def add_db_config
|
5
|
+
if (nmsps = ModelsAuditor.config.connection_namespace).present?
|
6
|
+
inject_into_file 'config/database.yml', before: /\z/ do
|
7
|
+
"\n#{nmsps}_development: &#{nmsps}_development\n" +
|
8
|
+
" adapter: postgresql\n" +
|
9
|
+
" encoding: unicode\n" +
|
10
|
+
" database: audit_database\n" +
|
11
|
+
" pool: 5\n" +
|
12
|
+
" host: localhost\n" +
|
13
|
+
" username: audit_user\n" +
|
14
|
+
" password: \n" +
|
15
|
+
"#{nmsps}_production: *#{nmsps}_development\n" +
|
16
|
+
"#{nmsps}_staging: *#{nmsps}_development\n" +
|
17
|
+
"#{nmsps}_test: *#{nmsps}_development\n"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module ModelsAuditor
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
|
6
|
+
def descriptions
|
7
|
+
@descriptions ||= YAML.load_file(File.expand_path('../../../../../config/config_option_descriptions.yml', __FILE__))
|
8
|
+
end
|
9
|
+
|
10
|
+
def copy_initializer
|
11
|
+
template 'initializer.rb.erb', 'config/initializers/models_auditor.rb'
|
12
|
+
end
|
13
|
+
|
14
|
+
# def mount_routes
|
15
|
+
# [
|
16
|
+
# 'Rails.application.routes.draw do',
|
17
|
+
# 'Application.routes.draw do'
|
18
|
+
# ].each do |after_str|
|
19
|
+
# inject_into_file 'config/routes.rb', :after => after_str do
|
20
|
+
# "\n mount GlobalStore::Engine, at: '/global_store'\n"
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Настройки аудитора моделей
|
2
|
+
|
3
|
+
ModelsAuditor.configure do |config|
|
4
|
+
<%
|
5
|
+
(ModelsAuditor::Config::CONFIG_OPTIONS).each do |option_sym|
|
6
|
+
descr = descriptions[option_sym.to_s].try(:split, "\n").try(:join, "\n # ")
|
7
|
+
concat " # #{descr.force_encoding('ASCII-8BIT')}\n" if descr.present?
|
8
|
+
concat " # config.#{option_sym} = "
|
9
|
+
concat (ModelsAuditor.config.default[option_sym].try(:[], :config) || 'nil').force_encoding('ASCII-8BIT')
|
10
|
+
concat "\n\n"
|
11
|
+
end
|
12
|
+
%>
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
require 'generators/models_auditor/migrations_helper'
|
3
|
+
|
4
|
+
module ModelsAuditor
|
5
|
+
class MigrationsGenerator < Rails::Generators::Base
|
6
|
+
include MigrationsHelper
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
def create_migration_file
|
10
|
+
@migration_postfix = SecureRandom.hex
|
11
|
+
copy_migration 'create_audit_records', "create_audit_records_n#{@migration_postfix}"
|
12
|
+
copy_migration 'create_audit_requests', "create_audit_requests_n#{@migration_postfix}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateAuditRecordsN<%= @migration_postfix %> < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
<% audit_tbn = ModelsAuditor.config.audit_records_table_name %>
|
4
|
+
create_table :<%= audit_tbn %>, comment: 'Журнал изменений данных в моделях' do |t|
|
5
|
+
t.integer :request_id, null: true, unsigned: true, comment: 'зафиксированные изменения'
|
6
|
+
t.integer :action, null: false, unsigned: true, comment: 'действие'
|
7
|
+
t.json :content, null: false, default: {}, comment: 'зафиксированные изменения'
|
8
|
+
t.string :object_type, null: false, comment: 'класс логируемого объекта'
|
9
|
+
t.integer :object_id, null: false, unsigned: true, comment: 'id логируемого объекта'
|
10
|
+
t.json :bridge, null: true, comment: 'данные внешних ключей связующей таблицы'
|
11
|
+
t.datetime :created_at, null: false, comment: 'дата и время зафиксированных изменений'
|
12
|
+
end
|
13
|
+
add_index :<%= audit_tbn %>, :id
|
14
|
+
add_index :<%= audit_tbn %>, :request_id
|
15
|
+
add_index :<%= audit_tbn %>, :created_at
|
16
|
+
add_index :<%= audit_tbn %>, [:object_id, :object_type]
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateAuditRequestsN<%= @migration_postfix %> < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
<% audit_tbn = ModelsAuditor.config.audit_requests_table_name %>
|
4
|
+
create_table :<%= audit_tbn %>, comment: 'Журнал изменений данных в моделях' do |t|
|
5
|
+
t.integer :user_id, null: true, unsigned: true, comment: 'id ответственного'
|
6
|
+
t.json :request_info, null: false, default: {}, comment: 'Информация о запросе'
|
7
|
+
t.datetime :created_at, null: false, comment: 'дата и время запроса'
|
8
|
+
end
|
9
|
+
add_index :<%= audit_tbn %>, :id
|
10
|
+
add_index :<%= audit_tbn %>, :created_at
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ModelsAuditor
|
2
|
+
module MigrationsHelper
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
def self.next_migration_number(dirname)
|
9
|
+
next_migration_number = current_migration_number(dirname) + 1
|
10
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy_migration(filename, destination)
|
15
|
+
migrations_dir = File.join('db', ModelsAuditor.config.audit_migrations_dir)
|
16
|
+
|
17
|
+
if self.class.migration_exists?(migrations_dir, "#{destination}.rb")
|
18
|
+
say_status('skipped', "Migration #{destination}.rb already exists in #{migrations_dir}")
|
19
|
+
else
|
20
|
+
migration_template "#{filename}.rb.erb", File.join(migrations_dir, "#{destination}.rb")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'request_store'
|
2
|
+
require 'models_auditor/engine'
|
3
|
+
require 'models_auditor/config'
|
4
|
+
require 'models_auditor/audit'
|
5
|
+
require 'models_auditor/controller'
|
6
|
+
|
7
|
+
module ModelsAuditor
|
8
|
+
module_function
|
9
|
+
def log_error(*args)
|
10
|
+
if (logger = ModelsAuditor.config.logger)
|
11
|
+
logger.error(*args)
|
12
|
+
end
|
13
|
+
puts *args
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_info(*args)
|
17
|
+
if (logger = ModelsAuditor.config.logger)
|
18
|
+
logger.info(*args)
|
19
|
+
end
|
20
|
+
puts *args
|
21
|
+
end
|
22
|
+
|
23
|
+
def log_warn(*args)
|
24
|
+
if (logger = ModelsAuditor.config.logger)
|
25
|
+
logger.warn(*args)
|
26
|
+
end
|
27
|
+
puts *args
|
28
|
+
end
|
29
|
+
|
30
|
+
def store
|
31
|
+
RequestStore.store[:models_auditor_store] ||= {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
ActiveSupport.on_load(:active_record) do
|
36
|
+
include ModelsAuditor::Audit
|
37
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module ModelsAuditor
|
2
|
+
module Audit
|
3
|
+
# Сбор данных через метод #as_json
|
4
|
+
# @example enable_audit ModelsAuditor::Audit::AUDIT_MODE_JSON, only: [:title, :subtitle, :published_at]
|
5
|
+
AUDIT_MODE_JSON = 1
|
6
|
+
# Сбор данных через сериалайзер
|
7
|
+
# @example enable_audit ModelsAuditor::Audit::AUDIT_MODE_SERIALIZER, serializer: AuditPostSerializer
|
8
|
+
AUDIT_MODE_SERIALIZER = 2
|
9
|
+
# Сбор данных через назначенный метод
|
10
|
+
# @example enable_audit ModelsAuditor::Audit::AUDIT_MODE_SERIALIZER, method: :logged_data
|
11
|
+
AUDIT_MODE_METHOD = 3
|
12
|
+
# Сбор данных через #previous_changes
|
13
|
+
# @example enable_audit ModelsAuditor::Audit::AUDIT_MODE_CHANGES_ONLY
|
14
|
+
AUDIT_MODE_CHANGES_ONLY = 4
|
15
|
+
|
16
|
+
AUDIT_SNAPSHOT_MODES = [AUDIT_MODE_JSON, AUDIT_MODE_SERIALIZER, AUDIT_MODE_METHOD]
|
17
|
+
AUDIT_CHANGES_MODES = [AUDIT_MODE_CHANGES_ONLY]
|
18
|
+
|
19
|
+
def self.included(base)
|
20
|
+
base.send :extend, ClassMethods
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
def do_audit_init_snapshot
|
25
|
+
return unless ModelsAuditor.config.audit_enabled
|
26
|
+
mode = self.class.instance_variable_get(:@audit_mode)
|
27
|
+
return unless self.class.instance_variable_get(:@audit_enabled) && AUDIT_SNAPSHOT_MODES.include?(mode)
|
28
|
+
ma_store_initial_state(ModelsAuditor.store)
|
29
|
+
end
|
30
|
+
|
31
|
+
def do_audit_process
|
32
|
+
return unless ModelsAuditor.config.audit_enabled
|
33
|
+
return unless self.class.instance_variable_get(:@audit_enabled)
|
34
|
+
mode = self.class.instance_variable_get(:@audit_mode)
|
35
|
+
options = self.class.instance_variable_get(:@audit_settings) || {}
|
36
|
+
store = ModelsAuditor.store
|
37
|
+
|
38
|
+
initial_data = ma_get_initial_state(store)
|
39
|
+
current_data = ma_auditor_get_data
|
40
|
+
|
41
|
+
action =
|
42
|
+
case
|
43
|
+
when transaction_include_any_action?([:create])
|
44
|
+
ModelsAuditor::AuditRecord::ACTION_CREATE
|
45
|
+
when transaction_include_any_action?([:update])
|
46
|
+
ModelsAuditor::AuditRecord::ACTION_UPDATE
|
47
|
+
when transaction_include_any_action?([:destroy])
|
48
|
+
ModelsAuditor::AuditRecord::ACTION_DESTROY
|
49
|
+
end
|
50
|
+
|
51
|
+
bridge =
|
52
|
+
if options[:bridge]
|
53
|
+
options[:bridge].each_with_object({}) { |(model_name, key), o| o[model_name] = {key => __send__(key)} }
|
54
|
+
end
|
55
|
+
|
56
|
+
Thread.new do
|
57
|
+
begin
|
58
|
+
log_anyway = !ModelsAuditor.config.audit_request_changes_only
|
59
|
+
if (request = store[:audit_request]) || log_anyway
|
60
|
+
body =
|
61
|
+
case
|
62
|
+
when AUDIT_SNAPSHOT_MODES.include?(mode)
|
63
|
+
ma_eliminate_not_changed_keys(initial_data, current_data)
|
64
|
+
when AUDIT_CHANGES_MODES.include?(mode)
|
65
|
+
current_data
|
66
|
+
else
|
67
|
+
raise ArgumentError.new('Incorrect value of argument audit_type')
|
68
|
+
end
|
69
|
+
|
70
|
+
if request.try(:new_record?) && !request.save
|
71
|
+
ModelsAuditor.log_error("Couldn't save request record")
|
72
|
+
ModelsAuditor.log_error(request.errors.full_messages)
|
73
|
+
return
|
74
|
+
end
|
75
|
+
record =
|
76
|
+
ModelsAuditor::AuditRecord.new(
|
77
|
+
request: request,
|
78
|
+
auditable: self,
|
79
|
+
content: body,
|
80
|
+
action: action,
|
81
|
+
bridge: bridge
|
82
|
+
)
|
83
|
+
unless record.save
|
84
|
+
ModelsAuditor.log_error("Couldn't logged changes of #{self.class.name} id: #{self.try(:id)}")
|
85
|
+
ModelsAuditor.log_error(record.errors.full_messages)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
rescue Exception => e
|
89
|
+
ModelsAuditor.log_error("Couldn't logged changes of #{self.class.name} id: #{self.try(:id)}")
|
90
|
+
ModelsAuditor.log_error(e.message)
|
91
|
+
ModelsAuditor.log_error(e.backtrace.take(100).join("\n"))
|
92
|
+
end
|
93
|
+
# TODO To remove the #join call from the thread block after debugging
|
94
|
+
end.join
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Сравнивает два хэша, оставляя только отличающиеся по значению ключи
|
100
|
+
# @return [Hash] filtered result with different attributes only
|
101
|
+
def ma_eliminate_not_changed_keys(old_hash, new_hash)
|
102
|
+
case
|
103
|
+
# Равны или оба nil
|
104
|
+
when old_hash == new_hash
|
105
|
+
{}
|
106
|
+
# Один из них nil
|
107
|
+
when (old_hash && new_hash).nil?
|
108
|
+
(old_hash || new_hash).keys.each_with_object({}) do |key, o|
|
109
|
+
if (was = old_hash.try(:[], key)) != (now = new_hash.try(:[], key))
|
110
|
+
o[key] = [was, now]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
else # Оба не nil
|
114
|
+
(old_hash.keys | new_hash.keys).each_with_object({}) do |key, o|
|
115
|
+
if (was = old_hash[key]) != (now = new_hash[key])
|
116
|
+
o[key] = [was, now]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Запоминает сериализованные данные для аудита
|
123
|
+
# Вызывать данный метод следует в коллбэке after_initialize
|
124
|
+
# Или в любом другом месте до изменения значений аттрибутов
|
125
|
+
def ma_store_initial_state(store)
|
126
|
+
store[:initial_states] ||= {}
|
127
|
+
states_of_mclass = (store[:initial_states][self.class.name] ||= {})
|
128
|
+
states_of_mclass[self.id] ||= ma_auditor_get_data
|
129
|
+
end
|
130
|
+
|
131
|
+
# Получает сериализованные данные для аудита подготовленные при инициализации сущности
|
132
|
+
# @return [Hash|nil] Начальные данные
|
133
|
+
def ma_get_initial_state(store)
|
134
|
+
store[:initial_states].try(:[], self.class.name).try(:[], self.id)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Получает сериализованные данные для аудита
|
138
|
+
def ma_auditor_get_data
|
139
|
+
options = self.class.instance_variable_get(:@audit_settings) || {}
|
140
|
+
audit_params = options[:params]
|
141
|
+
mode = self.class.instance_variable_get(:@audit_mode)
|
142
|
+
case mode
|
143
|
+
when AUDIT_MODE_JSON
|
144
|
+
self.as_json(audit_params)
|
145
|
+
when AUDIT_MODE_SERIALIZER
|
146
|
+
if (serializer = options[:serializer]).blank?
|
147
|
+
raise ArgumentError.new('Required option :serializer for AUDIT_MODE_SERIALIZER was not passed')
|
148
|
+
end
|
149
|
+
serializer.new(self, audit_params || {}).as_json
|
150
|
+
when AUDIT_MODE_METHOD
|
151
|
+
if (method = options[:serializer]).blank?
|
152
|
+
raise ArgumentError.new('Required option :method for AUDIT_MODE_METHOD was not passed')
|
153
|
+
end
|
154
|
+
unless self.respond_to?(method)
|
155
|
+
raise ArgumentError.new("Passed method '#{method}' is undefined")
|
156
|
+
end
|
157
|
+
self.__send__(method)
|
158
|
+
when AUDIT_MODE_CHANGES_ONLY
|
159
|
+
self.previous_changes
|
160
|
+
else
|
161
|
+
raise ArgumentError.new('Incorrect value of argument audit_type')
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
module ClassMethods
|
168
|
+
# Активирует аудит изменений данных модели
|
169
|
+
# @param [Integer] audit_mode Способ логирования
|
170
|
+
# возможные значения: AUDIT_MODE_JSON | AUDIT_MODE_SERIALIZER | AUDIT_MODE_METHOD | AUDIT_MODE_CHANGES_ONLY
|
171
|
+
# AUDIT_MODE_JSON - Сериализация путем вызова метода as_json
|
172
|
+
# AUDIT_MODE_SERIALIZER - Сериализация через использование сериалайзера, указанного в опции :serializer
|
173
|
+
# AUDIT_MODE_METHOD - Сериализация данных формируемых в методе, указанном в опции :method
|
174
|
+
# AUDIT_MODE_CHANGES_ONLY - Сериализация данных модели, которые были изменены
|
175
|
+
# @param [Hash] options Настройки логирования
|
176
|
+
# @option options [params] Параметры сериализации данных.
|
177
|
+
# Для AUDIT_MODE_JSON значение передается в метод #as_json
|
178
|
+
# @example enable_audit ModelsAuditor::Audit::AUDIT_MODE_JSON, only: [:title, :subtitle, :published_at]
|
179
|
+
# Для AUDIT_MODE_SERIALIZER значение передается в сериалайзер в качестве опций
|
180
|
+
# @example enable_audit ModelsAuditor::Audit::AUDIT_MODE_SERIALIZER, serializer: AuditPostSerializer
|
181
|
+
# Для AUDIT_MODE_METHOD значение игнорируется
|
182
|
+
# @example enable_audit ModelsAuditor::Audit::AUDIT_MODE_SERIALIZER, method: :logged_data
|
183
|
+
# Для AUDIT_MODE_CHANGES_ONLY значение игнорируется
|
184
|
+
# @example enable_audit ModelsAuditor::Audit::AUDIT_MODE_CHANGES_ONLY
|
185
|
+
def enable_audit(audit_mode, options = {})
|
186
|
+
@audit_enabled = true
|
187
|
+
@audit_mode = audit_mode
|
188
|
+
@audit_settings = options
|
189
|
+
# Lazily include the instance methods so we don't clutter up
|
190
|
+
# any more ActiveRecord models than we have to.
|
191
|
+
send :include, InstanceMethods
|
192
|
+
after_initialize :do_audit_init_snapshot
|
193
|
+
after_commit :do_audit_process
|
194
|
+
end
|
195
|
+
|
196
|
+
# Дезактивирует аудит изменений данных модели
|
197
|
+
def disable_audit
|
198
|
+
@audit_enabled = false
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module ModelsAuditor
|
2
|
+
class Config
|
3
|
+
CONFIG_OPTIONS = %i(
|
4
|
+
audit_enabled
|
5
|
+
connection_namespace
|
6
|
+
audit_records_table_name
|
7
|
+
audit_requests_table_name
|
8
|
+
audit_migrations_dir
|
9
|
+
audit_request_changes_only
|
10
|
+
logger
|
11
|
+
records_per_page
|
12
|
+
fake_total_count
|
13
|
+
audit_controller_base
|
14
|
+
respond_to_json_enabled
|
15
|
+
respond_to_html_enabled
|
16
|
+
json_response_data_key
|
17
|
+
json_response_meta_key
|
18
|
+
)
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@indexed_relations = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def default
|
25
|
+
@default ||= {
|
26
|
+
audit_enabled: {
|
27
|
+
config: 'true',
|
28
|
+
val: true
|
29
|
+
},
|
30
|
+
connection_namespace: {
|
31
|
+
config: "'audit'",
|
32
|
+
val: 'audit'
|
33
|
+
},
|
34
|
+
audit_records_table_name: {
|
35
|
+
config: "'audit_records'",
|
36
|
+
val: 'audit_records'
|
37
|
+
},
|
38
|
+
audit_requests_table_name: {
|
39
|
+
config: "'audit_requests'",
|
40
|
+
val: 'audit_requests'
|
41
|
+
},
|
42
|
+
audit_migrations_dir: {
|
43
|
+
config: "'audit_migrate'",
|
44
|
+
val: 'audit_migrate'
|
45
|
+
},
|
46
|
+
logger: {
|
47
|
+
config: "Logger.new(Rails.root.join('log', 'models_auditor.log'))",
|
48
|
+
val: Logger.new(Rails.root.join('log', 'models_auditor.log'))
|
49
|
+
},
|
50
|
+
audit_request_changes_only: {
|
51
|
+
config: 'true',
|
52
|
+
val: true
|
53
|
+
},
|
54
|
+
records_per_page: {
|
55
|
+
config: '10',
|
56
|
+
val: 10
|
57
|
+
},
|
58
|
+
fake_total_count: {
|
59
|
+
config: 'true',
|
60
|
+
val: true
|
61
|
+
},
|
62
|
+
audit_controller_base: {
|
63
|
+
config: "'ModelsAuditor::AuditBaseController'",
|
64
|
+
val: 'ModelsAuditor::AuditBaseController'
|
65
|
+
},
|
66
|
+
respond_to_json_enabled: {
|
67
|
+
config: 'true',
|
68
|
+
val: true
|
69
|
+
},
|
70
|
+
respond_to_html_enabled: {
|
71
|
+
config: 'false',
|
72
|
+
val: false
|
73
|
+
},
|
74
|
+
json_response_data_key: {
|
75
|
+
config: "'entries'",
|
76
|
+
val: 'entries'
|
77
|
+
},
|
78
|
+
json_response_meta_key: {
|
79
|
+
config: "'meta'",
|
80
|
+
val: 'meta'
|
81
|
+
},
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def method_missing(method_sym, *args)
|
86
|
+
method_name = method_sym.to_s
|
87
|
+
option_name = method_name.tr('=', '')
|
88
|
+
super if CONFIG_OPTIONS.exclude?(option_name.to_sym)
|
89
|
+
if method_name =~ /^.*=$/
|
90
|
+
raise ArgumentError.new('Incorrect number of arguments') if args.size != 1
|
91
|
+
instance_variable_set("@#{option_name}", args[0]) unless ModelsAuditor.configured?
|
92
|
+
else
|
93
|
+
var_name = "@#{option_name}"
|
94
|
+
instance_variable_defined?(var_name) ?
|
95
|
+
instance_variable_get(var_name) :
|
96
|
+
default[option_name.to_sym].try(:[], :val)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
module_function
|
103
|
+
|
104
|
+
def configure(&block)
|
105
|
+
Rails.application.config.after_initialize do
|
106
|
+
block.call(config)
|
107
|
+
@configured = true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def configured?
|
112
|
+
@configured
|
113
|
+
end
|
114
|
+
|
115
|
+
def config
|
116
|
+
@config ||= Config.new
|
117
|
+
end
|
118
|
+
end
|