partial_inspector 0.1.1 → 2.0.0

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: 477f232814bfb32f86217691b6f57d7cac11d35c968c615043c666c2d5bda1d9
4
- data.tar.gz: 86db2c1e38e85b85ac2fb8e9116610d5807826bb98a6dee97c5f69363e144329
3
+ metadata.gz: b01271288a1127081cd52384cec35879907aa6bf778d9c16c30760e37d5df700
4
+ data.tar.gz: 503dea0d0f53e11ddcab09d73f6eda69e6cb65dd52bbcbd9007c3d24e6ac016c
5
5
  SHA512:
6
- metadata.gz: 18f5c0bdf3eb999843868a165abfc12aa9d5775a6922c0171237f7063a43248ecea45c3dd7788cfdf7df11a9d1c46fce4ab832768973b5391126500b5a9e420a
7
- data.tar.gz: 0bad6dc2b8d120877eacd910dc53f9ce7eeb1addbe23ba6348dbef7a2e379c0a0b01d019e9127c8ace1c2d6173702d00f528cc4db9c3a21cc0d73251ce017abb
6
+ metadata.gz: c9ac5c9677eb732664821aa681e1fae7d6a7e35817371fd3f6b4a69e585e26fd5feaf4ecc9376c5f66211399c988c38379016fb571bdec1a84b2cd6404ae771f
7
+ data.tar.gz: d48cd66554591f92771c0fb9f1bdb26ec808ff66fc2ff6b20f17ae341c2cded972e2f6262017779ff9f6c8321b66f01bc5fadf4783300a73bcd515033b9ef532
data/README.md CHANGED
@@ -1 +1,43 @@
1
1
  # Partial Inspector
2
+ A tiny, developer-focused gem to help you find where your Rails partials are used.
3
+
4
+ When working on large Rails projects, it's easy to lose track of where a partial is rendered throughout the codebase. **partial_inspector** scans your project and shows exactly which files render a given partial, making it much easier to refactor, test or remove unused partials confidently.
5
+
6
+ ## Installation
7
+
8
+ ```
9
+ gem install partial_inspector
10
+ ```
11
+
12
+
13
+ ## How it works?
14
+ 1. Open irb
15
+ 2. Require the gem:
16
+
17
+ ```
18
+ require "partial_inspector"
19
+ ```
20
+
21
+ ### Find where a partial is used
22
+ 1. Run the scanner:
23
+
24
+ ```
25
+ PartialInspector.scanner.inspect_files_rendering_partial('path/to/partial')
26
+ ```
27
+ 2. Example Output
28
+
29
+ <img width="803" alt="image" src="https://github.com/user-attachments/assets/9008c2c1-d6ea-4497-945b-1e47823208fc" />
30
+
31
+ ### Find unused partials
32
+ 1. Run the scanner:
33
+
34
+ ```
35
+ PartialInspector.scanner.scan_unused_partials
36
+ ```
37
+ 2. Example Output
38
+
39
+ <img width="606" height="263" alt="image" src="https://github.com/user-attachments/assets/f7aef983-be65-4167-9703-810f29631a95" />
40
+
41
+
42
+
43
+
@@ -0,0 +1,70 @@
1
+ require_relative 'utils'
2
+
3
+ module PartialInspector
4
+ module PartialTree
5
+ include PartialInspector::Utils
6
+
7
+ private
8
+ def data_to_build_tree(partial_path)
9
+ router(partial_path)
10
+ end
11
+
12
+ def router(path)
13
+ base_data = []
14
+ partial_path = path.split('/')[0]=='app' ? path.split('/')[2..-1].join('/') : path
15
+ files = find_base_files_rendering_partial(partial_path)
16
+ return [[path]] if files.empty?
17
+ files.each do |file|
18
+ if rendered_by_partial(file)
19
+ child_traces = router(file)
20
+ child_traces.each do |trace|
21
+ base_data << [path] + trace
22
+ end
23
+ else
24
+ base_data << [path, file]
25
+ end
26
+ end
27
+ base_data
28
+ end
29
+
30
+ def find_base_files_rendering_partial(partial_path)
31
+ return [] if partial_path.nil? || partial_path == ''
32
+ base_files = []
33
+
34
+ files = Dir.glob("app/**/*.{rb,html.erb,js.erb,turbo_stream.erb}")
35
+ files.each do |file|
36
+ file_content = File.readlines(file)
37
+ file_content.each_with_index do |line, index|
38
+ if (line.include?('render') || line.include?('partial:')) && check_partial_exists(line, partial_path)
39
+ base_files << file
40
+ end
41
+ end
42
+ end
43
+
44
+ partial_dir = build_partial_base_dir_path(partial_path)
45
+ partial_name = extract_partial_name(partial_path)
46
+
47
+ base_files + find_base_files_rendering_partial_against_partial_name(partial_dir, partial_name)
48
+ end
49
+
50
+
51
+ def find_base_files_rendering_partial_against_partial_name(partial_dir, partial_name)
52
+ base_files = []
53
+
54
+ Dir.glob("app/views/#{partial_dir}*.*.erb").each do |file|
55
+ file_content = File.readlines(file)
56
+ file_content.each_with_index do |line, index|
57
+ if line.include?('render') && check_partial_exists(line, partial_name)
58
+ base_files << file
59
+ end
60
+ end
61
+ end
62
+
63
+ base_files
64
+ end
65
+
66
+ def rendered_by_partial(file)
67
+ file.split('/').last[0] == '_'
68
+ end
69
+ end
70
+ end
@@ -1,8 +1,12 @@
1
- require_relative "utils"
1
+ require_relative "used_partials"
2
+ require_relative "unused_partials"
3
+ require_relative "partial_tree"
2
4
 
