cruft_tracker 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +460 -0
- data/Rakefile +8 -0
- data/app/assets/config/cruft_tracker_manifest.js +1 -0
- data/app/assets/stylesheets/cruft_tracker/application.css +15 -0
- data/app/controllers/cruft_tracker/application_controller.rb +4 -0
- data/app/controllers/cruft_tracker/methods_controller.rb +10 -0
- data/app/helpers/cruft_tracker/application_helper.rb +4 -0
- data/app/models/cruft_tracker/application_record.rb +5 -0
- data/app/models/cruft_tracker/argument.rb +7 -0
- data/app/models/cruft_tracker/backtrace.rb +7 -0
- data/app/models/cruft_tracker/method.rb +43 -0
- data/app/services/cruft_tracker/application_service.rb +6 -0
- data/app/services/cruft_tracker/cleanup_untracked_methods.rb +20 -0
- data/app/services/cruft_tracker/record_arguments.rb +41 -0
- data/app/services/cruft_tracker/record_backtrace.rb +53 -0
- data/app/services/cruft_tracker/record_invocation.rb +15 -0
- data/app/services/cruft_tracker/track_all_methods.rb +47 -0
- data/app/services/cruft_tracker/track_method.rb +139 -0
- data/app/views/cruft_tracker/methods/index.html.erb +1 -0
- data/app/views/layouts/cruft_tracker/application.html.erb +15 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20220414134857_create_cruft_tracker_methods.rb +17 -0
- data/db/migrate/20220418133030_create_cruft_tracker_backtraces.rb +20 -0
- data/db/migrate/20220419171326_add_comment_to_cruft_tracker_methods.rb +9 -0
- data/db/migrate/20220419174055_create_cruft_tracker_arguments.rb +15 -0
- data/lib/cruft_tracker/engine.rb +17 -0
- data/lib/cruft_tracker/registry.rb +25 -0
- data/lib/cruft_tracker/version.rb +3 -0
- data/lib/cruft_tracker.rb +31 -0
- data/lib/tasks/cruft_tracker_tasks.rake +4 -0
- metadata +309 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CruftTracker
|
4
|
+
class RecordArguments < CruftTracker::ApplicationService
|
5
|
+
record :method, class: CruftTracker::Method
|
6
|
+
array :arguments
|
7
|
+
object :transformer, class: Proc
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def execute
|
12
|
+
arguments_record.with_lock do
|
13
|
+
arguments_record.reload
|
14
|
+
arguments_record.update(occurrences: arguments_record.occurrences + 1)
|
15
|
+
end
|
16
|
+
|
17
|
+
arguments_record
|
18
|
+
end
|
19
|
+
|
20
|
+
def arguments_record
|
21
|
+
@arguments_record ||=
|
22
|
+
begin
|
23
|
+
CruftTracker::Argument.create(
|
24
|
+
method: method,
|
25
|
+
arguments_hash: arguments_hash,
|
26
|
+
arguments: transformed_arguments
|
27
|
+
)
|
28
|
+
rescue ActiveRecord::RecordNotUnique
|
29
|
+
CruftTracker::Argument.find_by(arguments_hash: arguments_hash)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def arguments_hash
|
34
|
+
Digest::MD5.hexdigest(transformed_arguments.to_json)
|
35
|
+
end
|
36
|
+
|
37
|
+
def transformed_arguments
|
38
|
+
@transformed_arguments ||= transformer.call(arguments)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CruftTracker
|
4
|
+
class RecordBacktrace < CruftTracker::ApplicationService
|
5
|
+
record :method, class: CruftTracker::Method
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def execute
|
10
|
+
backtrace_record.with_lock do
|
11
|
+
backtrace_record.reload
|
12
|
+
backtrace_record.update(occurrences: backtrace_record.occurrences + 1)
|
13
|
+
end
|
14
|
+
|
15
|
+
backtrace_record
|
16
|
+
end
|
17
|
+
|
18
|
+
def backtrace_record
|
19
|
+
@backtrace_record ||=
|
20
|
+
begin
|
21
|
+
CruftTracker::Backtrace.create(
|
22
|
+
traceable: method,
|
23
|
+
trace_hash: backtrace_hash,
|
24
|
+
trace: filtered_backtrace
|
25
|
+
)
|
26
|
+
rescue ActiveRecord::RecordNotUnique
|
27
|
+
CruftTracker::Backtrace.find_by(trace_hash: backtrace_hash)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def backtrace_hash
|
32
|
+
Digest::MD5.hexdigest(filtered_backtrace.to_json)
|
33
|
+
end
|
34
|
+
|
35
|
+
def filtered_backtrace
|
36
|
+
last_locations_before_tracking_starts =
|
37
|
+
caller_locations.reverse.find_index do |location|
|
38
|
+
location.path.match(/.*track_method.*/)
|
39
|
+
end
|
40
|
+
|
41
|
+
caller_locations
|
42
|
+
.last(last_locations_before_tracking_starts || 0)
|
43
|
+
.map do |location|
|
44
|
+
{
|
45
|
+
path: location.path,
|
46
|
+
label: location.label,
|
47
|
+
base_label: location.base_label,
|
48
|
+
lineno: location.lineno
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CruftTracker
|
4
|
+
class RecordInvocation < CruftTracker::ApplicationService
|
5
|
+
record :method, class: CruftTracker::Method
|
6
|
+
|
7
|
+
def execute
|
8
|
+
increment_invocations
|
9
|
+
end
|
10
|
+
|
11
|
+
def increment_invocations
|
12
|
+
method.update(invocations: method.reload.invocations + 1)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CruftTracker
|
4
|
+
class TrackAllMethods < CruftTracker::ApplicationService
|
5
|
+
object :owner, class: Object
|
6
|
+
object :comment, class: Object, default: nil
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def execute
|
11
|
+
method_records = []
|
12
|
+
method_records +=
|
13
|
+
own_instance_methods.map do |instance_method|
|
14
|
+
CruftTracker::TrackMethod.run!(
|
15
|
+
owner: owner,
|
16
|
+
name: instance_method,
|
17
|
+
method_type: CruftTracker::Method::INSTANCE_METHOD,
|
18
|
+
comment: comment
|
19
|
+
)
|
20
|
+
end
|
21
|
+
method_records +=
|
22
|
+
own_class_methods.map do |class_method|
|
23
|
+
CruftTracker::TrackMethod.run!(
|
24
|
+
owner: owner,
|
25
|
+
name: class_method,
|
26
|
+
method_type: CruftTracker::Method::CLASS_METHOD,
|
27
|
+
comment: comment
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
method_records.flatten
|
32
|
+
end
|
33
|
+
|
34
|
+
def own_instance_methods
|
35
|
+
owner.instance_methods(false) + owner.private_instance_methods(false)
|
36
|
+
end
|
37
|
+
|
38
|
+
def own_class_methods
|
39
|
+
owner.methods(false) +
|
40
|
+
owner
|
41
|
+
.private_methods(false)
|
42
|
+
.select do |method|
|
43
|
+
owner.method(method).owner.inspect === owner.singleton_class.inspect
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require 'cruft_tracker/record_invocation'
|
4
|
+
# require 'cruft_tracker/record_backtrace'
|
5
|
+
module CruftTracker
|
6
|
+
class TrackMethod < CruftTracker::ApplicationService
|
7
|
+
private
|
8
|
+
|
9
|
+
object :owner, class: Module
|
10
|
+
symbol :name
|
11
|
+
symbol :method_type, default: -> { determine_method_type }
|
12
|
+
object :comment, class: Object, default: nil
|
13
|
+
object :arguments_transformer, class: Proc, default: nil
|
14
|
+
|
15
|
+
def execute
|
16
|
+
method_record = create_or_find_method_record
|
17
|
+
method_record.deleted_at = nil
|
18
|
+
method_record.comment = comment if comment != method_record.comment
|
19
|
+
method_record.save
|
20
|
+
|
21
|
+
wrap_target_method(
|
22
|
+
method_type,
|
23
|
+
target_method,
|
24
|
+
method_record,
|
25
|
+
arguments_transformer
|
26
|
+
)
|
27
|
+
|
28
|
+
CruftTracker::Registry << method_record
|
29
|
+
|
30
|
+
method_record
|
31
|
+
rescue ActiveRecord::StatementInvalid => e
|
32
|
+
raise unless e.cause.present? && e.cause.instance_of?(Mysql2::Error)
|
33
|
+
|
34
|
+
Rails.logger.warn(
|
35
|
+
'CruftTracker was unable to record a method. Does the cruft_tracker_methods table exist? Have migrations been run?'
|
36
|
+
)
|
37
|
+
rescue NoMethodError
|
38
|
+
Rails.logger.warn(
|
39
|
+
'CruftTracker was unable to record a method. Have migrations been run?'
|
40
|
+
)
|
41
|
+
rescue Mysql2::Error::ConnectionError,
|
42
|
+
ActiveRecord::ConnectionNotEstablished
|
43
|
+
Rails.logger.warn(
|
44
|
+
'CruftTracker was unable to record a method due to being unable to connect to the database. This may be a non-issue in cases where the database is intentionally not available.'
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def wrap_target_method(
|
49
|
+
method_type,
|
50
|
+
target_method,
|
51
|
+
method_record,
|
52
|
+
arguments_transformer
|
53
|
+
)
|
54
|
+
target_method.owner.define_method target_method.name do |*args|
|
55
|
+
CruftTracker::RecordInvocation.run!(method: method_record)
|
56
|
+
CruftTracker::RecordBacktrace.run!(method: method_record)
|
57
|
+
if arguments_transformer.present?
|
58
|
+
CruftTracker::RecordArguments.run!(
|
59
|
+
method: method_record,
|
60
|
+
arguments: args,
|
61
|
+
transformer: arguments_transformer
|
62
|
+
)
|
63
|
+
end
|
64
|
+
if method_type == CruftTracker::Method::INSTANCE_METHOD
|
65
|
+
target_method.bind(self).call(*args)
|
66
|
+
else
|
67
|
+
target_method.call(*args)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_or_find_method_record
|
73
|
+
CruftTracker::Method.create(
|
74
|
+
owner: owner.name,
|
75
|
+
name: name,
|
76
|
+
method_type: method_type,
|
77
|
+
comment: comment
|
78
|
+
)
|
79
|
+
rescue ActiveRecord::RecordNotUnique
|
80
|
+
CruftTracker::Method.find_by(
|
81
|
+
owner: owner.name,
|
82
|
+
name: name,
|
83
|
+
method_type: method_type
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def target_method
|
88
|
+
case method_type
|
89
|
+
when CruftTracker::Method::INSTANCE_METHOD
|
90
|
+
owner.instance_method(name)
|
91
|
+
when CruftTracker::Method::CLASS_METHOD
|
92
|
+
owner.method(name)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def determine_method_type
|
97
|
+
is_instance_method = all_instance_methods.include?(name)
|
98
|
+
is_class_method = all_class_methods.include?(name)
|
99
|
+
|
100
|
+
if is_instance_method && is_class_method
|
101
|
+
raise AmbiguousMethodType.new(owner.name, name)
|
102
|
+
elsif is_instance_method
|
103
|
+
CruftTracker::Method::INSTANCE_METHOD
|
104
|
+
elsif is_class_method
|
105
|
+
CruftTracker::Method::CLASS_METHOD
|
106
|
+
else
|
107
|
+
raise NoSuchMethod.new(owner.name, name)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def all_instance_methods
|
112
|
+
owner.instance_methods + owner.private_instance_methods
|
113
|
+
end
|
114
|
+
|
115
|
+
def all_class_methods
|
116
|
+
owner.methods + owner.private_methods
|
117
|
+
end
|
118
|
+
|
119
|
+
class AmbiguousMethodType < StandardError
|
120
|
+
def initialize(owner_name, ambiguous_name)
|
121
|
+
super(
|
122
|
+
"#{owner_name} has instance and class methods named '#{
|
123
|
+
ambiguous_name
|
124
|
+
}'. Please specify the correct type."
|
125
|
+
)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class NoSuchMethod < StandardError
|
130
|
+
def initialize(owner_name, missing_name)
|
131
|
+
super(
|
132
|
+
"#{owner_name} does not have an instance or class method named '#{
|
133
|
+
missing_name
|
134
|
+
}'."
|
135
|
+
)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
hi!!
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateCruftTrackerMethods < ActiveRecord::Migration[5.2]
|
2
|
+
def change
|
3
|
+
create_table :cruft_tracker_methods do |t|
|
4
|
+
t.string :owner, null: false
|
5
|
+
t.string :name, null: false
|
6
|
+
t.string :method_type, null: false
|
7
|
+
t.integer :invocations, null: false, default: 0
|
8
|
+
t.datetime :deleted_at
|
9
|
+
t.timestamps
|
10
|
+
|
11
|
+
t.index :owner
|
12
|
+
t.index :name
|
13
|
+
t.index %i[owner name]
|
14
|
+
t.index %i[owner name method_type], unique: true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class CreateCruftTrackerBacktraces < ActiveRecord::Migration[5.2]
|
2
|
+
def change
|
3
|
+
create_table :cruft_tracker_backtraces do |t|
|
4
|
+
t.references :traceable,
|
5
|
+
polymorphic: true,
|
6
|
+
null: false,
|
7
|
+
index: {
|
8
|
+
name: 'index_pcbt_on_traceable_id_and_type'
|
9
|
+
}
|
10
|
+
t.string :trace_hash, null: false, index: true
|
11
|
+
t.json :trace, null: false
|
12
|
+
t.integer :occurrences, null: false, index: true, default: 0
|
13
|
+
t.timestamps
|
14
|
+
|
15
|
+
t.index %i[traceable_id trace_hash],
|
16
|
+
unique: true,
|
17
|
+
name: 'index_pcbt_on_traceable_id_and_trace_hash'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateCruftTrackerArguments < ActiveRecord::Migration[5.2]
|
2
|
+
def change
|
3
|
+
create_table :cruft_tracker_arguments do |t|
|
4
|
+
t.references :method, null: false
|
5
|
+
t.string :arguments_hash, null: false, index: true
|
6
|
+
t.json :arguments, null: false
|
7
|
+
t.integer :occurrences, null: false, index: true, default: 0
|
8
|
+
t.timestamps
|
9
|
+
|
10
|
+
t.index %i[method_id arguments_hash],
|
11
|
+
unique: true,
|
12
|
+
name: 'index_pca_on_method_id_and_arguments_hash'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CruftTracker
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace CruftTracker
|
4
|
+
|
5
|
+
config.after_initialize do
|
6
|
+
CruftTracker::CleanupUntrackedMethods.run!
|
7
|
+
rescue StandardError
|
8
|
+
# Swallow all errors to prevent initialization failures.
|
9
|
+
end
|
10
|
+
|
11
|
+
# initializer 'cruft_tracker.action_controller' do |app|
|
12
|
+
# ActiveSupport.on_load(:action_controller) do
|
13
|
+
# ::ActionController::Base.helper(CruftTracker::ApplicationHelper)
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CruftTracker
|
4
|
+
class Registry
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
attr_accessor :tracked_methods
|
8
|
+
|
9
|
+
def self.<<(tracked_method)
|
10
|
+
instance.tracked_methods << tracked_method
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.include?(tracked_method)
|
14
|
+
instance.tracked_methods.include?(tracked_method)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.reset
|
18
|
+
instance.tracked_methods = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@tracked_methods = []
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'cruft_tracker/version'
|
2
|
+
require 'cruft_tracker/engine'
|
3
|
+
require 'cruft_tracker/registry'
|
4
|
+
|
5
|
+
module CruftTracker
|
6
|
+
# Your code goes here...
|
7
|
+
|
8
|
+
def self.is_this_view_used?
|
9
|
+
puts '>>>> is this view used?'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.is_this_method_used?(
|
13
|
+
owner,
|
14
|
+
name,
|
15
|
+
method_type: nil,
|
16
|
+
comment: nil,
|
17
|
+
track_arguments: nil
|
18
|
+
)
|
19
|
+
CruftTracker::TrackMethod.run!(
|
20
|
+
owner: owner,
|
21
|
+
name: name,
|
22
|
+
method_type: method_type,
|
23
|
+
comment: comment,
|
24
|
+
arguments_transformer: track_arguments
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.are_any_of_these_methods_being_used?(owner, comment: nil)
|
29
|
+
CruftTracker::TrackAllMethods.run!(owner: owner, comment: comment)
|
30
|
+
end
|
31
|
+
end
|