bulletmark_repairer 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5eabc96bcf1961f834090322c3b9a62173139f806513c5ec99d452f18a661c71
4
- data.tar.gz: b55ab6f5adade690b6cb34ef92fc752bdfa697a3b8d7779d54150aaf4e1083bb
3
+ metadata.gz: 39af4a769d591a28ab628d174b6224984bf3f87460e2ee4d782a5a16a3c04d87
4
+ data.tar.gz: 2b163d905540357085ef7d9436abc189d4cde357f95a5e7650b538b195bb95bc
5
5
  SHA512:
6
- metadata.gz: 714da7b74b7015194f965c83c1260d31b8a4f75b4f7a24b3038eac6c81ced4aa8a77a79abbe5a684a347093fbb00d0e9f1b05682729b96afe64ed2a83b110534
7
- data.tar.gz: e48064688caaa2003d30192bfd2a76ff677fdcb4b4bb02bb8b828fece22b9fd5dab69a47aef2fb836a69e9f5b16db10d2c1faefcb9e249ba5b8a3df671310c03
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
- inserted = ".includes(#{associations})"
51
- unless node.location.expression.source.include?(inserted)
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
- @instance_variable_name = line&.scan(/\b?(@[\w]+)\b?/)&.flatten&.last
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.add(name: model.name, method_type: :includes, args: args)
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.add(name: model.name, method_type: :eager_load, args: args)
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.add(name: model.name, method_type: :preload, args: args)
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
- inserted = ".includes(#{associations})"
25
- return if node.location.expression.source.include?(inserted)
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
- insert_after node.location.expression, ".includes(#{associations})"
28
- @patched = true
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 add(name:, method_type:, args:)
13
- touch
14
- ::Thread.current[:bulletmark_repaier_loaded_associations][name][method_type].add(args)
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[:bulletmark_repaier_loaded_associations] = nil
38
+ ::Thread.current[:bulletmark_repairer] = nil
19
39
  end
20
40
 
21
41
  private
22
42
 
23
- def touch
24
- ::Thread.current[:bulletmark_repaier_loaded_associations] ||= Hash.new do |hash, key|
25
- hash[key] = { includes: Set.new, eager_load: Set.new, preload: Set.new }
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BulletmarkRepairer
4
- VERSION = '0.1.6'
4
+ VERSION = '0.1.7'
5
5
  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.6
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-08 00:00:00.000000000 Z
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.1.4
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