3
5
  module PartialInspector
4
6
  class Reporter
5
- include PartialInspector::Utils
7
+ include PartialInspector::UsedPartials
8
+ include PartialInspector::UnusedPartials
9
+ include PartialInspector::PartialTree
6
10
 
7
11
  def report_files_rendering_partial(partial_path)
8
12
  files = base_scanner(partial_path)
@@ -27,24 +31,37 @@ module PartialInspector
27
31
  return
28
32
  end
29
33
 
30
- private
31
- def combine_unique_files(files)
32
- results = {}
33
- files.each do |file|
34
- same_files = files.filter { |f| f[:file] == file[:file] }
34
+ def report_unused_partials
35
+ partials = inspect_unused_partials
36
+ unused_partials = partials[:unused_partials]
35
37
 
36
- same_files.each do |same_file|
37
- files.delete(same_file)
38
- results[same_file[:file].to_sym] = [] if results[same_file[:file].to_sym] == nil
39
- results[same_file[:file].to_sym] << { line_number: same_file[:line_number], line_content: file[:line_content] }
38
+ puts "\n\e[36mSEARCH SUMMARY\e[0m"
39
+ puts "\e[35mTOTAL PARTIALS\e[0m: \e[32m#{partials[:total_partials]}\e[0m"
40
+ puts "\e[35mUSED PARTIALS\e[0m: \e[32m#{partials[:used_partials]}\e[0m"
41
+ puts "\e[35mUNUSED PARTIALS\e[0m: \e[32m#{unused_partials.size}\e[0m"
42
+
43
+ unless unused_partials.empty?
44
+ puts "\n\e[36mDETAILS\e[0m"
45
+ unused_partials.each do |partial|
46
+ puts partial
40
47
  end
41
48
  end
49
+ return
50
+ end
42
51
 
43
- files.each do |file|
44
- results[file[:file].to_sym] = [{ line_number: file[:line_number], line_content: file[:line_content] }]
52
+ def report_partial_tree(partial_path)
53
+ paths = data_to_build_tree(partial_path)
54
+ paths.each_with_index do |path, index|
55
+ puts "\e[35mTREE #{index+1}\e[0m"
56
+ space = ""
57
+ path[1..-1].each do |sub_path|
58
+ puts "#{space}-> #{sub_path}"
59
+ space = space + "\t"
60
+ end
61
+ space=""
62
+ puts "\n"
45
63
  end
46
-
47
- results
64
+ return
48
65
  end
49
66
  end
50
67
  end
@@ -8,6 +8,16 @@ module PartialInspector
8
8
  return
9
9
  end
10
10
 
