eager_eye 1.1.0 → 1.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03025eeca47525318e9dbd1ffe0046bcd6647c16bc5696a1fd0b7906906ebc9d
4
- data.tar.gz: c9fa809e3bcc95a6ea20d6f9ef18ca45241ad96e325fef76b0aa90e79b19e3a3
3
+ metadata.gz: 8b6c076a6dd51cb872bfb213cbb83369a7d9f159bb20054b41901710b738b4df
4
+ data.tar.gz: 15850c235c48858cbbb2585c83ffaeacf1331a029b959f973aede31ef584ae24
5
5
  SHA512:
6
- metadata.gz: c3faea6fb82cda3d118584970dba7bb88fa2fa1024dd6906d03f89699ff93b5f4d2a487b6c598eb9ee6bc18ae222b308f959d3cefbaed7d54c808aba93bc2fb2
7
- data.tar.gz: 355d9b1c678b755ff679492183e7046b3edb511bff6e8fe3ff0990f091274917877cf8495984cb8315fe1096b0308a3528aff8c56fc6f1ed22270aa4257b0d03
6
+ metadata.gz: 0454d92551dcc8d96ae35513ae89c3198a707ee6aac43538c7233d679cb9328c9190c8def794736c53cf61b06419051baa937a9358288a046590de14d15c22b1
7
+ data.tar.gz: bfd0dc368252cf5d0118a5dcf912b981ea02bf6f158a80a59a1fc475f0f5810c302c223c4126dfc639f8a5ad5e8be3fbcfd3ed87d5c21d3e148405a5f5033b1a
data/.rubocop.yml CHANGED
@@ -20,7 +20,7 @@ Metrics/BlockLength:
20
20
  - "lib/eager_eye/railtie.rb"
21
21
 
22
22
  Metrics/ClassLength:
23
- Max: 165
23
+ Max: 155
24
24
 
25
25
  Metrics/ParameterLists:
26
26
  Max: 6
data/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.1.1] - 2026-01-03
11
+
12
+ ### Fixed
13
+
14
+ - **SerializerNesting False Positive** - No longer flags `belongs_to` associations
15
+ - `user.author`, `subscription.user` etc. (singular) are now ignored
16
+ - Only `has_many` associations (plural names) are flagged as potential N+1
17
+
10
18
  ## [1.1.0] - 2025-12-28
11
19
 
12
20
  ### Added
data/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  <p align="center">
12
12
  <a href="https://github.com/hamzagedikkaya/eager_eye/actions/workflows/main.yml"><img src="https://github.com/hamzagedikkaya/eager_eye/actions/workflows/main.yml/badge.svg" alt="CI"></a>
13
- <a href="https://rubygems.org/gems/eager_eye"><img src="https://img.shields.io/badge/gem-v1.1.0-red.svg" alt="Gem Version"></a>
13
+ <a href="https://rubygems.org/gems/eager_eye"><img src="https://img.shields.io/badge/gem-v1.1.1-red.svg" alt="Gem Version"></a>
14
14
  <a href="https://github.com/hamzagedikkaya/eager_eye"><img src="https://img.shields.io/badge/coverage-95%25-brightgreen.svg" alt="Coverage"></a>
15
15
  <a href="https://www.ruby-lang.org/"><img src="https://img.shields.io/badge/ruby-%3E%3D%203.1-ruby.svg" alt="Ruby"></a>
16
16
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
data/SECURITY.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  | Version | Supported |
6
6
  | ------- | ------------------ |
7
+ | 1.1.x | :white_check_mark: |
7
8
  | 1.0.x | :white_check_mark: |
8
9
  | < 1.0 | :x: |
9
10
 
@@ -33,17 +33,15 @@ module EagerEye
33
33
 
34
34
  def collect_association_preloads
35
35
  model_files.each do |file_path|
36
- source = File.read(file_path)
37
- ast = parse_source(source)
36
+ ast = parse_source(File.read(file_path))
38
37
  next unless ast
39
38
 
40
- model_name = extract_model_name(file_path)
41
39
  parser = AssociationParser.new
42
- parser.parse_model(ast, model_name)
40
+ parser.parse_model(ast, extract_model_name(file_path))
43
41
  @association_preloads.merge!(parser.preloaded_associations)
44
42
  end
45
43
  rescue StandardError
46
- # Silently skip errors in association parsing
44
+ nil
47
45
  end
