partial_inspector 0.1.1 → 1.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: 4da9f1dbf7e24e31a2ff0861e6a953077a40c3b8ddf8c32943b25b0dea006bac
4
+ data.tar.gz: be3e444cc43d7b41bb33806c426adeff4f155dce8d38273eac0a8241b8f52f84
5
5
  SHA512:
6
- metadata.gz: 18f5c0bdf3eb999843868a165abfc12aa9d5775a6922c0171237f7063a43248ecea45c3dd7788cfdf7df11a9d1c46fce4ab832768973b5391126500b5a9e420a
7
- data.tar.gz: 0bad6dc2b8d120877eacd910dc53f9ce7eeb1addbe23ba6348dbef7a2e379c0a0b01d019e9127c8ace1c2d6173702d00f528cc4db9c3a21cc0d73251ce017abb
6
+ metadata.gz: e6fea5f0e342ae64204796a40d9e210b3fbc9d61634d837be9f3adc0065d29dbb6c7f3ba70ebc175da071e321f80d453381ab2f17dfe73beec19e40e2e36e634
7
+ data.tar.gz: aab718cd76499900275cf18c41eba1ee46d6b5bf4d47648998d6b5bd73606265888bc22a8113805beedf32a2f7fc1dee58793422ea37f06d4256125c7e1a7584
data/README.md CHANGED
@@ -1 +1,39 @@
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
+
38
+
39
+
@@ -1,8 +1,10 @@
1
- require_relative "utils"
1
+ require_relative "used_partials"
2
+ require_relative "unused_partials"
2
3
 
3
4
  module PartialInspector
4
5
  class Reporter
5
- include PartialInspector::Utils
6
+ include PartialInspector::UsedPartials
7
+ include PartialInspector::UnusedPartials
6
8
 
7
9
  def report_files_rendering_partial(partial_path)
8
10
  files = base_scanner(partial_path)
@@ -27,24 +29,22 @@ module PartialInspector
27
29
  return
28
30
  end
29
31
 
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] }
32
+ def report_unused_partials
33
+ partials = inspect_unused_partials
34
+ unused_partials = partials[:unused_partials]
35
35
 
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] }
36
+ puts "\n\e[36mSEARCH SUMMARY\e[0m"
37
+ puts "\e[35mTOTAL PARTIALS\e[0m: \e[32m#{partials[:total_partials]}\e[0m"
38
+ puts "\e[35mUSED PARTIALS\e[0m: \e[32m#{partials[:used_partials]}\e[0m"
39
+ puts "\e[35mUNUSED PARTIALS\e[0m: \e[32m#{unused_partials.size}\e[0m"
40
+
41
+ unless unused_partials.empty?
42
+ puts "\n\e[36mDETAILS\e[0m"
43
+ unused_partials.each do |partial|
44
+ puts partial
40
45
  end
41
46
  end
42
-
43
- files.each do |file|
44
- results[file[:file].to_sym] = [{ line_number: file[:line_number], line_content: file[:line_content] }]
45
- end
46
-
47
- results
47
+ return
48
48
  end
49
49
  end
50
50
  end
@@ -8,6 +8,11 @@ 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
+
11
16
  private
12
17
  def reporter