11
+ def scan_unused_partials
12
+ puts reporter.report_unused_partials
13
+ return
14
+ end
15
+
16
+ def inspect_partial_tree(partial_path)
17
+ puts reporter.report_partial_tree(partial_path)
18
+ return
19
+ end
20
+
11
21
  private
12
22
  def reporter
13
23
  PartialInspector::Reporter.new
@@ -0,0 +1,73 @@
1
+ require_relative 'utils'
2
+
3
+ module PartialInspector
4
+ module UnusedPartials
5
+ include PartialInspector::Utils
6
+
7
+ private
8
+ def inspect_unused_partials
9
+ partials = Dir.glob("app/views/**/_*.*erb")
10
+ partial_paths_data = format_partial_paths_for_scanning(partials)
11
+ unused_partials = []
12
+ puts "\e[32mScanning...\e[0m\n"
13
+ partial_paths_data.each do |path_data|
14
+ unless full_path_used(path_data[:full_path]) || partial_name_used(path_data[:full_path])
15
+ unused_partials << path_data[:partial]
16
+ end
17
+ end
18
+
19
+ {
20
+ total_partials: partials.size,
21
+ unused_partials: unused_partials,
22
+ used_partials: partials.size - unused_partials.size
23
+ }
24
+ end
25
+
26
+ def format_partial_paths_for_scanning(partials)
27
+ partial_paths_for_rendering = []
28
+ partials.each do |partial|
29
+ partial_paths_for_rendering << extract_complete_partial_path(partial)
30
+ end
31
+
32
+ partial_paths_for_rendering
33
+ end
34
+
35
+ def extract_complete_partial_path(partial)
36
+ full_path = partial.split('app/views/')[1]
37
+ formatted_path = ""
38
+ path_components = full_path.split('/')
39
+ path_components[-1] = extract_partial_name(path_components[-1])
40
+ path_components.each do |path_component|
41
+ formatted_path += "#{path_component}/"
42
+ end
43
+ formatted_path = formatted_path[0..-2] if formatted_path[-1] == '/'
44
+
45
+ { partial: partial, full_path: formatted_path, partial_name: path_components[-1] }
46
+ end
47
+
48
+ def full_path_used(partial_path)
49
+ files = Dir.glob("app/**/*.{rb,html.erb,js.erb,turbo_stream.erb}")
50
+ files.each do |file|
51
+ file_content = File.readlines(file)
52
+ file_content.each do |line|
53
+ return true if line.include?("'#{partial_path}'") || line.include?("\"#{partial_path}\"")
54
+ end
55
+ end
56
+ return false
57
+ end
58
+
59
+ def partial_name_used(partial_path)
60
+ partial_dir = build_partial_base_dir_path(partial_path)
61
+ partial_name = extract_partial_name(partial_path)
62
+
63
+ files = Dir.glob("app/views/#{partial_dir}*.*.erb")
64
+ files.each do |file|
65
+ file_content = File.readlines(file)
66
+ file_content.each do |line|
67
+ return true if line.include?("'#{partial_name}'") || line.include?("\"#{partial_name}\"")
68
+ end
69
+ end
70
+ return false
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,99 @@
1
+ require_relative 'utils'
2
+
3
+ module PartialInspector
4
+ module UsedPartials
5
+ include PartialInspector::Utils
6
+
7
+ private
8
+ def base_scanner(partial_path)
9
+ return [] if partial_path.nil? || partial_path == ''
10
+ lines = []
11
+
12
+ files = Dir.glob("app/**/*.{rb,html.erb,js.erb,turbo_stream.erb}")
13
+ files.each do |file|
14
+ file_content = File.readlines(file)
15
+ file_content.each_with_index do |line, index|
16
+ if (line.include?('render') || line.include?('partial:')) && check_partial_exists(line, partial_path)
17
+ lines << {
18
+ file: file,
19
+ line_number: index + 1,
20
+ line_content: highlight_partial_form_content(partial_path, line.strip)
21
+ }
22
+ end
23
+ end
24
+ end
25
+
26
+ partial_dir = build_partial_base_dir_path(partial_path)
27
+ partial_name = extract_partial_name(partial_path)
28
+
29
+ lines + check_partial_against_name(partial_dir, partial_name)
30
+ end
31
+
32
+ def build_path(path_components)
33
+ path = ''
34
+ path_components.each do |path_component|
35
+ path = path+"#{path_component}/"
36
+ end
37
+
38
+ path[0..-2]
39
+ end
40
+
41
+ def check_partial_against_name(partial_dir, partial_name)
42
+ lines = []
43
+
44
+ Dir.glob("app/views/#{partial_dir}*.*.erb").each do |file|
45
+ file_content = File.readlines(file)
46
+ file_content.each_with_index do |line, index|
47
+ if line.include?('render') && check_partial_exists(line, partial_name)
48
+ lines << {
49
+ file: file,
50
+ line_number: index + 1,
51
+ line_content: highlight_partial_form_content(partial_name, line.strip)
52
+ }
53
+ end
54
+ end
55
+ end
56
+
57
+ lines
58
+ end
59
+
60
+ def highlight_partial_form_content(partial_path, content)
61
+ if content.include?("'#{partial_path}'")
62
+ content_components = content.split("'#{partial_path}'")
63
+ result = if content_components.size < 2
64
+ content_components[0]+"\e[44m\e[32m'#{partial_path}'\e[0m\e[0m"
65
+ else
66
+ content_components[0]+"\e[44m\e[32m'#{partial_path}'\e[0m\e[0m"+content_components[1]
67
+ end
68
+ return result
69
+ elsif content.include?("\"#{partial_path}\"")
70
+ content_components = content.split("\"#{partial_path}\"")
71
+ result = if content_components.size < 2
72
+ content_components[0]+"\e[44m\e[32m\"#{partial_path}\"\e[0m\e[0m"
73
+ else
74
+ content_components[0]+"\e[44m\e[32m\"#{partial_path}\"\e[0m\e[0m"+content_components[1]
75
+ end
76
+ return result
77
+ end
78
+ end
79
+
80
+ def combine_unique_files(files)
81
+ results = {}
82
+ files.each do |file|
83
+ same_files = files.filter { |f| f[:file] == file[:file] }
84
+
85
+ same_files.each do |same_file|
86
+ files.delete(same_file)
87
+ results[same_file[:file].to_sym] = [] if results[same_file[:file].to_sym] == nil
88
+ results[same_file[:file].to_sym] << { line_number: same_file[:line_number], line_content: file[:line_content] }
89
+ end
90
+ end
91
+
92
+ files.each do |file|
93
+ results[file[:file].to_sym] = [{ line_number: file[:line_number], line_content: file[:line_content] }]
94
+ end
95
+
96
+ results
97
+ end
98
+ end
99
+ end
@@ -1,30 +1,7 @@
1
1
  module PartialInspector