48
46
 
49
47
  def model_files
@@ -84,22 +82,15 @@ module EagerEye
84
82
  return unless ast
85
83
 
86
84
  comment_parser = CommentParser.new(source)
85
+ min_severity = EagerEye.configuration.min_severity
87
86
 
88
87
  enabled_detectors.each do |detector|
89
88
  args = [ast, file_path]
90
89
  args << @association_preloads if detector.is_a?(Detectors::LoopAssociation)
91
90
 
92
91
  file_issues = detector.detect(*args)
93
-
94
- # Filter suppressed issues
95
- file_issues.reject! do |issue|
96
- comment_parser.disabled_at?(issue.line_number, issue.detector)
97
- end
98
-
99
- # Filter by minimum severity
100
- min_severity = EagerEye.configuration.min_severity
92
+ file_issues.reject! { |issue| comment_parser.disabled_at?(issue.line_number, issue.detector) }
101
93
  file_issues.select! { |issue| issue.meets_minimum_severity?(min_severity) }
102
-
103
94
  @issues.concat(file_issues)
104
95
  end
105
96
  rescue Errno::ENOENT, Errno::EACCES => e
@@ -42,33 +42,17 @@ module EagerEye
42
42
  end
43
43
 
44
44
  def extract_association_name(node)
45
- args = node.children[2..]
46
- return nil if args.empty?
47
-
48
- first_arg = args[0]
49
- return nil unless first_arg&.type == :sym
50
-
51
- first_arg.children[0]
45
+ first_arg = node.children[2]
46
+ first_arg&.type == :sym ? first_arg.children[0] : nil
52
47
  end
53
48
 
54
49
  def extract_preloaded_associations(node)
55
50
  preloaded = Set.new
56
- args = node.children[2..]
57
- return preloaded if args.empty?
58
-
59
- # Check for block with includes/preload/eager_load
60
- block_node = args.find { |arg| arg&.type == :block }
61
- return preloaded unless block_node
62
-
63
- extract_from_block(block_node, preloaded)
51
+ block_node = node.children[2..].find { |arg| arg&.type == :block }
52
+ traverse_for_preloads(block_node&.children&.[](2), preloaded) if block_node
64
53
  preloaded
65
54
  end
66
55
 
67
- def extract_from_block(block_node, preloaded)
68
- block_body = block_node.children[2]
69
- traverse_for_preloads(block_body, preloaded)
70
- end
71
-
72
56
  def traverse_for_preloads(node, preloaded)
73
57
  return unless node.is_a?(Parser::AST::Node)
74
58
 
@@ -78,17 +62,11 @@ module EagerEye
78
62
  end
79
63
 
80
64
  def preload_call?(node)
81
- return false unless node.type == :send
82
-
83
- method = node.children[1]
84
- %i[includes preload eager_load].include?(method)
65
+ node.type == :send && %i[includes preload eager_load].include?(node.children[1])
85
66
  end
86
67
 
87
68
  def extract_includes_from_method(node, included)
88
- args = node.children[2..]
89
- return if args.empty?
90
-
91
- args.each { |arg| add_included_sym(arg, included) }
69
+ node.children[2..].each { |arg| add_included_sym(arg, included) }
92
70
  end
93
71
 
94
72
  def add_included_sym(arg, included)
@@ -12,11 +12,7 @@ module EagerEye
12
12
  fixes = collect_fixes
13
13
  return puts "No auto-fixable issues found." if fixes.empty?
14
14
 
15
- if @interactive
16
- apply_interactively(fixes)
17
- else
18
- apply_all(fixes)
19
- end
15
+ @interactive ? apply_interactively(fixes) : apply_all(fixes)
20
16
  end
21
17
 
22
18
  def suggest
@@ -37,12 +33,9 @@ module EagerEye
37
33
 
38
34
  def collect_fixes
39
35
  @issues.filter_map do |issue|
40
- source = read_file(issue.file_path)
41
- fixer = FixerRegistry.fixer_for(issue, source)
42
- next unless fixer&.fixable?
43
-
44
- fixer.diff
45
- end.compact
36
+ fixer = FixerRegistry.fixer_for(issue, read_file(issue.file_path))
37
+ fixer&.fixable? ? fixer.diff : nil
38
+ end
46
39
  end
47
40
 
48
41
  def read_file(path)
@@ -71,7 +64,6 @@ module EagerEye
71
64
  end
