rails_route_finder 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4e9e56607bae548dfe2480790c9f5e28c2ba5915dd9c7604b7ea9954f4830a78
4
+ data.tar.gz: 97149337b3ea9d995444881567fdd2ffe487750e39b0a6ed78faadf042585e15
5
+ SHA512:
6
+ metadata.gz: 0a953132a650e26da8ab071279bf4898242aae3b6b702a82e5f94c8aace255c722ff913408bf002575909e62091a740ae89d206775f1208692318c79c536e443
7
+ data.tar.gz: 2eb8ed2e42a20e5f79456dd7cf275a3f3cc00e747290f3e4ccd56b3ce6d35c438bc7a0b60beabf0258783d9425c9d693e52114a6279ba78fe4386fd47d1f4ba2
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-03-31
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 gagegolden00
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # RailsRouteFinder
2
+
3
+ RailsRouteFinder is a gem designed to help Rails developers visualize and analyze the routes of their application. The gem allows you to list, filter, and analyze routes based on various parameters such as path, HTTP verb, controller#action, required parameters, and namespaces.
4
+
5
+ ## Installation
6
+
7
+ Download the gem and note its location.
8
+ Add Rails Route Finder to your gemfile like so.
9
+
10
+ > ` gem 'rails_route_finder', path: 'absolute/path/to/rails_route_finder' `
11
+
12
+ Then run `bundle install`
13
+
14
+ ### Inject Routes into Your Application
15
+
16
+ After installing the gem, you'll need to inject the routes provided by `RailsRouteFinder` into your application. To do this, add the following snippet to your `config/routes.rb` file:
17
+
18
+ # In config/routes.rb
19
+ RailsRouteFinder::Engine.routes.draw do
20
+ get 'index', to: 'routes#index', as: :index
21
+ root to: 'routes#index'
22
+ end
23
+
24
+ This will add a new route (`/rails_route_finder`) to your application that you can visit to visualize your application's routes.
25
+
26
+ ## Usage
27
+
28
+ Once installed, you can access the `/rails_route_finder` route to see an overview of all your application's routes. The routes will be displayed with details like:
29
+
30
+ - Path
31
+ - HTTP verb
32
+ - Controller#action
33
+ - Required parameters
34
+ - Namespace
35
+
36
+ You can filter, sort, and analyze these routes based on the provided parameters.
37
+
38
+ ## Development
39
+ TO DO
40
+
41
+ ## Contributing
42
+ TO DO
43
+
44
+ ## License
45
+ TO DO
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ task default: :test
@@ -0,0 +1,202 @@
1
+ body {
2
+ background-color: #1e1e1e;
3
+ color: #e0e0e0;
4
+ font-family: Arial, sans-serif;
5
+ display: flex;
6
+ flex-direction: column;
7
+ }
8
+
9
+ .page {
10
+ margin-top: 20px;
11
+ display: flex;
12
+ flex-direction: column;
13
+ width: 100%;
14
+ }
15
+
16
+ .search-header {
17
+ display: flex;
18
+ justify-content: space-between;
19
+ align-items: center;
20
+ width: 100%;
21
+ margin-bottom: 20px;
22
+ }
23
+
24
+ h1 {
25
+ font-size: 28px;
26
+ color: #d9534f;
27
+ flex-shrink: 0;
28
+ width: 7%;
29
+ }
30
+
31
+ .filtering-form {
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: flex-start;
35
+ gap: 15px;
36
+ padding: 10px;
37
+ width: 90%;
38
+ border: 2px solid #444;
39
+ background-color: #333;
40
+ border-radius: 6px;
41
+ flex-wrap: wrap;
42
+ }
43
+
44
+ .sort-filter-form {
45
+ display: flex;
46
+ flex-wrap: wrap;
47
+ align-items: center;
48
+ width: 100%;
49
+ gap: 10px;
50
+ }
51
+
52
+ .filter-fields {
53
+ display: flex;
54
+ gap: 10px;
55
+ align-items: center;
56
+ flex-wrap: wrap;
57
+ }
58
+
59
+ .filter-submit {
60
+ margin-left: auto;
61
+ }
62
+
63
+ select,
64
+ input[type="submit"] {
65
+ padding: 6px 10px;
66
+ font-size: 14px;
67
+ border: 1px solid #666;
68
+ border-radius: 4px;
69
+ background-color: #444;
70
+ color: #ddd;
71
+ height: 41px;
72
+ max-width: 160px;
73
+ }
74
+
75
+ input[type="submit"] {
76
+ background-color: #d9534f;
77
+ color: white;
78
+ cursor: pointer;
79
+ border: none;
80
+ padding: 8px 16px;
81
+ }
82
+
83
+ .search-bar {
84
+ height: 41px;
85
+ width: auto;
86
+ border: 1px solid #666;
87
+ border-radius: 4px;
88
+ background-color: #444;
89
+ padding: 8px; /* Adjust padding as needed */
90
+ }
91
+
92
+ input[type="submit"]:hover {
93
+ background-color: #c9302c;
94
+ }
95
+
96
+ .multi-select {
97
+ position: relative;
98
+ display: inline-block;
99
+ width: 140px;
100
+ background-color: #444;
101
+ border: 1px solid #666;
102
+ border-radius: 4px;
103
+ cursor: pointer;
104
+ }
105
+
106
+ .select-box {
107
+ display: flex;
108
+ justify-content: space-between;
109
+ align-items: center;
110
+ padding: 8px 12px;
111
+ color: #ddd;
112
+ width: 140px;
113
+ }
114
+
115
+ .select-box .arrow {
116
+ width: 0;
117
+ height: 0;
118
+ border-left: 5px solid transparent;
119
+ border-right: 5px solid transparent;
120
+ border-top: 6px solid #ddd;
121
+ transition: transform 0.2s;
122
+ }
123
+
124
+ .multi-select.active .arrow {
125
+ transform: rotate(180deg);
126
+ }
127
+
128
+ .checkbox-dropdown {
129
+ display: none;
130
+ position: absolute;
131
+ width: 100%;
132
+ background-color: #333;
133
+ border: 1px solid #666;
134
+ border-radius: 4px;
135
+ padding: 5px 0;
136
+ max-height: 150px;
137
+ overflow-y: auto;
138
+ z-index: 10;
139
+ }
140
+
141
+ .multi-select.active .checkbox-dropdown {
142
+ display: block;
143
+ }
144
+
145
+ .checkbox-dropdown label {
146
+ display: block;
147
+ padding: 8px;
148
+ color: #ddd;
149
+ cursor: pointer;
150
+ }
151
+
152
+ .checkbox-dropdown label:hover {
153
+ background-color: #555;
154
+ }
155
+
156
+ .checkbox-dropdown input {
157
+ margin-right: 8px;
158
+ }
159
+
160
+ .routes-grid {
161
+ display: grid;
162
+ grid-template-columns: 2fr 1fr 2fr 1fr 1fr;
163
+ width: 100%;
164
+ border-collapse: collapse;
165
+ font-size: 14px;
166
+ box-sizing: border-box;
167
+ margin-top: 5px;
168
+ border-radius: 6px;
169
+ border: 2px solid #444;
170
+ }
171
+
172
+ .route-header {
173
+ padding: 12px 10px;
174
+ border: 1px solid #333;
175
+ background-color: #333;
176
+ color: #fff;
177
+ font-weight: bold;
178
+ text-transform: uppercase;
179
+ text-align: left;
180
+ }
181
+
182
+ .route-item {
183
+ display: contents;
184
+ }
185
+
186
+ .route-item div {
187
+ padding: 12px 10px;
188
+ border: 1px solid #444;
189
+ text-align: left;
190
+ background-color: #2a2a2a;
191
+ color: #ddd;
192
+ white-space: normal;
193
+ overflow-wrap: break-word;
194
+ }
195
+
196
+ .route-item div:nth-child(odd) {
197
+ background-color: #3a3a3a;
198
+ }
199
+
200
+ .routes-grid div {
201
+ overflow-x: auto;
202
+ }
@@ -0,0 +1,39 @@
1
+ module RailsRouteFinder
2
+ class RoutesController < ApplicationController
3
+ def index
4
+ @routes = RailsRouteFinder::Route.all.uniq do |route|
5
+ [route.path, route.verb, route.controller, route.required_params, route.namespace]
6
+ end
7
+
8
+ if params[:search].present? && !params[:search].blank?
9
+ query = params[:search].downcase
10
+ @routes.select! do |route|
11
+ route.path.downcase.include?(query)
12
+ end
13
+ end
14
+
15
+ if params[:verb].present? && !params[:verb].blank?
16
+ @routes.select! do |route|
17
+ route.verb.casecmp?(params[:verb])
18
+ end
19
+ end
20
+
21
+ if params[:namespace].present? && !params[:namespace].blank?
22
+ @routes.select! { |route| route.namespace&.casecmp?(params[:namespace]) }
23
+ end
24
+
25
+ if params[:quick_filters].present? && params[:quick_filters].include?('omit_rails_defaults')
26
+ @routes.reject! { |route| route.path.start_with?('/rails') }
27
+ end
28
+
29
+ case params[:sort]
30
+ when 'path'
31
+ @routes.sort_by! { |route| route.path.downcase }
32
+ when 'namespace'
33
+ @routes.sort_by! { |route| route.namespace.to_s.downcase }
34
+ when 'verb'
35
+ @routes.sort_by! { |route| route.verb.to_s.downcase }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,74 @@
1
+ <%= stylesheet_link_tag "rails_route_finder/rails_route_finder_index" %>
2
+ <div class='page'>
3
+ <div class="search-header">
4
+ <h1>Route Finder</h1>
5
+ <div class="filtering-form">
6
+ <%= form_with url: '/rails_route_finder', method: :get, class: 'sort-filter-form', local: true do |f| %>
7
+ <div class="filter-fields">
8
+ <%= f.select :sort, [["Sort by", ""], ["Path", "path"], ["Namespace", "namespace"], ["Verb", "verb"]] %>
9
+ <%= f.select :verb, options_for_select([["Filter Verb", ""]] + @routes.map(&:verb).uniq.compact.reject(&:blank?).map { |verb| [verb, verb] }), include_blank: false %>
10
+ <%= f.select :namespace, options_for_select(@routes.map(&:namespace).uniq.compact), include_blank: "Filter Namespace" %>
11
+ <!-- Custom Multi-Select with Checkboxes -->
12
+ <div class="multi-select">
13
+ <div class="select-box" onclick="toggleDropdown()">
14
+ <span id="selected-filters">Quick Filters</span>
15
+ <div class="arrow"></div>
16
+ </div>
17
+ <div class="checkbox-dropdown" id="checkbox-dropdown">
18
+ <label><input type="checkbox" name="quick_filters[]" value="omit_rails_defaults"> Omit Rails Defaults</label>
19
+ </div>
20
+ </div>
21
+ <%= f.text_field :search, placeholder: "Search routes...", class: "search-bar" %>
22
+
23
+ </div>
24
+ <div class="filter-submit">
25
+ <%= f.submit "Sort & Filter" %>
26
+ </div>
27
+ <% end %>
28
+ </div>
29
+ </div>
30
+ <div class="routes-grid">
31
+ <div class="route-header">Path</div>
32
+ <div class="route-header">Verb</div>
33
+ <div class="route-header">Controller#Action</div>
34
+ <div class="route-header">Required Params</div>
35
+ <div class="route-header">Namespace</div>
36
+ <% @routes.each do |route| %>
37
+ <div class="route-item">
38
+ <div><%= route.path %></div>
39
+ <div><%= route.verb || "ANY" %></div>
40
+ <div><%= "#{route.controller}##{route.action}" if route.controller %></div>
41
+ <div>
42
+ <% if route.required_params.any? %>
43
+ <% route.required_params.each do |param| %>
44
+ <div><%= param.values.first %></div>
45
+ <% end %>
46
+ <% else %>
47
+ <div>None</div>
48
+ <% end %>
49
+ </div>
50
+ <div><%= route.namespace || "None" %></div>
51
+ </div>
52
+ <% end %>
53
+ </div>
54
+ </div>
55
+ <script>
56
+ function toggleDropdown() {
57
+ document.querySelector(".multi-select").classList.toggle("active");
58
+ }
59
+
60
+ document.addEventListener("click", function(event) {
61
+ const multiSelect = document.querySelector(".multi-select");
62
+ if (!multiSelect.contains(event.target)) {
63
+ multiSelect.classList.remove("active");
64
+ }
65
+ });
66
+
67
+ document.querySelectorAll(".checkbox-dropdown input").forEach((checkbox) => {
68
+ checkbox.addEventListener("change", function () {
69
+ const selectedFilters = Array.from(document.querySelectorAll(".checkbox-dropdown input:checked"))
70
+ .map((cb) => cb.parentElement.textContent.trim());
71
+ document.getElementById("selected-filters").textContent = selectedFilters.length ? selectedFilters.join(", ") : "Quick Filters";
72
+ });
73
+ });
74
+ </script>
@@ -0,0 +1,15 @@
1
+ module RailsRouteFinder
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsRouteFinder
4
+
5
+ require 'rails_route_finder/route'
6
+
7
+ config.assets.paths << root.join('app', 'assets', 'stylesheets')
8
+
9
+ initializer 'rails_route_finder.append_routes' do |app|
10
+ app.routes.append do
11
+ mount RailsRouteFinder::Engine => '/rails_route_finder'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,55 @@
1
+ module RailsRouteFinder
2
+ class Route
3
+ attr_reader :path, :verb, :controller, :action, :required_params, :namespace, :base_resource, :other_resource,
4
+ :required_format
5
+
6
+ def initialize(route)
7
+ @path = route.path.spec.to_s
8
+ @verb = route.verb&.to_s
9
+ @controller = route.defaults[:controller]
10
+ @action = route.defaults[:action]
11
+ @required_params = extract_required_params(route.path)
12
+ @namespace = extract_namespace
13
+ @base_resource, @other_resource = extract_resources
14
+ @required_format = determine_required_format(@path)
15
+ end
16
+
17
+ def self.all
18
+ Rails.application.routes.routes.map { |route| new(route) }
19
+ end
20
+
21
+ private
22
+
23
+ def extract_required_params(path)
24
+ path_spec = path.spec.to_s
25
+ path_spec = path_spec.gsub(/\(.*\)/, '')
26
+ path_spec.scan(/(:[a-zA-Z0-9_]+)/).flatten
27
+ resource_params = []
28
+ segments = path_spec.split('/').reject { |segment| segment.empty? || segment == 'format' }
29
+
30
+ segments.each_with_index do |segment, index|
31
+ resource_params << { segments[index - 1].to_sym => segment.to_sym } if segment.start_with?(':')
32
+ end
33
+
34
+ resource_params
35
+ end
36
+
37
+ def extract_namespace
38
+ return nil unless controller
39
+
40
+ parts = controller.split('/')
41
+ parts.length > 1 ? parts[0..-2].join('/') : nil
42
+ end
43
+
44
+ def extract_resources
45
+ segments = path.split('/').reject(&:empty?)
46
+ base_resource = segments.first if segments.present?
47
+ other_resource = segments[1] if segments.length > 1
48
+ [base_resource, other_resource]
49
+ end
50
+
51
+ def determine_required_format(_path)
52
+ 'Dont know that this is necessary since pretty much all routes accept all formats'
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsRouteFinder
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rails_route_finder/version"
4
+ require_relative "rails_route_finder/engine" if defined?(Rails)
5
+
6
+ module RailsRouteFinder
7
+ class Error < StandardError; end
8
+ # Your code goes here...
9
+ end
10
+
@@ -0,0 +1,4 @@
1
+ module RailsRouteFinder
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_route_finder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - gagegolden00
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-04-01 00:00:00.000000000 Z
11
+ dependencies: []
12
+ email:
13
+ - malcolm.golden0@gmail.com
14
+ executables: []
15
+ extensions: []
16
+ extra_rdoc_files: []
17
+ files:
18
+ - CHANGELOG.md
19
+ - LICENSE.txt
20
+ - README.md
21
+ - Rakefile
22
+ - app/assets/stylesheets/rails_route_finder/rails_route_finder_index.css
23
+ - app/controllers/rails_route_finder/routes_controller.rb
24
+ - app/views/rails_route_finder/routes/index.html.erb
25
+ - lib/rails_route_finder.rb
26
+ - lib/rails_route_finder/engine.rb
27
+ - lib/rails_route_finder/route.rb
28
+ - lib/rails_route_finder/version.rb
29
+ - sig/rails_route_finder.rbs
30
+ homepage: https://github.com/gagegolden00/rails_route_finder
31
+ licenses:
32
+ - MIT
33
+ metadata:
34
+ homepage_uri: https://github.com/gagegolden00/rails_route_finder
35
+ source_code_uri: https://github.com/gagegolden00/rails_route_finder
36
+ changelog_uri: https://github.com/gagegolden00/rails_route_finder/blob/main/CHANGELOG.md
37
+ allowed_push_host: https://rubygems.org
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 3.1.0
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubygems_version: 3.6.6
53
+ specification_version: 4
54
+ summary: A simple engine to help sort and filter rails routes.
55
+ test_files: []