audited-serialize 0.0.2 → 1.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa2ae142361013574ee5100ed3edf7da72ffa6a04758a894dfab763a3c37b82e
4
- data.tar.gz: cee3114d051d34099d11d9f9f402c7dffa8a0d61c813d472a11f73a5afafabed
3
+ metadata.gz: c13c23cc79d29cf37a1c663634909b2f699bb38a7e69722d0d68c71159d01d96
4
+ data.tar.gz: a65ad64ea718d380b56711155a13dc2bb7f85fb65143ef52079ae584816960a8
5
5
  SHA512:
6
- metadata.gz: 929cd4a6b8a55c730db3d644755f23cc33bb27f5e39553bd54cf67b8d6bcea0fe80c6b84eb412d7f4c7e3c03851493022d40c3fb177e908c5fb5ea919d6f4d35
7
- data.tar.gz: f79cac8c70bddc12ee980194d557d9fb1f7e8d04c45d5083fdecd996015e3df1e9c31f7ed090866560263182251546675380a1050895ec9f8a4229668b2b4eca
6
+ metadata.gz: c24d1457e9cf4f2103d38ac8f37bde5799b13bc812cea1a3cded11f5743e8b728761ab267bc6578de42ea21c60e890305f35fe7bdf8dca11df9bc5e507fc0e5f
7
+ data.tar.gz: 9eacbfb7355928d94a0c948d7063fe5d50d2ccb8f695b173b5718a97a3648b85aa49b7f10d8c9ecfc8b6d082520c52ca09877c89684d0980ede7e02d6dc8f2ab
data/README.md CHANGED
@@ -26,6 +26,31 @@ audit = Audited::Audit.first
26
26
  audit.changes_list
27
27
  ```
28
28
 
29
+ ## Заголовки аудит-логов
30
+
31
+ На экземплярах audit-класса доступен метод `.title` для вывода заголовка c информацией о том, какое действие на какой сущности было совершено
32
+
33
+ ```
34
+ audit = Audited::Audit.first
35
+ audit.title
36
+
37
+ => "Изменение пользователя"
38
+ ```
39
+
40
+ ## Заголовки связанных записей
41
+
42
+ При наличии в аудит-логах полей, содержащих ID связанных сущностей, по умолчанию будет отображено название (поле `name`) этой сущности. При отсутствии у сущности поля `name` или необходимости отображения значения другого поля можно задать название этого поля в модели
43
+
44
+ ```
45
+ # app/models/user.rb
46
+
47
+ class User < ApplicationRecord
48
+
49
+ audit title: :email
50
+
51
+ end
52
+ ```
53
+
29
54
  ## Конфигурация
30
55
 
31
56
  Изменение настроек сериализации осуществляется путем редактирования стандартного файла конфигурации `Audited`
@@ -38,7 +63,7 @@ Audited.config do |config|
38
63
  end
39
64
  ```
40
65
 
41
- ### Исключения
66
+ ## Исключения
42
67
 
43
68
  Для добавления исключений для полей сущностей, которые не будут отображаться в списке добавьте в конфиг `serialization_exceptions`.
44
69
 
@@ -0,0 +1,41 @@
1
+ require 'audited/serialization'
2
+ require 'audited/serialization_changes/array_module'
3
+ require 'audited/serialization_changes/boolean_module'
4
+ require 'audited/serialization_changes/common_module'
5
+ require 'audited/serialization_changes/date_module'
6
+ require 'audited/serialization_changes/datetime_module'
7
+ require 'audited/serialization_changes/enum_module'
8
+ require 'audited/serialization_changes/object_module'
9
+ require 'audited/serialization_changes/related_record_module'
10
+
11
+ module Audited
12
+ class Audit < ::ActiveRecord::Base
13
+
14
+ include Audited::Serialization
15
+ include Audited::SerializationChanges::ArrayModule
16
+ include Audited::SerializationChanges::BooleanModule
17
+ include Audited::SerializationChanges::CommonModule
18
+ include Audited::SerializationChanges::DateModule
19
+ include Audited::SerializationChanges::DatetimeModule
20
+ include Audited::SerializationChanges::EnumModule
21
+ include Audited::SerializationChanges::ObjectModule
22
+ include Audited::SerializationChanges::RelatedRecordModule
23
+
24
+ scope :own_and_associated_for, (lambda do |record|
25
+ where('
26
+ (auditable_type = :type AND auditable_id = :id)
27
+ OR
28
+ (associated_type = :type AND associated_id = :id)',
29
+ type: record.class.to_s, id: record.id
30
+ ).order(id: :desc)
31
+ end)
32
+
33
+ def title
34
+ operation = I18n.t("audited.actions.#{action}")
35
+ entity = I18n.t("audited.auditable_types.#{auditable_type}")
36
+
37
+ "#{operation} #{entity}"
38
+ end
39
+
40
+ end
41
+ end
@@ -4,21 +4,29 @@ module Audited
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  def changes_list
7
- model = auditable_type.constantize
7
+ # проверка на наличие модели
8
+ # в случае ее отсутствия, верятнее всего она была удалена
9
+ begin
10
+ model = auditable_type.constantize
11
+ rescue NameError
12
+ return
13
+ end
14
+
8
15
  columns_hash = model.columns_hash
