columns_trace 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +29 -4
- data/lib/columns_trace/created_record.rb +44 -0
- data/lib/columns_trace/log_reporter.rb +57 -0
- data/lib/columns_trace/rails_integration.rb +8 -2
- data/lib/columns_trace/railtie.rb +3 -1
- data/lib/columns_trace/registry.rb +2 -4
- data/lib/columns_trace/sidekiq_integration.rb +1 -1
- data/lib/columns_trace/version.rb +1 -1
- data/lib/columns_trace.rb +5 -4
- metadata +4 -3
- data/lib/columns_trace/reporter.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c654a6d0c0772fffa33bb28544ba4e3299272b2673f8f4a138a4526a16a7f036
|
4
|
+
data.tar.gz: f8f2d7914dec1d9b1a39182626ba0f37736bdf2b3a618ade7f9cb1b5b204850a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 408421ba16b44441a4be3e24c46b0ef7e31bfbcb58d6eacc229c51882c00bd2fbb2956bf16a51572f8d2fab94a9ed515bebb4e8450efdea64b1e849bcffcffb7
|
7
|
+
data.tar.gz: 3ef7b2f0cf92cce13760dd581ef68a59fe833ce1fe646c9c9c5476ed6163f024efad6bf67732c6f4f338bf92a2a50deed2b560c9b923605d68a2d7d482d6c539
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
1
|
## master (unreleased)
|
2
2
|
|
3
|
+
## 0.2.0 (2023-10-13)
|
4
|
+
|
5
|
+
- Allow to use custom reporters
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class MyReporter
|
9
|
+
def report(title, created_records)
|
10
|
+
# do actual reporting
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
ColumnsTrace.reporter = MyReporter.new
|
15
|
+
```
|
16
|
+
|
17
|
+
`ColumnsTrace.logger=` setting is removed in favor of using `ColumnsTrace.reporter=`.
|
18
|
+
If you configured custom logger, you should now configure it via:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
logger = ActiveSupport::Logger.new("/my/custom/logger.log")
|
22
|
+
ColumnsTrace.reporter = ColumnsTrace::LogReporter.new(logger)
|
23
|
+
```
|
24
|
+
|
25
|
+
## 0.1.0 (2023-10-04)
|
26
|
+
|
3
27
|
- First release
|
data/README.md
CHANGED
@@ -75,10 +75,10 @@ ColumnsTrace.configure do |config|
|
|
75
75
|
# config.ignored_columns = [:updated_at, { User => :admin }]
|
76
76
|
config.ignored_columns = []
|
77
77
|
|
78
|
-
# The
|
79
|
-
# Defaults to
|
80
|
-
# when
|
81
|
-
config.
|
78
|
+
# The reporter that is used for reporting.
|
79
|
+
# Defaults to log reporter that outputs to `log/columns_trace.log` file
|
80
|
+
# when inside a Rails application.
|
81
|
+
config.reporter = nil
|
82
82
|
|
83
83
|
# Controls the contents of the printed backtrace.
|
84
84
|
# Is set to the default Rails.backtrace_cleaner when the gem is used in the Rails app.
|
@@ -94,6 +94,31 @@ end
|
|
94
94
|
ColumnsTrace.enable_sidekiq_tracing!
|
95
95
|
```
|
96
96
|
|
97
|
+
### Custom reporters
|
98
|
+
|
99
|
+
By default offenses are reported to a log reporter that outputs to `log/columns_trace.log` file
|
100
|
+
when inside a Rails application.
|
101
|
+
|
102
|
+
You can set your custom reporter by defining a class responding to `#report` method.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
class MyReporter
|
106
|
+
def report(title, created_records)
|
107
|
+
title # => "controller#action"
|
108
|
+
created_records # => [#<ColumnsTrace::CreatedRecord>]
|
109
|
+
created_records.each do |record|
|
110
|
+
record.model # class of ActiveRecord model
|
111
|
+
record.accessed_fields # array of accessed fields
|
112
|
+
record.unused_fields # array of unused fields
|
113
|
+
record.backtrace # array of strings
|
114
|
+
record.record # ActiveRecord model instance
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
ColumnsTrace.reporter = MyReporter.new
|
120
|
+
```
|
121
|
+
|
97
122
|
## Development
|
98
123
|
|
99
124
|
After checking out the repo, run `bundle install` to install dependencies. Then, run `rake` to run the linter and tests.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ColumnsTrace
|
4
|
+
# Class that is used to store metadata about created ActiveRecord records.
|
5
|
+
class CreatedRecord
|
6
|
+
# Model class
|
7
|
+
# @return [Class]
|
8
|
+
#
|
9
|
+
attr_reader :model
|
10
|
+
|
11
|
+
# Model instance
|
12
|
+
# @return [ActiveRecord::Base]
|
13
|
+
#
|
14
|
+
attr_reader :record
|
15
|
+
|
16
|
+
# Backtrace where the instance was created
|
17
|
+
# @return [Array<String>]
|
18
|
+
#
|
19
|
+
attr_reader :backtrace
|
20
|
+
|
21
|
+
def initialize(record, backtrace)
|
22
|
+
@model = record.class
|
23
|
+
@record = record
|
24
|
+
@backtrace = backtrace
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get accessed fields on model instance
|
28
|
+
# @return [Array<String>]
|
29
|
+
#
|
30
|
+
def accessed_fields
|
31
|
+
@accessed_fields ||= record.accessed_fields
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get unused fields on model instance
|
35
|
+
# @return [Array<String>]
|
36
|
+
#
|
37
|
+
def unused_fields
|
38
|
+
# We need to store this into local variable, because `record.attributes`
|
39
|
+
# will access all attributes.
|
40
|
+
accessed = accessed_fields
|
41
|
+
record.attributes.keys - accessed
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ColumnsTrace
|
4
|
+
# Reporter that reports into the provided logger.
|
5
|
+
class LogReporter
|
6
|
+
def initialize(logger)
|
7
|
+
@logger = logger
|
8
|
+
end
|
9
|
+
|
10
|
+
# Main reporter's method
|
11
|
+
#
|
12
|
+
# @param title [String] title of the reporting, e.g. controller action etc
|
13
|
+
# @param created_records [Array<ColumnsTrace::CreatedRecord>] items that hold
|
14
|
+
# metadata about created records
|
15
|
+
#
|
16
|
+
def report(title, created_records)
|
17
|
+
lines = []
|
18
|
+
|
19
|
+
created_records.group_by(&:model).each do |model, grouped_created_records|
|
20
|
+
lines.concat(lines_for_created_records(model, grouped_created_records))
|
21
|
+
end
|
22
|
+
|
23
|
+
if lines.any?
|
24
|
+
@logger.info("#{title}\n#{lines.join("\n")}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def lines_for_created_records(model, created_records)
|
30
|
+
lines = []
|
31
|
+
|
32
|
+
created_records.group_by(&:backtrace).each do |backtrace, grouped_created_records|
|
33
|
+
accessed = accessed_fields(grouped_created_records)
|
34
|
+
unused = grouped_created_records.first.unused_fields - accessed
|
35
|
+
unused.reject! { |column| ColumnsTrace.ignored_column?(model, column) }
|
36
|
+
|
37
|
+
if unused.any?
|
38
|
+
records_text = "record".pluralize(grouped_created_records.size)
|
39
|
+
lines << <<-MSG
|
40
|
+
#{grouped_created_records.size} #{model.name} #{records_text}: unused columns - #{format_columns(unused)}; used columns - #{format_columns(accessed)}
|
41
|
+
↳ #{backtrace.join("\n ")}
|
42
|
+
MSG
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
lines
|
47
|
+
end
|
48
|
+
|
49
|
+
def accessed_fields(created_records)
|
50
|
+
created_records.map(&:accessed_fields).flatten.uniq
|
51
|
+
end
|
52
|
+
|
53
|
+
def format_columns(columns)
|
54
|
+
columns.map(&:inspect).join(", ")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -15,11 +15,17 @@ module ColumnsTrace
|
|
15
15
|
|
16
16
|
ActiveSupport.on_load(:action_controller) do
|
17
17
|
before_action { Registry.clear }
|
18
|
-
|
18
|
+
|
19
|
+
after_action do
|
20
|
+
ColumnsTrace.reporter.report("#{self.class.name}##{action_name}", Registry.created_records)
|
21
|
+
end
|
19
22
|
end
|
20
23
|
|
21
24
|
ActiveSupport.on_load(:active_job) do
|
22
25
|
before_perform { Registry.clear }
|
23
|
-
|
26
|
+
|
27
|
+
after_perform do
|
28
|
+
ColumnsTrace.reporter.report(self.class.name, Registry.created_records)
|
29
|
+
end
|
24
30
|
end
|
25
31
|
end
|
@@ -5,7 +5,9 @@ module ColumnsTrace
|
|
5
5
|
class Railtie < Rails::Railtie
|
6
6
|
initializer "columns_trace.set_configs" do
|
7
7
|
ColumnsTrace.backtrace_cleaner = Rails.backtrace_cleaner
|
8
|
-
|
8
|
+
|
9
|
+
logger = ActiveSupport::Logger.new(Rails.root.join("log", "columns_trace.log"))
|
10
|
+
ColumnsTrace.reporter = LogReporter.new(logger)
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
@@ -4,18 +4,16 @@ module ColumnsTrace
|
|
4
4
|
# @private
|
5
5
|
# Note: can use ActiveSupport::IsolatedExecutionState instead of this module for rails 7.0+.
|
6
6
|
module Registry
|
7
|
-
Entry = Struct.new(:model, :record, :backtrace)
|
8
|
-
|
9
7
|
class << self
|
10
8
|
def register(record, backtrace)
|
11
|
-
state <<
|
9
|
+
state << CreatedRecord.new(record, backtrace)
|
12
10
|
end
|
13
11
|
|
14
12
|
def clear
|
15
13
|
state.clear
|
16
14
|
end
|
17
15
|
|
18
|
-
def
|
16
|
+
def created_records
|
19
17
|
state
|
20
18
|
end
|
21
19
|
|
data/lib/columns_trace.rb
CHANGED
@@ -2,9 +2,10 @@
|
|
2
2
|
|
3
3
|
require "active_record"
|
4
4
|
|
5
|
+
require_relative "columns_trace/created_record"
|
5
6
|
require_relative "columns_trace/registry"
|
6
7
|
require_relative "columns_trace/rails_integration"
|
7
|
-
require_relative "columns_trace/
|
8
|
+
require_relative "columns_trace/log_reporter"
|
8
9
|
require_relative "columns_trace/version"
|
9
10
|
require_relative "columns_trace/railtie" if defined?(Rails)
|
10
11
|
|
@@ -54,11 +55,11 @@ module ColumnsTrace
|
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
57
|
-
# Allows to set the
|
58
|
-
# Defaults to
|
58
|
+
# Allows to set the reporter.
|
59
|
+
# Defaults to log reporter that outputs to `log/columns_trace.log` file
|
59
60
|
# when inside a rails application.
|
60
61
|
#
|
61
|
-
attr_accessor :
|
62
|
+
attr_accessor :reporter
|
62
63
|
|
63
64
|
# @private
|
64
65
|
attr_reader :backtrace_cleaner
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: columns_trace
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fatkodima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -35,10 +35,11 @@ files:
|
|
35
35
|
- LICENSE.txt
|
36
36
|
- README.md
|
37
37
|
- lib/columns_trace.rb
|
38
|
+
- lib/columns_trace/created_record.rb
|
39
|
+
- lib/columns_trace/log_reporter.rb
|
38
40
|
- lib/columns_trace/rails_integration.rb
|
39
41
|
- lib/columns_trace/railtie.rb
|
40
42
|
- lib/columns_trace/registry.rb
|
41
|
-
- lib/columns_trace/reporter.rb
|
42
43
|
- lib/columns_trace/sidekiq_integration.rb
|
43
44
|
- lib/columns_trace/version.rb
|
44
45
|
- test/dummy/tmp/development_secret.txt
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ColumnsTrace
|
4
|
-
# @private
|
5
|
-
class Reporter
|
6
|
-
def self.report(title)
|
7
|
-
new.report(title)
|
8
|
-
end
|
9
|
-
|
10
|
-
def report(title)
|
11
|
-
lines = []
|
12
|
-
|
13
|
-
Registry.entries.group_by(&:model).each do |model, entries|
|
14
|
-
lines.concat(lines_for_entries(model, entries))
|
15
|
-
end
|
16
|
-
|
17
|
-
if lines.any?
|
18
|
-
ColumnsTrace.logger.info("#{title}\n#{lines.join("\n")}")
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
def lines_for_entries(model, entries)
|
24
|
-
lines = []
|
25
|
-
|
26
|
-
entries.group_by(&:backtrace).each do |backtrace, grouped_entries|
|
27
|
-
records = grouped_entries.map(&:record)
|
28
|
-
|
29
|
-
accessed = accessed_fields(records)
|
30
|
-
unused = records.first.attributes.keys - accessed
|
31
|
-
unused.reject! { |column| ColumnsTrace.ignored_column?(model, column) }
|
32
|
-
|
33
|
-
if unused.any?
|
34
|
-
records_text = "record".pluralize(records.size)
|
35
|
-
lines << <<-MSG
|
36
|
-
#{records.size} #{model.name} #{records_text}: unused columns - #{format_columns(unused)}; used columns - #{format_columns(accessed)}
|
37
|
-
↳ #{backtrace.join("\n ")}
|
38
|
-
MSG
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
lines
|
43
|
-
end
|
44
|
-
|
45
|
-
def accessed_fields(records)
|
46
|
-
records.map(&:accessed_fields).flatten.uniq
|
47
|
-
end
|
48
|
-
|
49
|
-
def format_columns(columns)
|
50
|
-
columns.map(&:inspect).join(", ")
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|