72
65
 
73
66
  def apply_all(fixes)
74
- # Group by file to minimize file operations
75
67
  fixes.group_by { |f| f[:file] }.each do |file, file_fixes|
76
68
  lines = File.readlines(file)
77
69
 
data/lib/eager_eye/cli.rb CHANGED
@@ -137,9 +137,7 @@ module EagerEye
137
137
 
138
138
  def analyze
139
139
  configure_from_options!
140
-
141
- analyzer = Analyzer.new(paths: options[:paths])
142
- analyzer.run
140
+ Analyzer.new(paths: options[:paths]).run
143
141
  end
144
142
 
145
143
  def configure_from_options!
@@ -165,10 +163,7 @@ module EagerEye
165
163
  end
166
164
 
167
165
  def exit_code(issues)
168
- return 0 unless options[:fail_on_issues]
169
- return 0 if issues.empty?
170
-
171
- 1
166
+ options[:fail_on_issues] && issues.any? ? 1 : 0
172
167
  end
173
168
  end
174
169
  end
@@ -131,11 +131,6 @@ module EagerEye
131
131
  end
132
132
  end
133
133
 
134
- def inline_disable?(line)
135
- code_part = line.split("#").first
136
- code_part && !code_part.strip.empty?
137
- end
138
-
139
134
  def code_before_comment?(line)
140
135
  code_part = line.split("#").first
141
136
  code_part && !code_part.strip.empty?
@@ -41,10 +41,7 @@ module EagerEye
41
41
  return unless node.is_a?(Parser::AST::Node)
42
42
 
43
43
  yield node
44
-
45
- node.children.each do |child|
46
- traverse_ast(child, &block)
47
- end
44
+ node.children.each { |child| traverse_ast(child, &block) }
48
45
  end
49
46
 
50
47
  def parse_source(source)
@@ -61,14 +58,10 @@ module EagerEye
61
58
 
62
59
  def extract_symbols_from_args(args)
63
60
  symbols = Set.new
64
- return symbols if args.empty?
65
-
66
61
  args.each do |arg|
67
62
  case arg&.type
68
- when :sym
69
- symbols.add(arg.children[0])
70
- when :hash
71
- extract_symbols_from_hash(arg, symbols)
63
+ when :sym then symbols.add(arg.children[0])
64
+ when :hash then extract_symbols_from_hash(arg, symbols)
72
65
  end
73
66
  end
74
67
  symbols
@@ -51,18 +51,11 @@ module EagerEye
51
51
  return unless node.is_a?(Parser::AST::Node)
52
52
 
53
53
  extract_callback_method_name(node) if callback_definition?(node)
54
-
55
- node.children.each do |child|
56
- find_callback_definitions(child)
57
- end
54
+ node.children.each { |child| find_callback_definitions(child) }
58
55
  end
59
56
 
60
57
  def callback_definition?(node)
61
- return false unless node.type == :send
62
- return false unless node.children[0].nil?
63
-
64
- method_name = node.children[1]
65
- CALLBACK_METHODS.include?(method_name)
58
+ node.type == :send && node.children[0].nil? && CALLBACK_METHODS.include?(node.children[1])
66
59
  end
67
60
 
68
61
  def extract_callback_method_name(node)
@@ -78,28 +71,13 @@ module EagerEye
78
71
  def check_callback_methods(node)
79
72
  return unless node.is_a?(Parser::AST::Node)
80
73
 
81
- if method_definition?(node)
74
+ if node.type == :def && @callback_methods.key?(node.children[0])
82
75
  method_name = node.children[0]
83
- if @callback_methods.key?(method_name)
84
- callback_type = @callback_methods[method_name]
85
- check_method_body_for_queries(node, method_name, callback_type)
86
- end
76
+ body = node.children[2]
77
+ find_iterations_with_queries(body, method_name, @callback_methods[method_name]) if body
87
78
  end
88
79
 
89
- node.children.each do |child|
90
- check_callback_methods(child)
91
- end
92
- end
93
-
94
- def method_definition?(node)
95
- node.type == :def
96
- end
97
-
98
- def check_method_body_for_queries(method_node, method_name, callback_type)
99
- method_body = method_node.children[2]
100
- return unless method_body
101
-
102
- find_iterations_with_queries(method_body, method_name, callback_type)
80
+ node.children.each { |child| check_callback_methods(child) }
103
81
  end
