ordit 0.1.2 → 0.1.3

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: fdefa2e20e2bd7fa34e8c898fc6e54e934a35e6c9dc3eb5e9ce9f22bf0403b87
4
- data.tar.gz: 704929dfdbee426f4d358512b18d217f2ddf1521f8e376cdb839993066b229a6
3
+ metadata.gz: d5ebf59e6ecec8bf7ff9c2e8f90923426694c8b9a0fb50ed0ac969e5e881fe5c
4
+ data.tar.gz: 6fdbb94c19ea9cfec2596571b04f6e58993af1dd83b5194023b2bcb435bce6b2
5
5
  SHA512:
6
- metadata.gz: 980a3d0f8ba5d373577b55594350962f784775de06f2c0b288271929a71a68998799fc60b1611293a68ce15102168923377d0dccde9699f2348775c5cf4dc180
7
- data.tar.gz: 3e83e18491bb40508a8f65a8210b29e1c31e94c4e87c67c64c033ed5a3682768c39cae36c794363d37a8d2bdb3f3a457e161a52001584e0b1eabd8349891a0cc
6
+ metadata.gz: e1815e1b1fe8e70814d121d485785b5dcc21a435e9d70cccb75156e914508cc2d7f8cc03c991f0dbe47136745e7f26e955b4384f8c4d74234598c4945485bcb1
7
+ data.tar.gz: b22613d4aaa8f7f405bcf3249ac24ab43c80be89847dd6d1709dd92d9cb840309e223ccfa31edf6075faee7e4d8d7c95bb62a2a69223e6a8f35097b37ba86862
data/CHANGELOG.md CHANGED
@@ -1,10 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.3] - 2024-16-12
4
+
5
+ - more refactor, clean up, make more logical
6
+
3
7
  ## [0.1.2] - 2024-12-12
4
8
 
5
9
  - refactor, clean up
6
10
 
7
-
8
11
  ## [0.1.1] - 2024-12-12
9
12
 
10
13
  - Fix namespace issue in rake task
data/README.md CHANGED
@@ -28,27 +28,21 @@ rails ordit:stimulus
28
28
  This will show:
29
29
  - Controllers that are defined but never used
30
30
  - Controllers that are used but don't have corresponding files
31
- - Active controllers and where they're being used
32
31
  - Summary statistics
33
32
 
34
33
  Example output:
35
34
  ```
36
35
  šŸ“Š Stimulus Controller Audit
37
36
 
38
- āŒ Defined but unused controllers:
39
- unused_feature
40
- └─ app/javascript/controllers/unused_feature_controller.js
37
+ āŒ Controllers not defined in any views:
38
+ unused-feature
39
+ users--edit-password
41
40
 
42
41
  āš ļø Used but undefined controllers:
43
- missing_controller
44
- └─ app/views/products/show.html.erb (lines: 15, 23)
45
-
46
- āœ… Active controllers:
47
- products
48
- └─ Defined in: app/javascript/controllers/products_controller.js
49
- └─ Used in:
50
- └─ app/views/products/index.html.erb (lines: 10, 45)
51
- └─ app/components/product_card/component.html.erb (lines: 3)
42
+ missing-controller
43
+ └─ šŸ“ app/views/products/show.html.erb
44
+ šŸ“ app/views/users/edit.html.erb
45
+
52
46
  ```
53
47
 
54
48
  ### Scan for Specific Controller Usage
@@ -56,13 +50,19 @@ Example output:
56
50
  Find all uses of a specific controller:
57
51
 
58
52
  ```bash
59
- rails ordit:scan[controller_name]
53
+ rails ordit:scan[toggle]
54
+ rails ordit:scan[users--name] # For namespaced controllers
60
55
  ```
61
56
 