9
- @reflections = model.reflections.to_h { |_relation, reflection| [reflection.foreign_key, reflection.klass] }
16
+ @reflections = model_reflections(model)
10
17
 
11
18
  audited_changes.map do |column, value|
12
- @column = column
13
-
14
19
  # пропускаем, если колонка в списке исключений
15
- next if Audited.serialization_exceptions[auditable_type]&.include?(@column)
20
+ next if Audited.serialization_exceptions[auditable_type]&.include?(column)
16
21
 
17
- # поле таблицы БД
22
+ @column = column
18
23
  @db_column = columns_hash[column]
19
24
 
25
+ # пропускаем, если колонка теперь отсутствует в БД
26
+ next unless @db_column
27
+
20
28
  # русскоязычное название поля
21
- @field = @db_column.comment
29
+ field = password_changes? ? 'Пароль' : @db_column.comment
22
30
 
23
31
  if action == 'update' && value.is_a?(Array)
24
32
  # значения, измененные во время редактирования
@@ -28,158 +36,45 @@ module Audited
28
36
  @changed = value
29
37
  end
30
38
 
31
- if related_record_changed?
32
- related_record_changes
33
- elsif object?
34
- json_changes
35
- elsif array?
36
- array_changes
37
- elsif password?
38
- password_changes
39
- elsif date?
40
- date_changes
41
- elsif datetime?
42
- datetime_changes
43
- elsif boolean?
44
- boolean_changes
45
- elsif enum_changes?
46
- enum_changes
47
- else
48
- common_column_changes
49
- end
39
+ # определение изменений было/стало
40
+ from, to = changes_from_to
50
41
 
51
- next if @from.blank? && @to.blank?
42
+ # пропускаем, если изменения не зафиксированы
43
+ next if from.blank? && to.blank?
52
44
 
53
45
  {
54
- field: @field,
55
- from: @from,
56
- to: @to
46
+ field:, from:, to:
57
47
  }
58
48
  end.compact
59
49
  end
60
50
 
61
51
  private
62
52
 
63
- def related_record_changed?
64
- @reflections[@column].present?
65
- end
66
-
67
- def object?
68
- @initial.is_a?(Hash) || @changed.is_a?(Hash)
69
- end
70
-
71
- def array?
72
- @initial.is_a?(Array) || @changed.is_a?(Array)
73
- end
74
-
75
- def password?
76
- @column == 'encrypted_password'
77
- end
78
-
79
- def datetime?
80
- @initial&.to_date&.acts_like?(:date) && @initial.include?(':') rescue false
81
- @changed&.to_date&.acts_like?(:date) && @changed.include?(':') rescue false
82
- end
83
-
84
- def date?
85
- @initial&.to_date&.acts_like?(:date) && @initial.to_s.include?('-') && @initial.to_s.exclude?(':') rescue false
86
- @changed&.to_date&.acts_like?(:date) && @changed.to_s.include?('-') && @changed.to_s.exclude?(':') rescue false
87
- end
88
-
89
- def boolean?
90
- bool_classes = [TrueClass, FalseClass]
91
-
92
- bool_classes.include?(@initial.class) || bool_classes.include?(@changed.class)
93
- end
94
-
95
- def enum_changes?
96
- @db_column.type == :enum
97
- end
98
-
99
- def related_record_changes
100
- # модель связанной сущности от колонки
101
- related_model = @reflections[@column]
102
-
103
- # заданные/измененные значения
104
- @from = related_record_value(related_model, @initial)
105
- @to = related_record_value(related_model, @changed)
106
- end
107
-
108
- def related_record_value(related_model, value)
109
- if value
110
- record = related_model.find_by(id: value)
111
- auditing_title(record) || "Запись ##{value}"
53
+ def model_reflections(model)
54
+ model.reflections.to_h { |_rel, reflection| [reflection.foreign_key, reflection.klass] }
55
+ end
56
+
57
+ def changes_from_to
58
+ if related_record_changes?
59
+ related_record_changes
60
+ elsif object_changes?
61
+ object_changes
62
+ elsif array_changes?
63
+ array_changes
64
+ elsif password_changes?
65
+ password_changes
66
+ elsif date_changes?
67
+ date_changes
68
+ elsif datetime_changes?
69
+ datetime_changes
70
+ elsif boolean_changes?
71
+ boolean_changes
72
+ elsif enum_changes?
73
+ enum_changes
112
74
  else