104
82
 
105
83
  def find_iterations_with_queries(node, method_name, callback_type)
@@ -111,9 +89,7 @@ module EagerEye
111
89
  find_query_calls_in_block(node, method_name, callback_type, block_var) if block_var
112
90
  end
113
91
 
114
- node.children.each do |child|
115
- find_iterations_with_queries(child, method_name, callback_type)
116
- end
92
+ node.children.each { |child| find_iterations_with_queries(child, method_name, callback_type) }
117
93
  end
118
94
 
119
95
  def find_query_calls_in_block(node, method_name, callback_type, block_var)
@@ -123,26 +99,16 @@ module EagerEye
123
99
  add_query_issue(node, method_name, callback_type)
124
100
  end
125
101
 
126
- node.children.each do |child|
127
- find_query_calls_in_block(child, method_name, callback_type, block_var)
128
- end
102
+ node.children.each { |child| find_query_calls_in_block(child, method_name, callback_type, block_var) }
129
103
  end
130
104
 
131
105
  def query_call?(node)
132
- return false unless node.type == :send
133
-
134
- method = node.children[1]
135
- QUERY_INDICATORS.include?(method)
106
+ node.type == :send && QUERY_INDICATORS.include?(node.children[1])
136
107
  end
137
108
 
138
109
  def iteration_block?(node)
139
- return false unless node.type == :block
140
-
141
- send_node = node.children[0]
142
- return false unless send_node&.type == :send
143
-
144
- method_name = send_node.children[1]
145
- ITERATION_METHODS.include?(method_name)
110
+ node.type == :block && node.children[0]&.type == :send &&
111
+ ITERATION_METHODS.include?(node.children[0].children[1])
146
112
  end
147
113
 
148
114
  def add_query_issue(node, method_name, callback_type)
@@ -3,14 +3,8 @@
3
3
  module EagerEye
4
4
  module Detectors
5
5
  class CountInIteration < Base
6
- # count always executes a COUNT query
7
- # size and length use memory when collection is loaded
8
6
  COUNT_METHODS = %i[count].freeze
9
-
10
- ITERATION_METHODS = %i[
11
- each map select find_all reject collect
12
- each_with_index each_with_object flat_map
13
- ].freeze
7
+ ITERATION_METHODS = %i[each map select find_all reject collect each_with_index each_with_object flat_map].freeze
14
8
 
15
9
  def self.detector_name
16
10
  :count_in_iteration
@@ -36,74 +30,48 @@ module EagerEye
36
30
 
37
31
  if iteration_block?(node)
38
32
  block_var = extract_block_variable(node)
39
- block_body = extract_block_body(node)
33
+ block_body = node.children[2]
40
34
  yield(block_body, block_var) if block_var && block_body
41
35
  end
42
36
 
43
- node.children.each do |child|
44
- find_iteration_blocks(child, &block)
45
- end
37
+ node.children.each { |child| find_iteration_blocks(child, &block) }
46
38
  end
47
39
 
48
40
  def iteration_block?(node)
49
- return false unless node.type == :block
50
-
51
- send_node = node.children[0]
52
- return false unless send_node&.type == :send
53
-
54
- method_name = send_node.children[1]
55
- ITERATION_METHODS.include?(method_name)
41
+ node.type == :block && node.children[0]&.type == :send &&
42
+ ITERATION_METHODS.include?(node.children[0].children[1])
56
43
  end
57
44
 
58
45
  def check_for_count_calls(node, block_var)
59
46
  return unless node.is_a?(Parser::AST::Node)
60
47
 
61
48
  add_issue(node) if count_on_association?(node, block_var)
62
-
63
- node.children.each do |child|
64
- check_for_count_calls(child, block_var)
65
- end
49
+ node.children.each { |child| check_for_count_calls(child, block_var) }
66
50
  end
67
51
 
68
52
  def count_on_association?(node, block_var)
69
- return false unless node.type == :send
70
-
71
- method_name = node.children[1]
72
- return false unless COUNT_METHODS.include?(method_name)
73
-
74
- receiver = node.children[0]
75
- association_call_on_block_var?(receiver, block_var)
53
+ node.type == :send && COUNT_METHODS.include?(node.children[1]) &&
54
+ association_call_on_block_var?(node.children[0], block_var)
76
55
  end
77
56
 
78
57
  def association_call_on_block_var?(node, block_var)
