bulletmark_repairer 0.1.6 → 0.1.7
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 +5 -0
- data/lib/bulletmark_repairer/controller_corrector.rb +26 -4
- data/lib/bulletmark_repairer/markers.rb +2 -2
- data/lib/bulletmark_repairer/monkey_patches/action_view/base.rb +14 -0
- data/lib/bulletmark_repairer/monkey_patches/active_record/query_method.rb +3 -3
- data/lib/bulletmark_repairer/rack.rb +8 -1
- data/lib/bulletmark_repairer/railtie.rb +4 -0
- data/lib/bulletmark_repairer/retry_corrector.rb +26 -4
- data/lib/bulletmark_repairer/thread.rb +37 -9
- data/lib/bulletmark_repairer/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39af4a769d591a28ab628d174b6224984bf3f87460e2ee4d782a5a16a3c04d87
|
4
|
+
data.tar.gz: 2b163d905540357085ef7d9436abc189d4cde357f95a5e7650b538b195bb95bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5a8331d8787887ff29e736d353978d6d86000d14713df1576f131c9a661aaf166421aa963bd4752a5d3d55f2ee642fdbfeac3e551defe126450cd6d4c5d1aa3
|
7
|
+
data.tar.gz: 9d9a6aa80d778ec59b86d4eea3123e732ab146b01dfaf46ef78a7d66ebb0e1923558a903eef36afd895fb97404b4650585ad7ee3b5cbc34e16b947d790952f7b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## [0.1.7] - 2023-11-21
|
2
|
+
|
3
|
+
- Validate detected instance_variable is an ActiveRecord::Relation [ed650ae](https://github.com/makicamel/bulletmark_repairer/commit/ed650ae41b4389774cb1135031f077e953d2c5db) [86497bd](https://github.com/makicamel/bulletmark_repairer/commit/86497bd3cbb6daf1672cd18210e5842d0ecc084f)
|
4
|
+
- Add includes after methods returning AR on autocorrect [767e5e1](https://github.com/makicamel/bulletmark_repairer/commit/767e5e1389f84daa6efce4463a4979a100c16640)
|
5
|
+
|
1
6
|
## [0.1.6] - 2023-11-09
|
2
7
|
|
3
8
|
Fix a bug when N+1 is caused not in the request (e.g. Sidekiq) [51f051e](https://github.com/makicamel/bulletmark_repairer/commit/51f051e608b84b7da96ac879a324ed438c14eeeb)
|
@@ -33,6 +33,14 @@ class ControllerCorrector < Parser::TreeRewriter
|
|
33
33
|
@patched ||= false
|
34
34
|
end
|
35
35
|
|
36
|
+
def includes_token
|
37
|
+
@includes_token ||= ".includes(#{associations})"
|
38
|
+
end
|
39
|
+
|
40
|
+
def inserted?(parent_node)
|
41
|
+
parent_node.location.expression.source.include?(includes_token)
|
42
|
+
end
|
43
|
+
|
36
44
|
def target_nodes
|
37
45
|
@target_nodes ||= {}
|
38
46
|
end
|
@@ -47,10 +55,8 @@ class ControllerCorrector < Parser::TreeRewriter
|
|
47
55
|
|
48
56
|
type, identifier = node.to_sexp_array.take(2)
|
49
57
|
if type == :ivasgn && identifier == instance_variable_name
|
50
|
-
|
51
|
-
|
52
|
-
insert_after node.children.last.location.expression, inserted
|
53
|
-
@patched = true
|
58
|
+
node.children.each do |child_node|
|
59
|
+
execute_insert_includes(node: child_node, parent_node: node)
|
54
60
|
end
|
55
61
|
else
|
56
62
|
node
|
@@ -67,6 +73,22 @@ class ControllerCorrector < Parser::TreeRewriter
|
|
67
73
|
end
|
68
74
|
end
|
69
75
|
|
76
|
+
def execute_insert_includes(node:, parent_node:)
|
77
|
+
return unless node.respond_to?(:children)
|
78
|
+
return if patched?
|
79
|
+
|
80
|
+
node.children.each do |child_node|
|
81
|
+
if child_node.is_a?(Symbol)
|
82
|
+
if BulletmarkRepairer::Thread.correctable_method?(child_node) && !inserted?(parent_node)
|
83
|
+
insert_after node.location.expression, includes_token
|
84
|
+
@patched = true
|
85
|
+
end
|
86
|
+
else
|
87
|
+
execute_insert_includes(node: child_node, parent_node: node)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
70
92
|
def action
|
71
93
|
:__EMBEDDED_ACTION__
|
72
94
|
end
|
@@ -82,7 +82,6 @@ module BulletmarkRepairer
|
|
82
82
|
end
|
83
83
|
view_file, view_yield_index = @stacktraces[view_file_index].scan(%r{\A(/[./\w]+):(\d+):in `[\w]+'\z}).flatten
|
84
84
|
view_yield_index = view_yield_index.to_i
|
85
|
-
# TODO: Compile views
|
86
85
|
File.open(view_file) do |f|
|
87
86
|
lines = f.readlines
|
88
87
|
loop do
|
@@ -90,7 +89,8 @@ module BulletmarkRepairer
|
|
90
89
|
|
91
90
|
view_yield_index -= 1
|
92
91
|
line = lines[view_yield_index]
|
93
|
-
|
92
|
+
token = line&.scan(/\b?(@[\w]+)\b?/)&.flatten&.last
|
93
|
+
@instance_variable_name = token if BulletmarkRepairer::Thread.instance_variable_name?(token)
|
94
94
|
end
|
95
95
|
end
|
96
96
|
@index = @instance_variable_name ? "#{view_file}:#{view_yield_index}" : nil
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BulletmarkRepairer
|
4
|
+
module ActionView
|
5
|
+
module Base
|
6
|
+
def initialize(*args)
|
7
|
+
super(*args)
|
8
|
+
@_assigns.each do |ivname, value|
|
9
|
+
BulletmarkRepairer::Thread.memorize_instance_variable_name(name: ivname, value: value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -4,17 +4,17 @@ module BulletmarkRepairer
|
|
4
4
|
module ActiveRecord
|
5
5
|
module QueryMethod
|
6
6
|
def includes(*args)
|
7
|
-
BulletmarkRepairer::Thread.
|
7
|
+
BulletmarkRepairer::Thread.memorize_associations(name: model.name, method_type: :includes, args: args)
|
8
8
|
super(args)
|
9
9
|
end
|
10
10
|
|
11
11
|
def eager_load(*args)
|
12
|
-
BulletmarkRepairer::Thread.
|
12
|
+
BulletmarkRepairer::Thread.memorize_associations(name: model.name, method_type: :eager_load, args: args)
|
13
13
|
super(args)
|
14
14
|
end
|
15
15
|
|
16
16
|
def preload(*args)
|
17
|
-
BulletmarkRepairer::Thread.
|
17
|
+
BulletmarkRepairer::Thread.memorize_associations(name: model.name, method_type: :preload, args: args)
|
18
18
|
super(args)
|
19
19
|
end
|
20
20
|
end
|
@@ -7,15 +7,22 @@ module BulletmarkRepairer
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(env)
|
10
|
+
trace_point = TracePoint.trace(:return) do |tp|
|
11
|
+
BulletmarkRepairer::Thread.memorize_methods(
|
12
|
+
method_name: tp.method_id,
|
13
|
+
value: tp.return_value
|
14
|
+
)
|
15
|
+
end
|
10
16
|
@app.call(env)
|
11
17
|
ensure
|
18
|
+
trace_point.disable
|
12
19
|
begin
|
13
20
|
if ::Thread.current[:bullet_notification_collector].notifications_present?
|
14
21
|
BulletmarkRepairer::Patcher.execute(
|
15
22
|
notifications: ::Thread.current[:bullet_notification_collector],
|
16
23
|
controller: env['action_dispatch.request.parameters']['controller'],
|
17
24
|
action: env['action_dispatch.request.parameters']['action'],
|
18
|
-
loaded_associations: BulletmarkRepairer::Thread.current
|
25
|
+
loaded_associations: BulletmarkRepairer::Thread.current(:loaded_associations)
|
19
26
|
)
|
20
27
|
end
|
21
28
|
rescue StandardError => e
|
@@ -14,5 +14,9 @@ module BulletmarkRepairer
|
|
14
14
|
require 'bulletmark_repairer/monkey_patches/active_record/query_method'
|
15
15
|
::ActiveRecord::Relation.prepend(BulletmarkRepairer::ActiveRecord::QueryMethod)
|
16
16
|
end
|
17
|
+
ActiveSupport.on_load(:action_view) do
|
18
|
+
require 'bulletmark_repairer/monkey_patches/action_view/base'
|
19
|
+
::ActionView::Base.prepend(BulletmarkRepairer::ActionView::Base)
|
20
|
+
end
|
17
21
|
end
|
18
22
|
end
|
@@ -13,6 +13,14 @@ class RetryCorrector < Parser::TreeRewriter
|
|
13
13
|
@patched ||= false
|
14
14
|
end
|
15
15
|
|
16
|
+
def includes_token
|
17
|
+
@includes_token ||= ".includes(#{associations})"
|
18
|
+
end
|
19
|
+
|
20
|
+
def inserted?(parent_node)
|
21
|
+
parent_node.location.expression.source.include?(includes_token)
|
22
|
+
end
|
23
|
+
|
16
24
|
def insert_includes(node:)
|
17
25
|
return if patched?
|
18
26
|
return if !node.respond_to?(:children) || node.children.empty?
|
@@ -21,11 +29,25 @@ class RetryCorrector < Parser::TreeRewriter
|
|
21
29
|
if node.type == :begin
|
22
30
|
node.children.each { |child_node| insert_includes(node: child_node) }
|
23
31
|
else
|
24
|
-
|
25
|
-
|
32
|
+
node.children.each do |child_node|
|
33
|
+
execute_insert_includes(node: child_node, parent_node: node)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def execute_insert_includes(node:, parent_node:)
|
39
|
+
return unless node.respond_to?(:children)
|
40
|
+
return if patched?
|
26
41
|
|
27
|
-
|
28
|
-
|
42
|
+
node.children.each do |child_node|
|
43
|
+
if child_node.is_a?(Symbol)
|
44
|
+
if BulletmarkRepairer::Thread.correctable_method?(child_node) && !inserted?(parent_node)
|
45
|
+
insert_after node.location.expression, includes_token
|
46
|
+
@patched = true
|
47
|
+
end
|
48
|
+
else
|
49
|
+
execute_insert_includes(node: child_node, parent_node: node)
|
50
|
+
end
|
29
51
|
end
|
30
52
|
end
|
31
53
|
|
@@ -5,24 +5,52 @@ require 'rails'
|
|
5
5
|
module BulletmarkRepairer
|
6
6
|
class Thread
|
7
7
|
class << self
|
8
|
-
def current
|
9
|
-
touch
|
8
|
+
def current(key)
|
9
|
+
touch(key)
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
12
|
+
def memorize_associations(name:, method_type:, args:)
|
13
|
+
current(:loaded_associations)[name][method_type].add(args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def memorize_instance_variable_name(name:, value:)
|
17
|
+
return unless value.is_a?(::ActiveRecord::Relation)
|
18
|
+
|
19
|
+
current(:loaded_instance_variables).add("@#{name}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def instance_variable_name?(name)
|
23
|
+
current(:loaded_instance_variables).include?(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO: Memorize methods with class
|
27
|
+
def memorize_methods(method_name:, value:)
|
28
|
+
return unless value.is_a?(::ActiveRecord::Associations::CollectionProxy) || value.is_a?(::ActiveRecord::Relation)
|
29
|
+
|
30
|
+
current(:loaded_methods).add(method_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def correctable_method?(method_name)
|
34
|
+
current(:loaded_methods).include?(method_name)
|
15
35
|
end
|
16
36
|
|
17
37
|
def clear
|
18
|
-
::Thread.current[:
|
38
|
+
::Thread.current[:bulletmark_repairer] = nil
|
19
39
|
end
|
20
40
|
|
21
41
|
private
|
22
42
|
|
23
|
-
def touch
|
24
|
-
::Thread.current[:
|
25
|
-
|
43
|
+
def touch(key)
|
44
|
+
::Thread.current[:bulletmark_repairer] ||= {}
|
45
|
+
case key
|
46
|
+
when :loaded_associations
|
47
|
+
::Thread.current[:bulletmark_repairer][:loaded_associations] ||= Hash.new do |hash, key|
|
48
|
+
hash[key] = { includes: Set.new, eager_load: Set.new, preload: Set.new }
|
49
|
+
end
|
50
|
+
when :loaded_instance_variables
|
51
|
+
::Thread.current[:bulletmark_repairer][:loaded_instance_variables] ||= Set.new
|
52
|
+
when :loaded_methods
|
53
|
+
::Thread.current[:bulletmark_repairer][:loaded_methods] ||= Set.new
|
26
54
|
end
|
27
55
|
end
|
28
56
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bulletmark_repairer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- makicamel
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -90,6 +90,7 @@ files:
|
|
90
90
|
- lib/bulletmark_repairer/corrector_builder.rb
|
91
91
|
- lib/bulletmark_repairer/loaded_associations.rb
|
92
92
|
- lib/bulletmark_repairer/markers.rb
|
93
|
+
- lib/bulletmark_repairer/monkey_patches/action_view/base.rb
|
93
94
|
- lib/bulletmark_repairer/monkey_patches/active_record/query_method.rb
|
94
95
|
- lib/bulletmark_repairer/patcher.rb
|
95
96
|
- lib/bulletmark_repairer/rack.rb
|
@@ -121,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
122
|
- !ruby/object:Gem::Version
|
122
123
|
version: '0'
|
123
124
|
requirements: []
|
124
|
-
rubygems_version: 3.
|
125
|
+
rubygems_version: 3.4.10
|
125
126
|
signing_key:
|
126
127
|
specification_version: 4
|
127
128
|
summary: Auto corrector for N+1 queries detected at runtime with Bullet
|