62
- Example:
63
- ```bash
64
- rails ordit:scan[products]
65
- rails ordit:scan[users--name] # For namespaced controllers
57
+ Example output:
58
+
59
+ ```
60
+ Searching for stimulus controller: 'toggle'
61
+
62
+ šŸ“ app/views/admin/new.html.erb
63
+ šŸ“ app/views/posts/edit.html.erb
64
+ šŸ“ app/views/products/show.html.erb
65
+ šŸ“ app/views/users/edit.html.erb
66
66
  ```
67
67
 
68
68
  ### Configuration
@@ -12,8 +12,7 @@ module Ordit
12
12
  base_path = Ordit.root
13
13
 
14
14
  @view_paths = [
15
- base_path.join("app/views/**/*.{html,erb,haml}").to_s,
16
- base_path.join("app/javascript/**/*.{js,jsx}").to_s,
15
+ base_path.join("app/views/**/*.{html,erb,haml,slim}").to_s,
17
16
  base_path.join("app/components/**/*.{html,erb,haml,rb}").to_s
18
17
  ]
19
18
 
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Ordit
6
+ class ConsoleOutput
7
+ extend Forwardable
8
+
9
+ def self.run(name = nil)
10
+ new(name).run
11
+ end
12
+
13
+ def_delegators :@results,
14
+ :files, :active_controllers, :undefined_controllers, :unused_controllers
15
+
16
+ def initialize(name = nil)
17
+ @name = name
18
+ @results = Results.new(name)
19
+ end
20
+
21
+ def run
22
+ return print_find if @name
23
+
24
+ print_audit
25
+ end
26
+
27
+ def print_audit
28
+ puts "\nšŸ“Š Stimulus Controller Audit\n"
29
+
30
+ if unused_controllers.any?
31
+ puts "\nāŒ Controllers not defined in any views:"
32
+ unused_controllers.sort.each do |controller|
33
+ puts " #{controller}"
34
+ end
35
+ end
36
+
37
+ if undefined_controllers.any?
38
+ puts "\nāš ļø View with defined but missing controller:"
39
+ undefined_controllers.each do |controller, files|
40
+ puts "\n #{controller}_controller"
41
+
42
+ files.each_with_index do |file, index|
43
+ puts " #{ index.zero? ? '└─' : ' ' } šŸ“ #{file}"
44
+ end
45
+ end
46
+ end
47
+
48
+ puts "\nšŸ“ˆ Summary:"
49
+ puts " Total controllers defined: #{files.size}"
50
+ puts " Total controllers in use: #{active_controllers.size}"
51
+ puts " Unused controllers: #{unused_controllers.size}"
52
+ puts " Undefined controllers: #{undefined_controllers.size}"
53
+ end
54
+
55
+ def print_find
56
+ puts "\nSearching for stimulus controller: '#{@name}'\n\n"
57
+
58
+ if files.empty?
59
+ puts "No matches found."
60
+ return
61
+ end
62
+
63
+ files.each do |file|
64
+ puts "šŸ“ #{file}"
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ordit
4
+ class DefinitionMatcher
5
+ # Match data-controller in HTML attributes
6
+ HTML_PATTERN = /data-controller=["']([^"']+)["']/.freeze
7
+ # Match controller: in Ruby/ERB
8
+ RUBY_PATTERN = /controller:\s*['"]([^'"]+)['"]/.freeze
9
+ # Match 'controller' => in Ruby/ERB
10
+ HASH_ROCKET_PATTERN = /(?:['"]controller['"]|:controller)\s*=>\s*['"]([^'"]+)['"]/.freeze
11
+
12
+ ALL_REGEX = [HTML_PATTERN, RUBY_PATTERN, HASH_ROCKET_PATTERN].freeze
13
+
14
+ def self.run(str)
15
+ ALL_REGEX.each_with_object(Set.new) do |pattern, controllers|
16
+ matches = str.scan(pattern)
17
+ next if matches.empty? || matches.nil?
18
+
19
+ flattened_matches = matches.flatten.flat_map(&:split)
20
+ controllers.merge(flattened_matches)
21
+ end.to_a
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ordit
4
+ class Definitions
5
+ def self.all
6
+ Ordit.configuration.view_paths.each_with_object({}) do |path, hash|
7
+ Dir.glob(path.to_s).each do |file|
8
+ content = File.read(file)
9
+ matches = DefinitionMatcher.run(content)
10
+ next if matches.empty?
11
+
12
+ matches.each do |match|
13
+ relative_file = Pathname.new(file).relative_path_from(
14
+ Pathname.new(Ordit.root)
15
+ ).to_s
16
+ hash[match] ||= []
17
+ hash[match] |= [relative_file]
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.find(name)
24
+ Ordit.configuration.view_paths.flat_map do |path|
25
+ results = Dir.glob(path.to_s).reject do |file|
26
+ content = File.read(file)
27
+ matches = DefinitionMatcher.run(content)
28
+ matches.empty?
29
+ end
30
+
31
+ results.flat_map do |result|
32
+ Pathname.new(result).relative_path_from(Pathname.new(Ordit.root)).to_s
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ordit
4
+ class Files
5
+ def self.all
6
+ run
7
+ end
8
+
9
+ def self.run
10
+ Ordit.configuration.controller_paths.flat_map do |path_pattern|
11
+ base_dir = path_pattern.split("/*").first
12
+
13
+ Dir.glob(path_pattern).select do |file|
14
+ # Only filter by controller pattern if we're finding all files
15
+ file.match?(%r{[^/]*_?controller\.(js|ts)$})
16
+ end.map do |file|
17
+ Pathname.new(file).relative_path_from(Pathname.new(base_dir)).to_s
18
+ end
19
+ end.uniq
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ordit
4
+ class Results
5
+ def initialize(name = nil)
6
+ @name = name
7
+ end
8
+
9
+ def files
10
+ return Definitions.find(@name) if @name
11
+
12
+ Files.all
13
+ end
14
+
15
+ def definitions
16
+ Definitions.all
17
+ end
18
+
19
+ def active_controllers = to_h[:active_controllers].sort
20
+ def undefined_controllers = to_h[:undefined_controllers]
21
+ def unused_controllers = to_h[:unused_controllers].sort
22
+
23
+ def to_h
24
+ @to_h ||= begin
25
+ base_object = {
26
+ active_controllers: [],
27
+ undefined_controllers: {},
28
+ unused_controllers: files.dup || []
29
+ }
30
+
31
+
32
+ definitions.each_with_object(base_object) do |(definition, file), result|
33
+ # Convert definition pattern to match file pattern
34
+ file_pattern = definition_to_file_pattern(definition)
35
+
36
+ # Find matching file
37
+ matching_file = files.find { |f| file_pattern == file_pattern_from_path(f) }
38
+
39
+ if matching_file
40
+ result[:active_controllers] << matching_file
41
+ result[:unused_controllers].delete(matching_file)
42
+ result[:active_controllers].sort!
43
+ else
44
+ result[:undefined_controllers][definition] ||= []
45
+ result[:undefined_controllers][definition] |= file
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def definition_to_file_pattern(definition)
54
+ # Handle nested paths (double hyphen becomes directory separator)
55
+ path_parts = definition.split("--")
56
+
57
+ # Convert each part from kebab-case to snake_case
58
+ path_parts = path_parts.map { |part| part.gsub("-", "_") }
59
+
60
+ # Join with directory separator
61
+ path_parts.join("/")
62
+ end
63
+
64
+ def file_pattern_from_path(file)
65
+ # Remove _controller.js suffix and return the pattern
66
+ file.sub(/_controller\.(js|ts)$/, "")
67
+ end
68
+ end
69
+ end
data/lib/ordit/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ordit
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/ordit.rb CHANGED
@@ -4,9 +4,11 @@ require "set"
4
4
  require "pathname"
5
5
  require_relative "ordit/version"
6
6
  require_relative "ordit/configuration"
7
- require_relative "ordit/auditor"
8
- require_relative "ordit/scanner"
9
- require_relative "ordit/result_generator"
7
+ require_relative "ordit/files"
8
+ require_relative "ordit/definitions"
9
+ require_relative "ordit/definition_matcher"
10
+ require_relative "ordit/results"
11
+ require_relative "ordit/console_output"
10
12
 
11
13
  if defined?(Rails)
12
14
  require "rails"
data/lib/tasks/ordit.rake CHANGED
@@ -3,7 +3,7 @@
3
3
  namespace :ordit do
4
4
  desc "Audit Stimulus controllers usage and find orphaned controllers"
5
5
  task stimulus: :environment do
6
- Ordit::Auditor.run
6
+ Ordit::ConsoleOutput.run
7
7
  end
8
8
 
9
9
  desc "Scan files for stimulus controller usage (e.g., rake audit:scan[products])"
@@ -15,6 +15,6 @@ namespace :ordit do
15
15
  next
16
16
  end
17
17
 
18
- Ordit::Scanner.run(controller)
18
+ Ordit::ConsoleOutput.run(controller)
19
19
  end
20
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ordit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-12 00:00:00.000000000 Z
11
+ date: 2024-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -50,11 +50,13 @@ files:
50
50
  - LICENSE.txt
51
51
  - README.md
52
52
  - lib/ordit.rb
53
- - lib/ordit/auditor.rb
54
53
  - lib/ordit/configuration.rb
54
+ - lib/ordit/console_output.rb
55
+ - lib/ordit/definition_matcher.rb
56
+ - lib/ordit/definitions.rb
57
+ - lib/ordit/files.rb
55
58
  - lib/ordit/railtie.rb
56
- - lib/ordit/result_generator.rb
57
- - lib/ordit/scanner.rb
59
+ - lib/ordit/results.rb
58
60
  - lib/ordit/version.rb
59
61
  - lib/tasks/ordit.rake
60
62
  homepage: https://github.com/tobyond/ordit
data/lib/ordit/auditor.rb DELETED
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ordit
4
- class Auditor
5
- def self.run
6
- new.run
7
- end
8
-
9
- def run
10
- ResultGenerator.new(
11
- defined_controllers:,
12
- used_controllers:,
13
- controller_locations:,
14
- usage_locations:
15
- )
16
- end
17
-
18
- private
19
-
20
- def config
21
- @config ||= Ordit.configuration
22
- end
23
-
24
- def defined_controllers
25
- config.controller_paths.each_with_object(Set.new) do |path, controllers|
26
- Dir.glob(path.to_s).each do |file|
27
- # Extract relative path from controllers directory
28
- full_path = Pathname.new(file)
29
- controllers_dir = full_path.each_filename.find_index("controllers")
30
-
31
- next unless controllers_dir
32
-
33
- # Get path components after 'controllers'
34
- controller_path = full_path.each_filename.to_a[(controllers_dir + 1)..]
35
- next unless controller_path[-1].include?('_controller')
36
-
37
- # Remove _controller.js from the last component
38
- controller_path[-1] = controller_path[-1].sub(/_controller\.(js|ts)$/, "")
39
- # Join with -- for namespacing and convert underscores to hyphens
40
- name = controller_path.join("--").gsub("_", "-")
41
- controllers << name
42
- end
43
- end
44
- end
45
-
46
- def used_controllers
47
- config.view_paths.each_with_object(Set.new) do |path, controllers|
48
- Dir.glob(path.to_s).each do |file|
49
- content = File.read(file)
50
- patterns.each do |pattern|
51
- content.scan(pattern) do |match|
52
- # Split in case of multiple controllers
53
- match[0].split(/\s+/).each do |controller|
54
- # Store controller names exactly as they appear in the view
55
- # (they should already have hyphens as per Stimulus conventions)
56
- controllers << controller
57
- end
58
- end
59
- end
60
- end
61
- end
62
- end
63
-
64
- def controller_locations
65
- config.controller_paths.each_with_object({}) do |path_pattern, locations|
66
- Dir.glob(path_pattern).each do |file|
67
- relative_path = Pathname.new(file).relative_path_from(Dir.pwd)
68
- controller_path = relative_path.to_s.gsub(%r{^app/javascript/controllers/|_controller\.(js|ts)$}, "")
69
- name = controller_path.gsub("/", "--")
70
- locations[name] = relative_path
71
- end
72
- end
73
- end
74
-
75
- def usage_locations
76
- config.view_paths.each_with_object(Hash.new { |h, k| h[k] = {} }) do |path_pattern, locations|
77
- Dir.glob(path_pattern).each do |file|
78
- File.readlines(file).each_with_index do |line, index|
79
- patterns.each do |pattern|
80
- line.scan(pattern) do |match|
81
- match[0].split(/\s+/).each do |controller|
82
- relative_path = Pathname.new(file).relative_path_from(Dir.pwd)
83
- locations[controller][relative_path] ||= []
84
- locations[controller][relative_path] << index + 1
85
- end
86
- end
87
- end
88
- end
89
- end
90
- end
91
- end
92
-
93
- def patterns
94
- @patterns ||= [
95
- /data-controller=["']([^"']+)["']/, # HTML attribute syntax
96
- # Ruby 1.9+ hash syntax - covers both with and without symbol prefix
97
- /data:\s*{(?:[^}]*\s)?(?::)?controller:\s*["']([^"']+)["']/,
98
- # Hash rocket syntax - covers both with and without symbol prefix
99
- /data:\s*{(?:[^}]*\s)?(?::)?controller\s*=>\s*["']([^"']+)["']/
100
- ]
101
- end
102
- end
103
- end
@@ -1,67 +0,0 @@
1
- module Ordit
2
- class ResultGenerator
3
- attr_reader :defined_controllers, :used_controllers,
4
- :controller_locations, :usage_locations
5
-
6
- def initialize(defined_controllers:, used_controllers:,
7
- controller_locations:, usage_locations:)
8
- @defined_controllers = defined_controllers
9
- @used_controllers = used_controllers
10
- @controller_locations = controller_locations
11
- @usage_locations = usage_locations
12
- end
13
-
14
- def unused_controllers
15
- defined_controllers - used_controllers
16
- end
17
-
18
- def undefined_controllers
19
- used_controllers - defined_controllers
20
- end
21
-
22
- def active_controllers
23
- defined_controllers & used_controllers
24
- end
25
-
26
- def to_console
27
- puts "\nšŸ“Š Stimulus Controller Audit\n"
28
-
29
- if unused_controllers.any?
30
- puts "\nāŒ Defined but unused controllers:"
31
- unused_controllers.sort.each do |controller|
32
- puts " #{controller}"
33
- puts " └─ #{controller_locations[controller]}"
34
- end
35
- end
36
-
37
- if undefined_controllers.any?
38
- puts "\nāš ļø Used but undefined controllers:"
39
- undefined_controllers.sort.each do |controller|
40
- puts " #{controller}"
41
- usage_locations[controller].each do |file, lines|
42
- puts " └─ #{file} (lines: #{lines.join(", ")})"
43
- end
44
- end
45
- end
46
-
47
- if active_controllers.any?
48
- puts "\nāœ… Active controllers:"
49
- active_controllers.sort.each do |controller|
50
- puts " #{controller}"
51
- puts " └─ Defined in: #{controller_locations[controller]}"
52
- puts " └─ Used in:"
53
- usage_locations[controller].each do |file, lines|
54
- puts " └─ #{file} (lines: #{lines.join(", ")})"
55
- end
56
- end
57
- end
58
-
59
- puts "\nšŸ“ˆ Summary:"
60
- puts " Total controllers defined: #{defined_controllers.size}"
61
- puts " Total controllers in use: #{used_controllers.size}"
62
- puts " Unused controllers: #{unused_controllers.size}"
63
- puts " Undefined controllers: #{undefined_controllers.size}"
64
- puts " Properly paired: #{active_controllers.size}"
65
- end
66
- end
67
- end
data/lib/ordit/scanner.rb DELETED
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ordit
4
- class Scanner
5
- def self.run(controller)
6
- new(controller).run
7
- end
8
-
9
- attr_reader :controller
10
-
11
- def initialize(controller)
12
- @controller = controller
13
- end
14
-
15
- def run
16
- print
17
- end
18
-
19
- def results
20
- config.view_paths.each_with_object([]) do |path, matches|
21
- Dir.glob(path.to_s).each do |file|
22
- content = File.readlines(file)
23
- content.each_with_index do |line, index|
24
- next unless patterns.any? { |pattern| line.match?(pattern) }
25
-
26
- matches << {
27
- file: Pathname.new(file).relative_path_from(Pathname.new(Dir.pwd)),
28
- line_number: index + 1,
29
- content: line.strip
30
- }
31
- end
32
- end
33
- end
34
- end
35
-
36
- private
37
-
38
- def config
39
- @config ||= Ordit.configuration
40
- end
41
-
42
- def patterns
43
- @patterns ||= [
44
- /data-controller=["'](?:[^"']*\s)?#{Regexp.escape(controller)}(?:\s[^"']*)?["']/, # HTML attribute
45
- /data:\s*{\s*(?:controller:|:controller\s*=>)\s*["'](?:[^"']*\s)?#{Regexp.escape(controller)}(?:\s[^"']*)?["']/ # Both hash syntaxes
46
- ]
47
- end
48
-
49
- def print
50
- puts "\nSearching for stimulus controller: '#{controller}'\n\n"
51
-
52
- if results.empty?
53
- puts "No matches found."
54
- return
55
- end
56
-
57
- current_file = nil
58
- results.each do |match|
59
- if current_file != match[:file]
60
- puts "šŸ“ #{match[:file]}"
61
- current_file = match[:file]
62
- end
63
- puts " Line #{match[:line_number]}:"
64
- puts " #{match[:content]}\n\n"
65
- end
66
- end
67
- end
68
- end