113
- ''
75
+ common_column_changes
114
76
  end
115
77
  end
116
78
 
117
- def auditing_title(record)
118
- return unless record
119
-
120
- if record.is_a?(House)
121
- record.number
122
- elsif record.klass.column_names.include?('name')
123
- record.name
124
- else
125
- "##{record.id}"
126
- end
127
- end
128
-
129
- def enum_changes
130
- enum_type = @db_column.sql_type
131
- values = I18n.t("enums.#{enum_type}").stringify_keys
132
-
133
- @from = values[@initial]
134
- @to = values[@changed]
135
- end
136
-
137
- def json_changes
138
- # удаление дубликатов ключей/значений
139
- initial = (@initial.to_a - @changed.to_a).to_h
140
- changed = (@changed.to_a - @initial.to_a).to_h
141
-
142
- @from = initial.map { |k, v| "#{k.humanize}: #{v}" }
143
- @to = changed.map { |k, v| "#{k.humanize}: #{v}" }
144
- end
145
-
146
- def array_changes
147
- @from = @initial&.join(', ')
148
- @to = @changed&.join(', ')
149
- end
150
-
151
- def password_changes
152
- @field = 'Пароль'
153
- @from = nil
154
- @to = '<новый>'
155
- end
156
-
157
- def date_changes
158
- @from = Date.parse(@initial).strftime('%d.%m.%Y') if @initial.present?
159
- @to = Date.parse(@changed).strftime('%d.%m.%Y') if @changed.present?
160
- end
161
-
162
- def datetime_changes
163
- if @initial.present?
164
- @from = DateTime.parse(@initial).in_time_zone('Europe/Moscow').strftime('%d.%m.%Y %H:%M')
165
- end
166
-
167
- if @changed.present?
168
- @to = DateTime.parse(@changed).in_time_zone('Europe/Moscow').strftime('%d.%m.%Y %H:%M')
169
- end
170
- end
171
-
172
- def boolean_changes
173
- values = { true => 'Да', false => 'Нет' }
174
-
175
- @from = values[@initial]
176
- @to = values[@changed]
177
- end
178
-
179
- def common_column_changes
180
- @from = @initial
181
- @to = @changed
182
- end
183
-
184
79
  end
185
80
  end
