eager_eye 1.2.6 → 1.2.8
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 +22 -0
- data/README.md +71 -7
- data/lib/eager_eye/analyzer.rb +16 -2
- data/lib/eager_eye/configuration.rb +4 -1
- data/lib/eager_eye/detectors/scope_chain_n_plus_one.rb +78 -0
- data/lib/eager_eye/scope_parser.rb +45 -0
- data/lib/eager_eye/version.rb +1 -1
- data/lib/eager_eye.rb +4 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79319b66a87234e31df5def9ba5c0a487e298de8620bd4022f9d7db63e455152
|
|
4
|
+
data.tar.gz: f400ab8eb6ca1762a9773f90d8d1311546ed58035b3a062c6d408f7a97fd17e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d70aff4b63cc9da79ca7956487a94d0e950aa85416b64636c1a2d8f94f898c1aea04712d3e964aaf5aafd8eea4fca9a817f09021806462123cc1b4bd45bec9ff
|
|
7
|
+
data.tar.gz: f77947b4812e87380618ac4544e717b5b17f08386540d6142a0e9f9fbbf9ed8fbdd6460beb8671e0a6e4fb8e8d2f21c1417eb9281082f5da67576bfd51990ab0
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.8] - 2026-03-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **New Detector: `ValidationNPlusOne`** - Detects batch create/save inside iterations for models with uniqueness validations
|
|
15
|
+
- Catches `User.create!(attrs)` and `User.new(attrs) + save!` patterns inside loops
|
|
16
|
+
- Parses model files for `validates :attr, uniqueness: true` and `validates_uniqueness_of` declarations
|
|
17
|
+
- Each uniqueness validation triggers a SELECT query per record — 2N queries total
|
|
18
|
+
- Suggests using `insert_all` with unique index constraints or batch-validating before saving
|
|
19
|
+
- **New Parser: `ValidationParser`** - Collects models with uniqueness validations for cross-file detection
|
|
20
|
+
|
|
21
|
+
## [1.2.7] - 2026-03-10
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **New Detector: `ScopeChainNPlusOne`** - Detects scope calls on associations inside iterations
|
|
26
|
+
- Catches `post.comments.recent`, `post.comments.approved.count` patterns
|
|
27
|
+
- Parses model files for `scope :name, -> { ... }` declarations
|
|
28
|
+
- Flags known scope names called on association chains inside loops
|
|
29
|
+
- Each scope call executes a new query per iteration — suggests preloading or joined queries
|
|
30
|
+
- **New Parser: `ScopeParser`** - Collects scope definitions from model files for cross-file detection
|
|
31
|
+
|
|
10
32
|
## [1.2.6] - 2026-02-25
|
|
11
33
|
|
|
12
34
|
### Changed
|
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.2.
|
|
13
|
+
<a href="https://rubygems.org/gems/eager_eye"><img src="https://img.shields.io/badge/gem-v1.2.8-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>
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
## Features
|
|
44
44
|
|
|
45
|
-
✨ **Detects
|
|
45
|
+
✨ **Detects 11 types of N+1 problems:**
|
|
46
46
|
- Loop associations (queries in iterations)
|
|
47
47
|
- Serializer nesting issues
|
|
48
48
|
- Missing counter caches
|
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
- Pluck to array misuse
|
|
53
53
|
- Delegation N+1s (hidden via `delegate :method, to: :association`)
|
|
54
54
|
- Decorator N+1s (Draper, SimpleDelegator, Presenter, ViewObject)
|
|
55
|
+
- Scope chain N+1s (named scopes on associations in loops)
|
|
56
|
+
- Validation N+1s (uniqueness validation in batch create/save)
|
|
55
57
|
|
|
56
58
|
🔧 **Developer-friendly:**
|
|
57
59
|
- Inline suppression (like RuboCop)
|
|
@@ -398,6 +400,62 @@ Supports the following object references inside decorators:
|
|
|
398
400
|
- `__getobj__` — SimpleDelegator standard
|
|
399
401
|
- `source`, `model` — alternative Draper aliases
|
|
400
402
|
|
|
403
|
+
### 10. Scope Chain N+1
|
|
404
|
+
|
|
405
|
+
Detects named scope calls on associations inside iterations. Unlike explicit query methods (`.where`, `.find_by`) caught by `CustomMethodQuery`, named scopes (`.recent`, `.active`, `.published`) are invisible query triggers.
|
|
406
|
+
|
|
407
|
+
```ruby
|
|
408
|
+
# Model
|
|
409
|
+
class Comment < ApplicationRecord
|
|
410
|
+
scope :recent, -> { where("created_at > ?", 1.week.ago) }
|
|
411
|
+
scope :approved, -> { where(approved: true) }
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Bad - scope call per iteration
|
|
415
|
+
posts.each do |post|
|
|
416
|
+
post.comments.recent # Query for each post!
|
|
417
|
+
post.comments.approved.count # Query for each post!
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# Good - preload and filter in Ruby
|
|
421
|
+
posts.includes(:comments).each do |post|
|
|
422
|
+
post.comments.select { |c| c.created_at > 1.week.ago }
|
|
423
|
+
end
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
EagerEye detects these by:
|
|
427
|
+
1. Scanning model files for `scope :name, -> { ... }` declarations
|
|
428
|
+
2. Flagging known scope names called on association chains inside iteration blocks
|
|
429
|
+
|
|
430
|
+
### 11. Validation N+1
|
|
431
|
+
|
|
432
|
+
Detects batch create/save operations inside iterations for models with `validates uniqueness`. Each uniqueness validation triggers a SELECT query per record, resulting in 2N queries (SELECT + INSERT per record).
|
|
433
|
+
|
|
434
|
+
```ruby
|
|
435
|
+
# Model
|
|
436
|
+
class User < ApplicationRecord
|
|
437
|
+
validates :email, uniqueness: true
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# Bad - 2N queries (SELECT + INSERT per record)
|
|
441
|
+
params[:users].each do |user_params|
|
|
442
|
+
User.create!(user_params) # SELECT + INSERT for each!
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Bad - same problem with new + save
|
|
446
|
+
params[:users].each do |user_params|
|
|
447
|
+
user = User.new(user_params)
|
|
448
|
+
user.save! # SELECT + INSERT for each!
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
# Good - use insert_all with unique index
|
|
452
|
+
User.insert_all(params[:users]) # Single bulk INSERT, DB enforces uniqueness
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
EagerEye detects these by:
|
|
456
|
+
1. Scanning model files for `validates :attr, uniqueness: true` or `validates_uniqueness_of` declarations
|
|
457
|
+
2. Flagging `Model.create/create!` or `Model.new` + `.save/.save!` patterns inside iteration blocks
|
|
458
|
+
|
|
401
459
|
## Inline Suppression
|
|
402
460
|
|
|
403
461
|
Suppress false positives using inline comments (RuboCop-style):
|
|
@@ -442,6 +500,8 @@ Both CamelCase and snake_case formats are accepted:
|
|
|
442
500
|
| Pluck to Array | `PluckToArray` | `pluck_to_array` |
|
|
443
501
|
| Delegation N+1 | `DelegationNPlusOne` | `delegation_n_plus_one` |
|
|
444
502
|
| Decorator N+1 | `DecoratorNPlusOne` | `decorator_n_plus_one` |
|
|
503
|
+
| Scope Chain N+1 | `ScopeChainNPlusOne` | `scope_chain_n_plus_one` |
|
|
504
|
+
| Validation N+1 | `ValidationNPlusOne` | `validation_n_plus_one` |
|
|
445
505
|
| All Detectors | `all` | `all` |
|
|
446
506
|
|
|
447
507
|
## Auto-fix (Experimental)
|
|
@@ -546,18 +606,22 @@ enabled_detectors:
|
|
|
546
606
|
- pluck_to_array
|
|
547
607
|
- delegation_n_plus_one
|
|
548
608
|
- decorator_n_plus_one
|
|
609
|
+
- scope_chain_n_plus_one
|
|
610
|
+
- validation_n_plus_one
|
|
549
611
|
|
|
550
612
|
# Severity levels per detector (error, warning, info)
|
|
551
613
|
severity_levels:
|
|
552
|
-
loop_association: error
|
|
614
|
+
loop_association: error # Definite N+1
|
|
553
615
|
serializer_nesting: warning
|
|
554
616
|
custom_method_query: warning
|
|
555
617
|
count_in_iteration: warning
|
|
556
618
|
callback_query: warning
|
|
557
|
-
pluck_to_array: warning
|
|
558
|
-
delegation_n_plus_one: warning
|
|
559
|
-
decorator_n_plus_one: warning
|
|
560
|
-
|
|
619
|
+
pluck_to_array: warning # Optimization
|
|
620
|
+
delegation_n_plus_one: warning # Hidden delegation N+1
|
|
621
|
+
decorator_n_plus_one: warning # Decorator/Presenter N+1
|
|
622
|
+
scope_chain_n_plus_one: warning # Scope chain on association
|
|
623
|
+
validation_n_plus_one: warning # Uniqueness validation in batch
|
|
624
|
+
missing_counter_cache: info # Suggestion
|
|
561
625
|
|
|
562
626
|
# Minimum severity to report (default: info)
|
|
563
627
|
min_severity: warning
|
data/lib/eager_eye/analyzer.rb
CHANGED
|
@@ -13,16 +13,20 @@ module EagerEye
|
|
|
13
13
|
callback_query: Detectors::CallbackQuery,
|
|
14
14
|
pluck_to_array: Detectors::PluckToArray,
|
|
15
15
|
delegation_n_plus_one: Detectors::DelegationNPlusOne,
|
|
16
|
-
decorator_n_plus_one: Detectors::DecoratorNPlusOne
|
|
16
|
+
decorator_n_plus_one: Detectors::DecoratorNPlusOne,
|
|
17
|
+
scope_chain_n_plus_one: Detectors::ScopeChainNPlusOne,
|
|
18
|
+
validation_n_plus_one: Detectors::ValidationNPlusOne
|
|
17
19
|
}.freeze
|
|
18
20
|
|
|
19
|
-
attr_reader :paths, :issues, :association_preloads, :delegation_maps
|
|
21
|
+
attr_reader :paths, :issues, :association_preloads, :delegation_maps, :scope_maps, :uniqueness_models
|
|
20
22
|
|
|
21
23
|
def initialize(paths: nil)
|
|
22
24
|
@paths = Array(paths || EagerEye.configuration.app_path)
|
|
23
25
|
@issues = []
|
|
24
26
|
@association_preloads = {}
|
|
25
27
|
@delegation_maps = {}
|
|
28
|
+
@scope_maps = {}
|
|
29
|
+
@uniqueness_models = Set.new
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
def run
|
|
@@ -48,6 +52,14 @@ module EagerEye
|
|
|
48
52
|
deleg_parser = DelegationParser.new
|
|
49
53
|
deleg_parser.parse_model(ast, model_name)
|
|
50
54
|
@delegation_maps.merge!(deleg_parser.delegation_maps)
|
|
55
|
+
|
|
56
|
+
scope_parser = ScopeParser.new
|
|
57
|
+
scope_parser.parse_model(ast, model_name)
|
|
58
|
+
@scope_maps.merge!(scope_parser.scope_maps)
|
|
59
|
+
|
|
60
|
+
validation_parser = ValidationParser.new
|
|
61
|
+
validation_parser.parse_model(ast, model_name)
|
|
62
|
+
@uniqueness_models.merge(validation_parser.uniqueness_models)
|
|
51
63
|
rescue Errno::ENOENT, Errno::EACCES
|
|
52
64
|
next
|
|
53
65
|
end
|
|
@@ -108,6 +120,8 @@ module EagerEye
|
|
|
108
120
|
args = [ast, file_path]
|
|
109
121
|
args << @association_preloads if detector.is_a?(Detectors::LoopAssociation)
|
|
110
122
|
args << @delegation_maps if detector.is_a?(Detectors::DelegationNPlusOne)
|
|
123
|
+
args << @scope_maps if detector.is_a?(Detectors::ScopeChainNPlusOne)
|
|
124
|
+
args << @uniqueness_models if detector.is_a?(Detectors::ValidationNPlusOne)
|
|
111
125
|
args
|
|
112
126
|
end
|
|
113
127
|
|
|
@@ -9,6 +9,7 @@ module EagerEye
|
|
|
9
9
|
loop_association serializer_nesting missing_counter_cache
|
|
10
10
|
custom_method_query count_in_iteration callback_query
|
|
11
11
|
pluck_to_array delegation_n_plus_one decorator_n_plus_one
|
|
12
|
+
scope_chain_n_plus_one validation_n_plus_one
|
|
12
13
|
].freeze
|
|
13
14
|
|
|
14
15
|
DEFAULT_SEVERITY_LEVELS = {
|
|
@@ -20,7 +21,9 @@ module EagerEye
|
|
|
20
21
|
callback_query: :warning,
|
|
21
22
|
pluck_to_array: :warning,
|
|
22
23
|
delegation_n_plus_one: :warning,
|
|
23
|
-
decorator_n_plus_one: :warning
|
|
24
|
+
decorator_n_plus_one: :warning,
|
|
25
|
+
scope_chain_n_plus_one: :warning,
|
|
26
|
+
validation_n_plus_one: :warning
|
|
24
27
|
}.freeze
|
|
25
28
|
|
|
26
29
|
VALID_SEVERITIES = %i[info warning error].freeze
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EagerEye
|
|
4
|
+
module Detectors
|
|
5
|
+
class ScopeChainNPlusOne < Base
|
|
6
|
+
ITERATION_METHODS = %i[each map select find_all reject collect detect find_index flat_map
|
|
7
|
+
find_each find_in_batches in_batches].freeze
|
|
8
|
+
|
|
9
|
+
def self.detector_name
|
|
10
|
+
:scope_chain_n_plus_one
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def detect(ast, file_path, scope_maps = {})
|
|
14
|
+
return [] unless ast
|
|
15
|
+
|
|
16
|
+
@issues = []
|
|
17
|
+
@file_path = file_path
|
|
18
|
+
@all_scopes = scope_maps.each_value.reduce(Set.new, :merge)
|
|
19
|
+
return [] if @all_scopes.empty?
|
|
20
|
+
|
|
21
|
+
find_iteration_blocks(ast)
|
|
22
|
+
@issues
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def find_iteration_blocks(node)
|
|
28
|
+
return unless node.is_a?(Parser::AST::Node)
|
|
29
|
+
|
|
30
|
+
if iteration_block?(node)
|
|
31
|
+
block_var = extract_block_variable(node)
|
|
32
|
+
check_block(node.children[2], block_var) if block_var
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
node.children.each { |child| find_iteration_blocks(child) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def iteration_block?(node)
|
|
39
|
+
node.type == :block && node.children[0]&.type == :send &&
|
|
40
|
+
ITERATION_METHODS.include?(node.children[0].children[1])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def check_block(node, block_var)
|
|
44
|
+
return unless node.is_a?(Parser::AST::Node)
|
|
45
|
+
|
|
46
|
+
add_issue(node) if scope_chain_on_association?(node, block_var)
|
|
47
|
+
node.children.each { |child| check_block(child, block_var) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def scope_chain_on_association?(node, block_var)
|
|
51
|
+
return false unless node.type == :send
|
|
52
|
+
|
|
53
|
+
method_name = node.children[1]
|
|
54
|
+
return false unless @all_scopes.include?(method_name)
|
|
55
|
+
|
|
56
|
+
receiver = node.children[0]
|
|
57
|
+
receiver_chain_starts_with?(receiver, block_var) && chain_depth(receiver, block_var) >= 1
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def chain_depth(node, block_var)
|
|
61
|
+
return 0 unless node.is_a?(Parser::AST::Node)
|
|
62
|
+
return 0 if node.type == :lvar && node.children[0] == block_var
|
|
63
|
+
|
|
64
|
+
node.type == :send ? 1 + chain_depth(node.children[0], block_var) : 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def add_issue(node)
|
|
68
|
+
chain = reconstruct_chain(node.children[0])
|
|
69
|
+
@issues << create_issue(
|
|
70
|
+
file_path: @file_path,
|
|
71
|
+
line_number: node.loc.line,
|
|
72
|
+
message: "Scope `.#{node.children[1]}` called on `#{chain}` inside iteration",
|
|
73
|
+
suggestion: "Each scope call executes a new query. Consider preloading or using a joined query."
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EagerEye
|
|
4
|
+
class ScopeParser
|
|
5
|
+
attr_reader :scope_maps
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@scope_maps = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def parse_model(ast, model_name)
|
|
12
|
+
return unless ast
|
|
13
|
+
|
|
14
|
+
traverse(ast, model_name)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def traverse(node, model_name)
|
|
20
|
+
return unless node.is_a?(Parser::AST::Node)
|
|
21
|
+
|
|
22
|
+
check_scope(node, model_name)
|
|
23
|
+
node.children.each { |child| traverse(child, model_name) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def check_scope(node, model_name)
|
|
27
|
+
return unless scope_call?(node)
|
|
28
|
+
|
|
29
|
+
scope_name = extract_scope_name(node)
|
|
30
|
+
return unless scope_name
|
|
31
|
+
|
|
32
|
+
@scope_maps[model_name] ||= Set.new
|
|
33
|
+
@scope_maps[model_name] << scope_name
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def scope_call?(node)
|
|
37
|
+
node.type == :send && node.children[0].nil? && node.children[1] == :scope
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def extract_scope_name(node)
|
|
41
|
+
first_arg = node.children[2]
|
|
42
|
+
first_arg&.type == :sym ? first_arg.children[0] : nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/eager_eye/version.rb
CHANGED
data/lib/eager_eye.rb
CHANGED
|
@@ -6,6 +6,8 @@ require_relative "eager_eye/configuration"
|
|
|
6
6
|
require_relative "eager_eye/issue"
|
|
7
7
|
require_relative "eager_eye/association_parser"
|
|
8
8
|
require_relative "eager_eye/delegation_parser"
|
|
9
|
+
require_relative "eager_eye/scope_parser"
|
|
10
|
+
require_relative "eager_eye/validation_parser"
|
|
9
11
|
require_relative "eager_eye/detectors/base"
|
|
10
12
|
require_relative "eager_eye/detectors/loop_association"
|
|
11
13
|
require_relative "eager_eye/detectors/serializer_nesting"
|
|
@@ -16,6 +18,8 @@ require_relative "eager_eye/detectors/callback_query"
|
|
|
16
18
|
require_relative "eager_eye/detectors/pluck_to_array"
|
|
17
19
|
require_relative "eager_eye/detectors/delegation_n_plus_one"
|
|
18
20
|
require_relative "eager_eye/detectors/decorator_n_plus_one"
|
|
21
|
+
require_relative "eager_eye/detectors/scope_chain_n_plus_one"
|
|
22
|
+
require_relative "eager_eye/detectors/validation_n_plus_one"
|
|
19
23
|
require_relative "eager_eye/comment_parser"
|
|
20
24
|
require_relative "eager_eye/analyzer"
|
|
21
25
|
require_relative "eager_eye/fixers/base"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: eager_eye
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- hamzagedikkaya
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ast
|
|
@@ -76,6 +76,7 @@ files:
|
|
|
76
76
|
- lib/eager_eye/detectors/loop_association.rb
|
|
77
77
|
- lib/eager_eye/detectors/missing_counter_cache.rb
|
|
78
78
|
- lib/eager_eye/detectors/pluck_to_array.rb
|
|
79
|
+
- lib/eager_eye/detectors/scope_chain_n_plus_one.rb
|
|
79
80
|
- lib/eager_eye/detectors/serializer_nesting.rb
|
|
80
81
|
- lib/eager_eye/fixer_registry.rb
|
|
81
82
|
- lib/eager_eye/fixers/base.rb
|
|
@@ -88,6 +89,7 @@ files:
|
|
|
88
89
|
- lib/eager_eye/reporters/json.rb
|
|
89
90
|
- lib/eager_eye/rspec.rb
|
|
90
91
|
- lib/eager_eye/rspec/matchers.rb
|
|
92
|
+
- lib/eager_eye/scope_parser.rb
|
|
91
93
|
- lib/eager_eye/version.rb
|
|
92
94
|
- sig/eager_eye.rbs
|
|
93
95
|
homepage: https://github.com/hamzagedikkaya/eager_eye
|