79
- return false unless node.is_a?(Parser::AST::Node)
80
- return false unless node.type == :send
58
+ return false unless node.is_a?(Parser::AST::Node) && node.type == :send
81
59
 
82
60
  receiver = node.children[0]
83
61
  return false unless receiver.is_a?(Parser::AST::Node)
84
62
 
85
- # post.comments.count -> receiver is post.comments
86
- # post.comments -> receiver is post (lvar)
87
- if receiver.type == :lvar && receiver.children[0] == block_var
88
- true
89
- elsif receiver.type == :send
90
- # Nested: post.author.posts.count
91
- chain_starts_with_block_var?(receiver, block_var)
92
- else
93
- false
94
- end
63
+ return true if receiver.type == :lvar && receiver.children[0] == block_var
64
+
65
+ receiver.type == :send && chain_starts_with_block_var?(receiver, block_var)
95
66
  end
96
67
 
97
68
  def chain_starts_with_block_var?(node, block_var)
98
69
  return false unless node.is_a?(Parser::AST::Node)
99
70
 
100
71
  case node.type
101
- when :lvar
102
- node.children[0] == block_var
103
- when :send
104
- chain_starts_with_block_var?(node.children[0], block_var)
105
- else
106
- false
72
+ when :lvar then node.children[0] == block_var
73
+ when :send then chain_starts_with_block_var?(node.children[0], block_var)
74
+ else false
107
75
  end
108
76
  end
109
77
 
@@ -112,13 +80,7 @@ module EagerEye
112
80
  return nil unless args_node&.type == :args
113
81
 
114
82
  first_arg = args_node.children[0]
115
- return nil unless first_arg&.type == :arg
116
-
117
- first_arg.children[0]
118
- end
119
-
120
- def extract_block_body(block_node)
121
- block_node.children[2]
83
+ first_arg&.type == :arg ? first_arg.children[0] : nil
122
84
  end
123
85
 
124
86
  def add_issue(node)
@@ -3,27 +3,9 @@
3
3
  module EagerEye
4
4
  module Detectors
5
5
  class CustomMethodQuery < Base
6
- QUERY_METHODS = %i[
7
- where
8
- find_by
9
- find_by!
10
- exists?
11
- find
12
- first
13
- last
14
- take
15
- pluck
16
- ids
17
- count
18
- sum
19
- average
20
- minimum
21
- maximum
22
- ].freeze
23
-
24
- # Array-only methods that should not be flagged when collection is clearly an array
6
+ QUERY_METHODS = %i[where find_by find_by! exists? find first last take pluck ids count sum average minimum
7
+ maximum].freeze
25
8
  ARRAY_METHODS = %i[first last take].freeze
26
-
27
9
  ITERATION_METHODS = %i[each map select find_all reject collect detect find_index flat_map].freeze
28
10
 
29
11
  def self.detector_name
@@ -51,34 +33,22 @@ module EagerEye
51
33
 
52
34
  if iteration_block?(node)
53
35
  block_var = extract_block_variable(node)
54
- block_body = extract_block_body(node)
55
- collection = extract_collection(node)
56
- yield(block_body, block_var, collection) if block_var && block_body
57
- end
58
-
59
- node.children.each do |child|
60
- find_iteration_blocks(child, &block)
36
+ block_body = node.children[2]
37
+ yield(block_body, block_var, node.children[0]) if block_var && block_body
61
38
  end
39
+ node.children.each { |child| find_iteration_blocks(child, &block) }
62
40
  end
63
41
 
64
42
  def iteration_block?(node)
65
- return false unless node.type == :block
66
-
67
- send_node = node.children[0]
68
- return false unless send_node&.type == :send
69
-
70
- method_name = send_node.children[1]
71
- ITERATION_METHODS.include?(method_name)
43
+ node.type == :block && node.children[0]&.type == :send &&
44
+ ITERATION_METHODS.include?(node.children[0].children[1])
72
45
  end
73
46
 
74
47
  def check_block_for_query_methods(node, block_var, is_array_collection = false) # rubocop:disable Style/OptionalBooleanParameter
75
48
  return unless node.is_a?(Parser::AST::Node)
76
49
 
77
50
  add_issue(node) if query_chain_on_association?(node, block_var, is_array_collection)
78
-
79
- node.children.each do |child|
80
- check_block_for_query_methods(child, block_var, is_array_collection)
81
- end
51
+ node.children.each { |child| check_block_for_query_methods(child, block_var, is_array_collection) }
82
52
  end