@@ -0,0 +1,22 @@
1
+ module Audited
2
+ module SerializationChanges
3
+ module ArrayModule
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def array_changes?
10
+ @initial.is_a?(Array) || @changed.is_a?(Array)
11
+ end
12
+
13
+ def array_changes
14
+ from = @initial&.join(', ')
15
+ to = @changed&.join(', ')
16
+
17
+ return from, to
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ module Audited
2
+ module SerializationChanges
3
+ module BooleanModule
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def boolean_changes?
10
+ classes = [TrueClass, FalseClass]
11
+
12
+ classes.include?(@initial.class) || classes.include?(@changed.class)
13
+ end
14
+
15
+ def boolean_changes
16
+ values = { true => 'Да', false => 'Нет' }
17
+
18
+ return values[@initial], values[@changed]
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Audited
2
+ module SerializationChanges
3
+ module CommonModule
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def password_changes?
10
+ @column == 'encrypted_password'
11
+ end
12
+
13
+ def password_changes
14
+ return nil, '<новый>'
15
+ end
16
+
17
+ def common_column_changes
18
+ return @initial, @changed
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ module Audited
2
+ module SerializationChanges
3
+ module DateModule
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def date_changes?
10
+ @initial&.to_date&.acts_like?(:date) && @initial.to_s.include?('-') && @initial.to_s.exclude?(':') rescue false
11
+ @changed&.to_date&.acts_like?(:date) && @changed.to_s.include?('-') && @changed.to_s.exclude?(':') rescue false
12
+ end
13
+
14
+ def date_changes
15
+ from = date_change_formatted(@initial)
16
+ to = date_change_formatted(@changed)
17
+
18
+ return from, to
19
+ end
20
+
21
+ def date_change_formatted(value)
22
+ return if value.blank?
23
+
24
+ Date.parse(value).strftime('%d.%m.%Y')
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module Audited
2
+ module SerializationChanges
3
+ module DatetimeModule
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def datetime_changes?
10
+ @initial&.to_date&.acts_like?(:date) && @initial.include?(':') rescue false
11
+ @changed&.to_date&.acts_like?(:date) && @changed.include?(':') rescue false
12
+ end
13
+
14
+ def datetime_changes
15
+ from = datetime_change_formatted(@initial)
16
+ to = datetime_change_formatted(@changed)
17
+
18
+ return from, to
19
+ end
20
+
21
+ def datetime_change_formatted(value)
22
+ return if value.blank?
23
+
24
+ DateTime.parse(value).in_time_zone('Europe/Moscow').strftime('%d.%m.%Y %H:%M')
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ module Audited
2
+ module SerializationChanges
3
+ module EnumModule
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def enum_changes?
10
+ @db_column.type == :enum
11
+ end
12
+
13
+ def enum_changes
14
+ type = @db_column.sql_type
15
+ values = I18n.t("enums.#{type}").stringify_keys
16
+
17
+ return values[@initial], values[@changed]
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ module Audited
2
+ module SerializationChanges
3
+ module ObjectModule
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def object_changes?
10
+ @initial.is_a?(Hash) || @changed.is_a?(Hash)
11
+ end
12
+
13
+ def object_changes
14
+ from = object_change_formatted(@initial, @changed)
15
+ to = object_change_formatted(@changed, @initial)
16
+
17
+ return from, to
18
+ end
19
+
20
+ def object_change_formatted(from, to)
21
+ # удаление дубликатов ключей/значений
22
+ hash = (from.to_a - to.to_a).to_h
23
+
24
+ hash.map { |key, val| "#{key.humanize}: #{val}" }
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ module Audited
2
+ module SerializationChanges
3
+ module RelatedRecordModule
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def related_record_changes?
10
+ @reflections[@column].present?
11
+ end
12
+
13
+ def related_record_changes
14
+ # модель связанной сущности от колонки
15
+ related_model = @reflections[@column]
16
+
17
+ # заданные/измененные значения
18
+ from = related_record_value(related_model, @initial)
19
+ to = related_record_value(related_model, @changed)
20
+
21
+ return from, to
22
+ end
23
+
24
+ def related_record_value(related_model, related_record_id)
25
+ return '' unless related_record_id
26
+
27
+ record = related_model.find_by(id: related_record_id)
28
+ return "Запись ##{related_record_id}" unless record
29
+
30
+ related_record_auditing_title(record)
31
+ end
32
+
33
+ def related_record_auditing_title(record)
34
+ audited_title = record.class.audited_options[:title]
35
+
36
+ if audited_title
37
+ record.send(audited_title)
38
+ elsif record.class.column_names.include?('name')
39
+ record.name
40
+ else
41
+ "##{record.id}"
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -1,5 +1,5 @@
1
1
  require 'audited_config'
2
- require 'audited/original_audit'
2
+ require 'audited/audit_extensions'
3
3
 
4
4
  module AuditedSerialize
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: audited-serialize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Павел Бабин
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-12 00:00:00.000000000 Z
11
+ date: 2024-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: audited
@@ -32,8 +32,16 @@ extra_rdoc_files: []
32
32
  files:
33
33
  - README.md
34
34
  - lib/audited-serialize.rb
35
- - lib/audited/original_audit.rb
35
+ - lib/audited/audit_extensions.rb
36
36
  - lib/audited/serialization.rb
37
+ - lib/audited/serialization_changes/array_module.rb
38
+ - lib/audited/serialization_changes/boolean_module.rb
39
+ - lib/audited/serialization_changes/common_module.rb
40
+ - lib/audited/serialization_changes/date_module.rb
41
+ - lib/audited/serialization_changes/datetime_module.rb
42
+ - lib/audited/serialization_changes/enum_module.rb
43
+ - lib/audited/serialization_changes/object_module.rb
44
+ - lib/audited/serialization_changes/related_record_module.rb
37
45
  - lib/audited_config.rb
38
46
  - lib/audited_serialize.rb
39
47
  homepage:
@@ -1,9 +0,0 @@
1
- require 'audited/serialization'
2
-
3
- module Audited
4
- class Audit < ::ActiveRecord::Base
5
-
6
- include Audited::Serialization
7
-
8
- end
9
- end