2
2
  module Utils
3
- private
4
- def base_scanner(partial_path)
5
- return [] if partial_path.nil? || partial_path == ''
6
- lines = []
7
-
8
- files = Dir.glob("app/**/*.{rb,html.erb,js.erb,turbo_stream.erb}")
9
- files.each do |file|
10
- file_content = File.readlines(file)
11
- file_content.each_with_index do |line, index|
12
- if (line.include?('render') || line.include?('partial:')) && check_partial_exists(line, partial_path)
13
- lines << {
14
- file: file,
15
- line_number: index + 1,
16
- line_content: highlight_partial_form_content(partial_path, line.strip)
17
- }
18
- end
19
- end
20
- end
21
-
22
- partial_dir = build_partial_base_dir_path(partial_path)
23
- partial_name = extract_partial_name(partial_path)
24
-
25
- lines + check_partial_against_name(partial_dir, partial_name)
26
- end
27
3
 
4
+ private
28
5
  def extract_partial_file_name(partial_path)
29
6
  file_name = partial_path.split('/').last
30
7
  file_name
@@ -37,6 +14,17 @@ module PartialInspector
37
14
  partial_name
38
15
  end
39
16
 
17
+ def build_partial_base_dir_path(partial_path)
18
+ path_components = partial_path.split('/')
19
+ path = ''
20
+ path_components = path_components[0..-2]
21
+ path_components.each do |path_component|
22
+ path = path + "#{path_component}/"
23
+ end
24
+
25
+ path
26
+ end
27
+
40
28
  def check_partial_exists(line, partial)