83
53
 
84
54
  def query_chain_on_association?(node, block_var, is_array_collection = false) # rubocop:disable Style/OptionalBooleanParameter
@@ -86,35 +56,23 @@ module EagerEye
86
56
 
87
57
  method_name = node.children[1]
88
58
  return false unless QUERY_METHODS.include?(method_name)
59
+ return false if is_array_collection && ARRAY_METHODS.include?(method_name) &&
60
+ receiver_is_only_block_var?(node.children[0], block_var)
89
61
 
90
- # Skip array-only methods when collection is clearly an array (.map result)
91
- # AND the receiver is only the block variable (not chained)
92
- if is_array_collection && ARRAY_METHODS.include?(method_name) &&
93
- receiver_is_only_block_var?(node.children[0], block_var)
94
- return false
95
- end
96
-
97
- receiver = node.children[0]
98
- receiver_chain_starts_with?(receiver, block_var)
62
+ receiver_chain_starts_with?(node.children[0], block_var)
99
63
  end
100
64
 
101
65
  def receiver_is_only_block_var?(node, block_var)
102
- # Returns true only if receiver is EXACTLY the block variable, not a chain
103
- node.is_a?(Parser::AST::Node) &&
104
- node.type == :lvar &&
105
- node.children[0] == block_var
66
+ node.is_a?(Parser::AST::Node) && node.type == :lvar && node.children[0] == block_var
106
67
  end
107
68
 
108
69
  def receiver_chain_starts_with?(node, block_var)
109
70
  return false unless node.is_a?(Parser::AST::Node)
110
71
 
111
72
  case node.type
112
- when :lvar
113
- node.children[0] == block_var
114
- when :send
115
- receiver_chain_starts_with?(node.children[0], block_var)
116
- else
117
- false
73
+ when :lvar then node.children[0] == block_var
74
+ when :send then receiver_chain_starts_with?(node.children[0], block_var)
75
+ else false
118
76
  end
119
77
  end
120
78
 
@@ -123,37 +81,16 @@ module EagerEye
123
81
  return nil unless args_node&.type == :args
124
82
 
125
83
  first_arg = args_node.children[0]
126
- return nil unless first_arg&.type == :arg
127
-
128
- first_arg.children[0]
84
+ first_arg&.type == :arg ? first_arg.children[0] : nil
129
85
  end
130
86
 
131
- def extract_block_body(block_node)
132
- block_node.children[2]
133
- end
134
-
135
- def extract_collection(block_node)
136
- # Extract the collection being iterated on
137
- # For: collection.each { |item| ... }
138
- # Returns: the send node representing the collection method call
139
- block_node.children[0]
140
- end
141
-
142
- def collection_is_array?(collection_node)
143
- return false unless collection_node.is_a?(Parser::AST::Node)
87
+ def collection_is_array?(node)
88
+ return false unless node.is_a?(Parser::AST::Node)
144
89
 
145
- case collection_node.type
146
- when :array
147
- # Literal array: [1, 2, 3].each { |item| ... }
148
- true
149
- when :send
150
- # Only consider these methods as definitely returning arrays when iterating
151
- method_name = collection_node.children[1]
152
- # map, select, collect, etc. on anything return arrays for iteration
153
- %i[map select collect flat_map to_a uniq compact].include?(method_name)
154
- else
155
- # Block variable itself won't tell us if it's an array
156
- false
90
+ case node.type
91
+ when :array then true
92
+ when :send then %i[map select collect flat_map to_a uniq compact].include?(node.children[1])
93
+ else false
157
94
  end
158
95
  end
159
96
 
@@ -173,14 +110,11 @@ module EagerEye
173
110
  return "" unless node.is_a?(Parser::AST::Node)
174
111
 
175
112
  case node.type
176
- when :lvar
177
- node.children[0].to_s
113
+ when :lvar then node.children[0].to_s
178
114
  when :send
179
115
  receiver_str = reconstruct_chain(node.children[0])
180
- method = node.children[1]
181
- receiver_str.empty? ? method.to_s : "#{receiver_str}.#{method}"
182
- else
183
- ""
116
+ receiver_str.empty? ? node.children[1].to_s : "#{receiver_str}.#{node.children[1]}"
117
+ else ""
184
118
  end
185
119
  end
186
120
  end