13
18
  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,125 @@
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 check_partial_exists(line, partial)
33
+ partial_path = ""
34
+ line_content = line.split(' ')
35
+ line_content.each_with_index do |value, index|
36
+ if line.include?("render partial:") && value == "render"
37
+ partial_path = line_content[index + 2]
38
+ break
39
+ elsif value == "render"
40
+ partial_path = line_content[index + 1]
41
+ break
42
+ elsif value == "partial:"
43
+ partial_path = line_content[index + 1]
44
+ break
45
+ end
46
+ end
47
+ if partial_path[0] == "\""
48
+ partial_path = partial_path[1..-2]
49
+ partial_path = partial_path[0..-2] if partial_path[-1] == '"'
50
+ elsif partial_path[0] == "'"
51
+ partial_path = partial_path[1..-2]
52
+ partial_path = partial_path[0..-2] if partial_path[-1] == "'"
53
+ end
54
+
55
+ partial_path == partial
56
+ end
57
+
58
+ def build_path(path_components)
59
+ path = ''
60
+ path_components.each do |path_component|
61
+ path = path+"#{path_component}/"
62
+ end
63
+
64
+ path[0..-2]
65
+ end
66
+
67
+ def check_partial_against_name(partial_dir, partial_name)
68
+ lines = []
69
+
70
+ Dir.glob("app/views/#{partial_dir}*.*.erb").each do |file|
71
+ file_content = File.readlines(file)
72
+ file_content.each_with_index do |line, index|
73
+ if line.include?('render') && check_partial_exists(line, partial_name)
74
+ lines << {
75
+ file: file,
76
+ line_number: index + 1,
77
+ line_content: highlight_partial_form_content(partial_name, line.strip)
78
+ }
79
+ end
80
+ end
81
+ end
82
+
83
+ lines
84
+ end
85
+
86
+ def highlight_partial_form_content(partial_path, content)
87
+ if content.include?("'#{partial_path}'")
88
+ content_components = content.split("'#{partial_path}'")
89
+ result = if content_components.size < 2
90
+ content_components[0]+"\e[44m\e[32m'#{partial_path}'\e[0m\e[0m"
91
+ else
92
+ content_components[0]+"\e[44m\e[32m'#{partial_path}'\e[0m\e[0m"+content_components[1]
93
+ end
94
+ return result
95
+ elsif content.include?("\"#{partial_path}\"")
96
+ content_components = content.split("\"#{partial_path}\"")
97
+ result = if content_components.size < 2
98
+ content_components[0]+"\e[44m\e[32m\"#{partial_path}\"\e[0m\e[0m"
99
+ else
100
+ content_components[0]+"\e[44m\e[32m\"#{partial_path}\"\e[0m\e[0m"+content_components[1]
101
+ end
102
+ return result
103
+ end
104
+ end
105
+
106
+ def combine_unique_files(files)
107
+ results = {}
108
+ files.each do |file|
109
+ same_files = files.filter { |f| f[:file] == file[:file] }
110
+
111
+ same_files.each do |same_file|
112
+ files.delete(same_file)
113
+ results[same_file[:file].to_sym] = [] if results[same_file[:file].to_sym] == nil
114
+ results[same_file[:file].to_sym] << { line_number: same_file[:line_number], line_content: file[:line_content] }
115
+ end
116
+ end
117
+
118
+ files.each do |file|
119
+ results[file[:file].to_sym] = [{ line_number: file[:line_number], line_content: file[:line_content] }]
120
+ end
121
+
122
+ results
123
+ end
124
+ end
125
+ 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,41 +14,6 @@ module PartialInspector
37
14
  partial_name
38
15
  end
39
16
 
40
- def check_partial_exists(line, partial)
41
- partial_path = ""
42
- line_content = line.split(' ')
43
- line_content.each_with_index do |value, index|
44
- if line.include?("render partial:") && value == "render"
45
- partial_path = line_content[index + 2]
46
- break
47
- elsif value == "render"
48
- partial_path = line_content[index + 1]
49
- break
50
- elsif value == "partial:"
51
- partial_path = line_content[index + 1]
52
- break
53
- end
54
- end
55
- if partial_path[0] == "\""
56
- partial_path = partial_path[1..-2]
57
- partial_path = partial_path[0..-2] if partial_path[-1] == '"'
58
- elsif partial_path[0] == "'"
59
- partial_path = partial_path[1..-2]
60
- partial_path = partial_path[0..-2] if partial_path[-1] == "'"
61
- end
62
-
63
- partial_path == partial
64
- 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
17
  def build_partial_base_dir_path(partial_path)
76
18
  path_components = partial_path.split('/')
77
19
  path = ''
@@ -82,44 +24,5 @@ module PartialInspector
82
24
 
83
25
  path
84
26
  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
27
  end
125
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PartialInspector
4
- VERSION = "0.1.1"
4
+ VERSION = "1.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: 1.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: []
@@ -22,6 +20,8 @@ files:
22
20
  - lib/partial_inspector.rb
23
21
  - lib/partial_inspector/reporter.rb
24
22
  - lib/partial_inspector/scanner.rb
23
+ - lib/partial_inspector/unused_partials.rb
24
+ - lib/partial_inspector/used_partials.rb
25
25
  - lib/partial_inspector/utils.rb
26
26
  - lib/partial_inspector/version.rb
27
27
  - sig/partial_inspector.rbs
@@ -29,7 +29,6 @@ homepage: https://github.com/SalihaShahid/partial_inspector
29
29
  licenses:
30
30
  - MIT
31
31
  metadata: {}
32
- post_install_message:
33
32
  rdoc_options: []
34
33
  require_paths:
35
34
  - lib
@@ -44,8 +43,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
44
43
  - !ruby/object:Gem::Version
45
44
  version: '0'
46
45
  requirements: []
47
- rubygems_version: 3.2.3
48
- signing_key:
46
+ rubygems_version: 3.6.7
49
47
  specification_version: 4
50
48
  summary: Partial Inspector
51
49
  test_files: []