paper_trail-human 0.3.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 (31) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +52 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +435 -0
  5. data/config/locales/en.yml +6 -0
  6. data/config/locales/pt-BR.yml +6 -0
  7. data/lib/generators/paper_trail/human/install_generator.rb +17 -0
  8. data/lib/generators/paper_trail/human/templates/initializer.rb +23 -0
  9. data/lib/paper_trail/human/adapters/formatters/html.rb +44 -0
  10. data/lib/paper_trail/human/adapters/formatters/markdown.rb +32 -0
  11. data/lib/paper_trail/human/adapters/formatters/text.rb +33 -0
  12. data/lib/paper_trail/human/adapters/resolvers/boolean.rb +22 -0
  13. data/lib/paper_trail/human/adapters/resolvers/custom.rb +21 -0
  14. data/lib/paper_trail/human/adapters/resolvers/date.rb +36 -0
  15. data/lib/paper_trail/human/adapters/resolvers/enum.rb +57 -0
  16. data/lib/paper_trail/human/adapters/resolvers/number.rb +62 -0
  17. data/lib/paper_trail/human/adapters/resolvers/relation.rb +41 -0
  18. data/lib/paper_trail/human/adapters/resolvers/text.rb +29 -0
  19. data/lib/paper_trail/human/configuration.rb +88 -0
  20. data/lib/paper_trail/human/core/batch_presenter.rb +133 -0
  21. data/lib/paper_trail/human/core/change_extractor.rb +79 -0
  22. data/lib/paper_trail/human/core/event_translator.rb +25 -0
  23. data/lib/paper_trail/human/core/field_formatter.rb +92 -0
  24. data/lib/paper_trail/human/core/presenter.rb +76 -0
  25. data/lib/paper_trail/human/core/timeline.rb +30 -0
  26. data/lib/paper_trail/human/ports/resolver.rb +13 -0
  27. data/lib/paper_trail/human/railtie.rb +26 -0
  28. data/lib/paper_trail/human/version.rb +7 -0
  29. data/lib/paper_trail/human.rb +74 -0
  30. data/lib/paper_trail-human.rb +5 -0
  31. metadata +100 -0
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaperTrail
4
+ module Human
5
+ module Core
6
+ class Presenter
7
+ def initialize(configuration)
8
+ @configuration = configuration
9
+ @change_extractor = ChangeExtractor.new
10
+ end
11
+
12
+ def call(version, only: nil, except: nil)
13
+ changes = @change_extractor.call(version)
14
+ model_config = @configuration.config_for(version.item_type)
15
+ formatter = FieldFormatter.new(
16
+ model_config,
17
+ version.item_type,
18
+ field_name_resolver: @configuration.field_name_resolver
19
+ )
20
+
21
+ result = {
22
+ user: @configuration.resolve_whodunnit(version.whodunnit),
23
+ event: EventTranslator.call(version.event, translate: @configuration.translate_events),
24
+ model: version.item_type,
25
+ item_id: version.item_id,
26
+ created_at: version.created_at,
27
+ fields: build_fields(changes, formatter, version.event, only: only, except: except)
28
+ }
29
+
30
+ item_name = @configuration.resolve_item_name(version)
31
+ result[:item_name] = item_name if item_name
32
+
33
+ apply_after_format(result, version)
34
+ end
35
+
36
+ private
37
+
38
+ def build_fields(changes, formatter, event, only: nil, except: nil)
39
+ changes
40
+ .reject { |field, _| @configuration.ignored_fields.include?(field.to_s) }
41
+ .select { |field, _| filter_field?(field, only, except) }
42
+ .map { |field, values| format_field(formatter, field, values, event) }
43
+ end
44
+
45
+ def filter_field?(field, only, except)
46
+ field_s = field.to_s
47
+ return only.map(&:to_s).include?(field_s) if only
48
+
49
+ return !except.map(&:to_s).include?(field_s) if except
50
+
51
+ true
52
+ end
53
+
54
+ def format_field(formatter, field, values, event)
55
+ previous_value, new_value = Array(values)
56
+ result = formatter.call(field, previous_value, new_value)
57
+
58
+ case event
59
+ when 'create'
60
+ result.delete(:previous_value)
61
+ when 'destroy'
62
+ result.delete(:value)
63
+ end
64
+
65
+ result
66
+ end
67
+
68
+ def apply_after_format(result, version)
69
+ return result unless @configuration.after_format
70
+
71
+ @configuration.after_format.call(result, version)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaperTrail
4
+ module Human
5
+ module Core
6
+ class Timeline
7
+ GROUPINGS = {
8
+ day: '%Y-%m-%d',
9
+ week: '%G-W%V',
10
+ month: '%Y-%m',
11
+ year: '%Y'
12
+ }.freeze
13
+
14
+ def initialize(configuration)
15
+ @configuration = configuration
16
+ @batch_presenter = BatchPresenter.new(configuration)
17
+ end
18
+
19
+ def call(versions, group_by: :day, only: nil, except: nil)
20
+ formatted = @batch_presenter.call(versions, only: only, except: except)
21
+ format_str = GROUPINGS.fetch(group_by.to_sym) do
22
+ raise Error, "Unknown group_by: #{group_by}. Available: #{GROUPINGS.keys.join(', ')}"
23
+ end
24
+
25
+ formatted.group_by { |r| r[:created_at].strftime(format_str) }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaperTrail
4
+ module Human
5
+ module Ports
6
+ module Resolver
7
+ def resolve(value)
8
+ raise NotImplementedError, "#{self.class}#resolve must be implemented"
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaperTrail
4
+ module Human
5
+ class Railtie < ::Rails::Railtie
6
+ initializer 'paper_trail_human.i18n' do
7
+ locale_path = File.expand_path('../../../config/locales/*.yml', __dir__)
8
+ I18n.load_path += Dir[locale_path]
9
+ end
10
+
11
+ initializer 'paper_trail_human.extend_version_model' do
12
+ ActiveSupport.on_load(:active_record) do
13
+ if PaperTrail::Human.configuration.extend_version_model
14
+ PaperTrail::Version.include(PaperTrail::Human::VersionExtension)
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ module VersionExtension
21
+ def formatted_log
22
+ PaperTrail::Human.format(self)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaperTrail
4
+ module Human
5
+ VERSION = '0.3.0'
6
+ end
7
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'human/version'
4
+ require_relative 'human/configuration'
5
+ require_relative 'human/core/change_extractor'
6
+ require_relative 'human/core/field_formatter'
7
+ require_relative 'human/core/event_translator'
8
+ require_relative 'human/core/presenter'
9
+ require_relative 'human/core/batch_presenter'
10
+ require_relative 'human/core/timeline'
11
+ require_relative 'human/ports/resolver'
12
+ require_relative 'human/adapters/resolvers/relation'
13
+ require_relative 'human/adapters/resolvers/enum'
14
+ require_relative 'human/adapters/resolvers/boolean'
15
+ require_relative 'human/adapters/resolvers/custom'
16
+ require_relative 'human/adapters/resolvers/text'
17
+ require_relative 'human/adapters/resolvers/date'
18
+ require_relative 'human/adapters/resolvers/number'
19
+ require_relative 'human/adapters/formatters/text'
20
+ require_relative 'human/adapters/formatters/markdown'
21
+ require_relative 'human/adapters/formatters/html'
22
+
23
+ module PaperTrail
24
+ module Human
25
+ class Error < StandardError; end
26
+
27
+ MUTEX = Mutex.new
28
+ private_constant :MUTEX
29
+
30
+ FORMATTERS = {
31
+ text: Adapters::Formatters::Text,
32
+ markdown: Adapters::Formatters::Markdown,
33
+ html: Adapters::Formatters::Html
34
+ }.freeze
35
+ private_constant :FORMATTERS
36
+
37
+ class << self
38
+ def configuration
39
+ @configuration || MUTEX.synchronize { @configuration ||= Configuration.new }
40
+ end
41
+
42
+ def configure
43
+ yield(configuration)
44
+ end
45
+
46
+ def reset_configuration!
47
+ MUTEX.synchronize { @configuration = Configuration.new }
48
+ end
49
+
50
+ def format(version, only: nil, except: nil, as: nil)
51
+ result = Core::Presenter.new(configuration).call(version, only: only, except: except)
52
+ as ? formatter(as).call(result) : result
53
+ end
54
+
55
+ def format_collection(versions, only: nil, except: nil, as: nil)
56
+ results = Core::BatchPresenter.new(configuration).call(versions, only: only, except: except)
57
+ as ? results.map { |r| formatter(as).call(r) } : results
58
+ end
59
+
60
+ private
61
+
62
+ def formatter(type)
63
+ klass = FORMATTERS[type.to_sym]
64
+ raise Error, "Unknown format: #{type}. Available: #{FORMATTERS.keys.join(', ')}" unless klass
65
+
66
+ klass.new
67
+ end
68
+
69
+ def timeline(versions, group_by: :day, only: nil, except: nil)
70
+ Core::Timeline.new(configuration).call(versions, group_by: group_by, only: only, except: except)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record'
4
+ require 'paper_trail'
5
+ require_relative 'paper_trail/human'
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paper_trail-human
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activerecord
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '6.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '6.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: paper_trail
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '12.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '12.0'
40
+ description: Resolves foreign keys, enums, booleans and custom transformations from
41
+ PaperTrail::Version into structured, UI-ready hashes.
42
+ executables: []
43
+ extensions: []
44
+ extra_rdoc_files: []
45
+ files:
46
+ - CHANGELOG.md
47
+ - LICENSE.txt
48
+ - README.md
49
+ - config/locales/en.yml
50
+ - config/locales/pt-BR.yml
51
+ - lib/generators/paper_trail/human/install_generator.rb
52
+ - lib/generators/paper_trail/human/templates/initializer.rb
53
+ - lib/paper_trail-human.rb
54
+ - lib/paper_trail/human.rb
55
+ - lib/paper_trail/human/adapters/formatters/html.rb
56
+ - lib/paper_trail/human/adapters/formatters/markdown.rb
57
+ - lib/paper_trail/human/adapters/formatters/text.rb
58
+ - lib/paper_trail/human/adapters/resolvers/boolean.rb
59
+ - lib/paper_trail/human/adapters/resolvers/custom.rb
60
+ - lib/paper_trail/human/adapters/resolvers/date.rb
61
+ - lib/paper_trail/human/adapters/resolvers/enum.rb
62
+ - lib/paper_trail/human/adapters/resolvers/number.rb
63
+ - lib/paper_trail/human/adapters/resolvers/relation.rb
64
+ - lib/paper_trail/human/adapters/resolvers/text.rb
65
+ - lib/paper_trail/human/configuration.rb
66
+ - lib/paper_trail/human/core/batch_presenter.rb
67
+ - lib/paper_trail/human/core/change_extractor.rb
68
+ - lib/paper_trail/human/core/event_translator.rb
69
+ - lib/paper_trail/human/core/field_formatter.rb
70
+ - lib/paper_trail/human/core/presenter.rb
71
+ - lib/paper_trail/human/core/timeline.rb
72
+ - lib/paper_trail/human/ports/resolver.rb
73
+ - lib/paper_trail/human/railtie.rb
74
+ - lib/paper_trail/human/version.rb
75
+ homepage: https://github.com/gabrielrumiranda/paper_trail-human
76
+ licenses:
77
+ - MIT
78
+ metadata:
79
+ homepage_uri: https://github.com/gabrielrumiranda/paper_trail-human
80
+ source_code_uri: https://github.com/gabrielrumiranda/paper_trail-human
81
+ changelog_uri: https://github.com/gabrielrumiranda/paper_trail-human/blob/main/CHANGELOG.md
82
+ rubygems_mfa_required: 'true'
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '3.1'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.6.8
98
+ specification_version: 4
99
+ summary: Transforms PaperTrail versions into human-readable hashes for audit logs.
100
+ test_files: []