41
29
  partial_path = ""
42
30
  line_content = line.split(' ')
@@ -62,64 +50,5 @@ module PartialInspector
62
50
 
63
51
  partial_path == partial
64
52
  end
65
-
66
- def build_path(path_components)
67
- path = ''
68
- path_components.each do |path_component|
69
- path = path+"#{path_component}/"
70
- end
71
-
72
- path[0..-2]
73
- end
74
-
75
- def build_partial_base_dir_path(partial_path)
76
- path_components = partial_path.split('/')
77
- path = ''
78
- path_components = path_components[0..-2]
79
- path_components.each do |path_component|
80
- path = path + "#{path_component}/"
81
- end
82
-
83
- path
84
- end
85
-
86
- def check_partial_against_name(partial_dir, partial_name)
87
- lines = []
88
-
89
- Dir.glob("app/views/#{partial_dir}*.*.erb").each do |file|
90
- file_content = File.readlines(file)
91
- file_content.each_with_index do |line, index|
92
- if line.include?('render') && check_partial_exists(line, partial_name)
93
- lines << {
94
- file: file,
95
- line_number: index + 1,
96
- line_content: highlight_partial_form_content(partial_name, line.strip)
97
- }
98
- end
99
- end
100
- end
101
-
102
- lines
103
- end
104
-
105
- def highlight_partial_form_content(partial_path, content)
106
- if content.include?("'#{partial_path}'")
107
- content_components = content.split("'#{partial_path}'")
108
- result = if content_components.size < 2
109
- content_components[0]+"\e[44m\e[32m'#{partial_path}'\e[0m\e[0m"
110
- else
111
- content_components[0]+"\e[44m\e[32m'#{partial_path}'\e[0m\e[0m"+content_components[1]
112
- end
113
- return result
114
- elsif content.include?("\"#{partial_path}\"")
115
- content_components = content.split("\"#{partial_path}\"")
116
- result = if content_components.size < 2
117
- content_components[0]+"\e[44m\e[32m\"#{partial_path}\"\e[0m\e[0m"
118
- else
119
- content_components[0]+"\e[44m\e[32m\"#{partial_path}\"\e[0m\e[0m"+content_components[1]
120
- end
121
- return result
122
- end
123
- end
124
53
  end
125
54
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PartialInspector
4
- VERSION = "0.1.1"
4
+ VERSION = "2.0.0"
5
5
  end
@@ -5,7 +5,6 @@ require_relative "partial_inspector/scanner"
5
5
 
6
6
  module PartialInspector
7
7
  class Error < StandardError; end
8
- # Your code goes here...
9
8
 
10
9
  def self.scanner()
11
10
  PartialInspector::Scanner.new
metadata CHANGED
@@ -1,16 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: partial_inspector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SalihaShahid
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-07-06 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description:
14
12
  email:
15
13
  - salihashahid1102@gmail.com
16
14
  executables: []
@@ -20,8 +18,11 @@ files:
20
18
  - README.md
21
19
  - Rakefile
22
20
  - lib/partial_inspector.rb
21
+ - lib/partial_inspector/partial_tree.rb
23
22
  - lib/partial_inspector/reporter.rb
24
23
  - lib/partial_inspector/scanner.rb
24
+ - lib/partial_inspector/unused_partials.rb
25
+ - lib/partial_inspector/used_partials.rb
25
26
  - lib/partial_inspector/utils.rb
26
27
  - lib/partial_inspector/version.rb
27
28
  - sig/partial_inspector.rbs
@@ -29,7 +30,6 @@ homepage: https://github.com/SalihaShahid/partial_inspector
29
30
  licenses:
30
31
  - MIT
31
32
  metadata: {}
32
- post_install_message:
33
33
  rdoc_options: []
34
34
  require_paths:
35
35
  - lib
@@ -44,8 +44,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
46
  requirements: []
47
- rubygems_version: 3.2.3
48
- signing_key:
47
+ rubygems_version: 3.6.7
49
48
  specification_version: 4
50
49
  summary: Partial Inspector
51
50
  test_files: []