cruft_tracker 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/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
|