ordit 0.1.2 → 0.1.4

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: 85359a41eabc4eca9dd38bf08749860a980e7eb3a4f31c4e1550243a141fa14a
4
+ data.tar.gz: c2fb171d7a685aa3180c00fa3aee02af58d265f53e35d7d1eca7ca66bd33a3a3
5
5
  SHA512:
6
- metadata.gz: 980a3d0f8ba5d373577b55594350962f784775de06f2c0b288271929a71a68998799fc60b1611293a68ce15102168923377d0dccde9699f2348775c5cf4dc180
7
- data.tar.gz: 3e83e18491bb40508a8f65a8210b29e1c31e94c4e87c67c64c033ed5a3682768c39cae36c794363d37a8d2bdb3f3a457e161a52001584e0b1eabd8349891a0cc
6
+ metadata.gz: 8366966cf1d4fed14fe17580e926d01e0f2f2450f4499b7fd724cda766cdf774cd0edd4203a418c9f7fc6445082366604cd32284372af70324988c24a91eefe8
7
+ data.tar.gz: 1d9af5057943c712b64116749d069cd8a737ae0a0338e32e8a7e11037b1b35a5701afc40a530fe82b768c22cc77196e21557cb44b312e8a16dc1948622843ab3
data/CHANGELOG.md CHANGED
@@ -1,10 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.4] - 2024-16-12
4
+
5
+ - remove scan, your editor does this already
6
+
7
+ ## [0.1.3] - 2024-16-12
8
+
9
+ - more refactor, clean up, make more logical
10
+
3
11
  ## [0.1.2] - 2024-12-12
4
12
 
5
13
  - refactor, clean up
6
14
 
7
-
8
15
  ## [0.1.1] - 2024-12-12
9
16
 
10
17
  - Fix namespace issue in rake task
data/README.md CHANGED
@@ -17,7 +17,7 @@ bundle install
17
17
 
18
18
  ## Usage
19
19
 
20
- ### Audit All Controllers
20
+ ### Audit Stimulus Controllers
21
21
 
22
22
  Run an audit to see all defined and used controllers in your application:
23
23
 
@@ -28,41 +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)
52
- ```
53
-
54
- ### Scan for Specific Controller Usage
55
-
56
- Find all uses of a specific controller:
57
-
58
- ```bash
59
- rails ordit:scan[controller_name]
60
- ```
42
+ missing-controller
43
+ └─ šŸ“ app/views/products/show.html.erb
44
+ šŸ“ app/views/users/edit.html.erb
61
45
 
62
- Example:
63
- ```bash
64
- rails ordit:scan[products]
65
- rails ordit:scan[users--name] # For namespaced controllers
66
46
  ```
67
47
 
68
48
  ### Configuration
@@ -98,6 +78,9 @@ end
98
78
 
99
79
  # Hash rocket syntax
100
80
  <%= f.submit 'Save', data: { :controller => 'products' } %>
81
+
82
+ # Hash rocket string syntax
83
+ <%= f.submit 'Save', 'data' => { 'controller' => 'products' } %>
101
84
  ```
102
85
  - Scans ERB, HTML, and HAML files
103
86
  - Works with both JavaScript and TypeScript controller files
@@ -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,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Ordit
6
+ class ConsoleOutput
7
+ extend Forwardable
8
+
9
+ def self.run
10
+ new.run
11
+ end
12
+
13
+ def_delegators :@results,
14
+ :files, :active_controllers, :undefined_controllers, :unused_controllers
15
+
16
+ def initialize
17
+ @results = Results.new
18
+ end
19
+
20
+ def run
21
+ print_audit
22
+ end
23
+
24
+ def print_audit
25
+ puts "\nšŸ“Š Stimulus Controller Audit\n"
26
+
27
+ if unused_controllers.any?
28
+ puts "\nāŒ Controllers not defined in any views:"
29
+ unused_controllers.sort.each do |controller|
30
+ puts " #{controller}"
31
+ end
32
+ end
33
+
34
+ if undefined_controllers.any?
35
+ puts "\nāš ļø View with defined but missing controller:"
36
+ undefined_controllers.each do |controller, files|
37
+ puts "\n #{controller}_controller"
38
+
39
+ files.each_with_index do |file, index|
40
+ puts " #{ index.zero? ? '└─' : ' ' } šŸ“ #{file}"
41
+ end
42
+ end
43
+ end
44
+
45
+ puts "\nšŸ“ˆ Summary:"
46
+ puts " Total controllers defined: #{files.size}"
47
+ puts " Total controllers in use: #{active_controllers.size}"
48
+ puts " Unused controllers: #{unused_controllers.size}"
49
+ puts " Undefined controllers: #{undefined_controllers.size}"
50
+ end
51
+ end
52
+ 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,23 @@
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
+ end
23
+ 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,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ordit
4
+ class Results
5
+ def initialize
6
+ end
7
+
8
+ def files
9
+ Files.all
10
+ end
11
+
12
+ def definitions
13
+ Definitions.all
14
+ end
15
+
16
+ def active_controllers = to_h[:active_controllers].sort
17
+ def undefined_controllers = to_h[:undefined_controllers]
18
+ def unused_controllers = to_h[:unused_controllers].sort
19
+
20
+ def to_h
21
+ @to_h ||= begin
22
+ base_object = {
23
+ active_controllers: [],
24
+ undefined_controllers: {},
25
+ unused_controllers: files.dup || []
26
+ }
27
+
28
+
29
+ definitions.each_with_object(base_object) do |(definition, file), result|
30
+ # Convert definition pattern to match file pattern
31
+ file_pattern = definition_to_file_pattern(definition)
32
+
33
+ # Find matching file
34
+ matching_file = files.find { |f| file_pattern == file_pattern_from_path(f) }
35
+
36
+ if matching_file
37
+ result[:active_controllers] << matching_file
38
+ result[:unused_controllers].delete(matching_file)
39
+ result[:active_controllers].sort!
40
+ else
41
+ result[:undefined_controllers][definition] ||= []
42
+ result[:undefined_controllers][definition] |= file
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def definition_to_file_pattern(definition)
51
+ # Handle nested paths (double hyphen becomes directory separator)
52
+ path_parts = definition.split("--")
53
+
54
+ # Convert each part from kebab-case to snake_case
55
+ path_parts = path_parts.map { |part| part.gsub("-", "_") }
56
+
57
+ # Join with directory separator
58
+ path_parts.join("/")
59
+ end
60
+
61
+ def file_pattern_from_path(file)
62
+ # Remove _controller.js suffix and return the pattern
63
+ file.sub(/_controller\.(js|ts)$/, "")
64
+ end
65
+ end
66
+ 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.4"
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,18 +3,6 @@
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
7
- end
8
-
9
- desc "Scan files for stimulus controller usage (e.g., rake audit:scan[products])"
10
- task :scan, [:controller] => :environment do |_, args|
11
- controller = args[:controller]
12
-
13
- if controller.nil? || controller.empty?
14
- puts "Please provide a controller name: rake ordit:scan[controller_name]"
15
- next
16
- end
17
-
18
- Ordit::Scanner.run(controller)
6
+ Ordit::ConsoleOutput.run
19
7
  end
20
8
  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.4
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-18 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