routes-analyzer 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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +147 -0
- data/Rakefile +3 -0
- data/config/initializers/routes_analyzer.rb.example +16 -0
- data/lib/generators/routes/analyzer/install_generator.rb +35 -0
- data/lib/generators/routes/analyzer/templates/routes_analyzer.rb +21 -0
- data/lib/routes/analyzer/configuration.rb +35 -0
- data/lib/routes/analyzer/middleware.rb +91 -0
- data/lib/routes/analyzer/railtie.rb +17 -0
- data/lib/routes/analyzer/route_usage_tracker.rb +89 -0
- data/lib/routes/analyzer/version.rb +5 -0
- data/lib/routes/analyzer.rb +25 -0
- data/lib/tasks/routes/analyzer_tasks.rake +112 -0
- metadata +89 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7f58db787f3f902a902e91e34159d5f3faec5df6716e2003f643228d322effad
|
|
4
|
+
data.tar.gz: 07ddd074b8305ac9af28a058c5ed718ea1016829047a725f1f74628e26ea83c6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a186755509cd04163bb0c58140970e23613c8f8fae77191f6061c412a5aa479fc99ead4a83f79a5a05178aca51c746973eb8bd51b965fba2c48c8f08c4b0a990
|
|
7
|
+
data.tar.gz: 2b6fe9f6d24e3937bbab1ebbebe1db3043dbe0af90705a717e0a6507c19d78d39363812a6f3c3d48459e5ef577bb947edc70f893b63fe954d8ae52cd38309b95
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Gregorio Galante
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Routes::Analyzer
|
|
2
|
+
|
|
3
|
+
A Ruby on Rails plugin that tracks and analyzes route usage in your application. It uses Redis to store route access patterns and provides insights into which routes are being used and which are not.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Route Usage Tracking**: Automatically tracks which routes are accessed and how often
|
|
8
|
+
- **Redis Storage**: Uses Redis to store usage statistics efficiently
|
|
9
|
+
- **Configurable Timeframe**: Set custom analysis periods (default 30 days)
|
|
10
|
+
- **Comprehensive Reporting**: Shows both used and unused routes
|
|
11
|
+
- **Rake Tasks**: Easy-to-use commands for viewing statistics and managing data
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add this line to your application's Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem "routes-analyzer"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
And then execute:
|
|
22
|
+
```bash
|
|
23
|
+
$ bundle install
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
After installation, generate the configuration file:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bundle exec rails generate routes:analyzer:install
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This will create `config/initializers/routes_analyzer.rb` with the following content:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
Routes::Analyzer.configure do |config|
|
|
38
|
+
# Redis connection URL
|
|
39
|
+
config.redis_url = ENV['REDIS_URL'] || 'redis://localhost:6379/0'
|
|
40
|
+
|
|
41
|
+
# Redis key prefix for storing route usage data
|
|
42
|
+
config.redis_key_prefix = 'routes_analyzer'
|
|
43
|
+
|
|
44
|
+
# Timeframe for route usage analysis in days
|
|
45
|
+
config.timeframe = 30
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Alternatively, you can create the configuration file manually in `config/initializers/routes_analyzer.rb`.
|
|
50
|
+
|
|
51
|
+
### Configuration Options
|
|
52
|
+
|
|
53
|
+
- **redis_url**: The Redis connection URL (required)
|
|
54
|
+
- **redis_key_prefix**: Prefix for Redis keys to avoid conflicts (default: "routes_analyzer")
|
|
55
|
+
- **timeframe**: Number of days to consider for "recent" usage (default: 30)
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
Once installed and configured, the middleware will automatically start tracking route usage. No additional code changes are required.
|
|
60
|
+
|
|
61
|
+
### Rake Tasks
|
|
62
|
+
|
|
63
|
+
#### View Route Usage Statistics
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bundle exec rake routes:analyzer:usage
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This command shows:
|
|
70
|
+
- All routes defined in your `routes.rb` file
|
|
71
|
+
- Usage count for each route in the specified timeframe
|
|
72
|
+
- Last access timestamp
|
|
73
|
+
- Summary statistics (total, used, unused routes)
|
|
74
|
+
|
|
75
|
+
Example output:
|
|
76
|
+
```
|
|
77
|
+
Routes Usage Analysis (30 days)
|
|
78
|
+
================================================================================
|
|
79
|
+
|
|
80
|
+
COUNT ROUTE METHOD CONTROLLER#ACTION LAST ACCESSED
|
|
81
|
+
--------------------------------------------------------------------------------
|
|
82
|
+
45 /users GET users#index 2025-06-18 14:30
|
|
83
|
+
23 /users/:id GET users#show 2025-06-18 12:15
|
|
84
|
+
12 /posts GET posts#index 2025-06-17 09:45
|
|
85
|
+
0 /admin/reports GET admin/reports#index Never
|
|
86
|
+
0 /api/v1/health GET api/v1/health#check Never
|
|
87
|
+
|
|
88
|
+
Total routes: 5
|
|
89
|
+
Used routes: 3
|
|
90
|
+
Unused routes: 2
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### Clear Usage Data
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
bundle exec rake routes:analyzer:clear
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Removes all stored route usage data from Redis.
|
|
100
|
+
|
|
101
|
+
#### Check Configuration
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
bundle exec rake routes:analyzer:config
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Displays current configuration and tests Redis connectivity.
|
|
108
|
+
|
|
109
|
+
## How It Works
|
|
110
|
+
|
|
111
|
+
1. **Middleware Integration**: The plugin automatically adds middleware to your Rails application
|
|
112
|
+
2. **Request Tracking**: Each HTTP request is analyzed to extract route information
|
|
113
|
+
3. **Redis Storage**: Usage data is stored in Redis with the following structure:
|
|
114
|
+
- Route path and HTTP method
|
|
115
|
+
- Access count within the timeframe
|
|
116
|
+
- Last access timestamp
|
|
117
|
+
4. **Data Expiration**: Redis keys automatically expire after the configured timeframe plus a buffer period
|
|
118
|
+
|
|
119
|
+
## Data Structure
|
|
120
|
+
|
|
121
|
+
For each tracked route, the following data is stored in Redis:
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
{
|
|
125
|
+
route: "/users/:id", # Route pattern
|
|
126
|
+
method: "GET", # HTTP method
|
|
127
|
+
count: 15, # Number of accesses
|
|
128
|
+
last_accessed: 1718721600 # Unix timestamp
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Requirements
|
|
133
|
+
|
|
134
|
+
- Ruby on Rails 8.0.2+
|
|
135
|
+
- Redis 5.0+
|
|
136
|
+
|
|
137
|
+
## Contributing
|
|
138
|
+
|
|
139
|
+
1. Fork the repository
|
|
140
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
141
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
142
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
143
|
+
5. Create a Pull Request
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Example configuration for Routes Analyzer
|
|
2
|
+
# Copy this file to config/initializers/routes_analyzer.rb in your Rails application
|
|
3
|
+
|
|
4
|
+
Routes::Analyzer.configure do |config|
|
|
5
|
+
# Redis connection URL
|
|
6
|
+
# You can use environment variables to keep this secure
|
|
7
|
+
config.redis_url = ENV['REDIS_URL'] || 'redis://localhost:6379/0'
|
|
8
|
+
|
|
9
|
+
# Redis key prefix for storing route usage data
|
|
10
|
+
# This helps avoid conflicts with other Redis data
|
|
11
|
+
config.redis_key_prefix = 'routes_analyzer'
|
|
12
|
+
|
|
13
|
+
# Timeframe for route usage analysis in days
|
|
14
|
+
# Only routes accessed within this timeframe will be considered "active"
|
|
15
|
+
config.timeframe = 30
|
|
16
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require "rails/generators/base"
|
|
2
|
+
|
|
3
|
+
module Routes
|
|
4
|
+
module Analyzer
|
|
5
|
+
module Generators
|
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
|
7
|
+
desc "Install Routes Analyzer configuration file"
|
|
8
|
+
|
|
9
|
+
def self.source_root
|
|
10
|
+
@source_root ||= File.expand_path("templates", __dir__)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create_configuration_file
|
|
14
|
+
template "routes_analyzer.rb", "config/initializers/routes_analyzer.rb"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def show_instructions
|
|
18
|
+
say "\n"
|
|
19
|
+
say "Routes Analyzer has been installed!", :green
|
|
20
|
+
say "\n"
|
|
21
|
+
say "Configuration file created at: config/initializers/routes_analyzer.rb"
|
|
22
|
+
say "\n"
|
|
23
|
+
say "Next steps:"
|
|
24
|
+
say "1. Configure your Redis connection in the initializer"
|
|
25
|
+
say "2. Restart your Rails application"
|
|
26
|
+
say "3. Use rake tasks to analyze your routes:"
|
|
27
|
+
say " - rails routes:analyzer:usage # Show route usage statistics"
|
|
28
|
+
say " - rails routes:analyzer:config # Check configuration"
|
|
29
|
+
say " - rails routes:analyzer:clear # Clear usage data"
|
|
30
|
+
say "\n"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Configuration for Routes Analyzer
|
|
2
|
+
# This plugin tracks route usage in your Rails application using Redis
|
|
3
|
+
|
|
4
|
+
Routes::Analyzer.configure do |config|
|
|
5
|
+
# Redis connection URL
|
|
6
|
+
# You can use environment variables to keep this secure
|
|
7
|
+
# Examples:
|
|
8
|
+
# - redis://localhost:6379/0
|
|
9
|
+
# - redis://username:password@localhost:6379/0
|
|
10
|
+
# - rediss://localhost:6380/0 (for SSL/TLS)
|
|
11
|
+
config.redis_url = ENV["REDIS_URL"] || "redis://localhost:6379/0"
|
|
12
|
+
|
|
13
|
+
# Redis key prefix for storing route usage data
|
|
14
|
+
# This helps avoid conflicts with other Redis data in your application
|
|
15
|
+
config.redis_key_prefix = "routes_analyzer"
|
|
16
|
+
|
|
17
|
+
# Timeframe for route usage analysis in days
|
|
18
|
+
# Only routes accessed within this timeframe will be considered "active"
|
|
19
|
+
# This also determines how long data is stored in Redis
|
|
20
|
+
config.timeframe = 30
|
|
21
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Routes
|
|
2
|
+
module Analyzer
|
|
3
|
+
class Configuration
|
|
4
|
+
attr_accessor :redis_url, :redis_key_prefix, :timeframe
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@redis_url = nil
|
|
8
|
+
@redis_key_prefix = "routes_analyzer"
|
|
9
|
+
@timeframe = 30 # days
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def redis_client
|
|
13
|
+
@redis_client ||= Redis.new(url: redis_url) if redis_url
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def validate!
|
|
17
|
+
raise "Redis URL must be configured" unless redis_url
|
|
18
|
+
|
|
19
|
+
# Test Redis connection
|
|
20
|
+
redis_client.ping
|
|
21
|
+
rescue Redis::BaseError => e
|
|
22
|
+
raise "Failed to connect to Redis: #{e.message}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def valid?
|
|
26
|
+
return false unless redis_url
|
|
27
|
+
|
|
28
|
+
redis_client.ping
|
|
29
|
+
true
|
|
30
|
+
rescue Redis::BaseError
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require "redis"
|
|
2
|
+
|
|
3
|
+
module Routes
|
|
4
|
+
module Analyzer
|
|
5
|
+
class Middleware
|
|
6
|
+
def initialize(app)
|
|
7
|
+
@app = app
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call(env)
|
|
11
|
+
status, headers, response = @app.call(env)
|
|
12
|
+
|
|
13
|
+
# Track route usage after processing the request
|
|
14
|
+
track_route_usage(env) if should_track?(env)
|
|
15
|
+
|
|
16
|
+
[ status, headers, response ]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def should_track?(env)
|
|
22
|
+
# Only track if configuration is valid and we have a Rails route
|
|
23
|
+
return false unless Routes::Analyzer.track_usage?
|
|
24
|
+
return false unless env["action_controller.instance"]
|
|
25
|
+
|
|
26
|
+
true
|
|
27
|
+
rescue => e
|
|
28
|
+
Rails.logger.warn "Routes::Analyzer: Failed to check tracking conditions: #{e.message}"
|
|
29
|
+
false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def track_route_usage(env)
|
|
33
|
+
request = Rack::Request.new(env)
|
|
34
|
+
route_info = extract_route_info(env, request)
|
|
35
|
+
|
|
36
|
+
return unless route_info
|
|
37
|
+
|
|
38
|
+
redis_key = build_redis_key(route_info[:route], route_info[:method])
|
|
39
|
+
current_time = Time.current
|
|
40
|
+
|
|
41
|
+
configuration.redis_client.multi do |redis|
|
|
42
|
+
# Increment counter
|
|
43
|
+
redis.hincrby(redis_key, "count", 1)
|
|
44
|
+
|
|
45
|
+
# Update last accessed timestamp
|
|
46
|
+
redis.hset(redis_key, "last_accessed", current_time.to_i)
|
|
47
|
+
|
|
48
|
+
# Set route and method info (in case it's the first time)
|
|
49
|
+
redis.hset(redis_key, "route", route_info[:route])
|
|
50
|
+
redis.hset(redis_key, "method", route_info[:method])
|
|
51
|
+
|
|
52
|
+
# Set expiration based on timeframe (add some buffer)
|
|
53
|
+
redis.expire(redis_key, (configuration.timeframe + 7) * 24 * 60 * 60)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
rescue => e
|
|
57
|
+
Rails.logger.warn "Routes::Analyzer: Failed to track route usage: #{e.message}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def extract_route_info(env, request)
|
|
61
|
+
# Get the matched route from Rails
|
|
62
|
+
if env["action_controller.instance"]
|
|
63
|
+
controller = env["action_controller.instance"]
|
|
64
|
+
action = controller.action_name
|
|
65
|
+
controller_name = controller.controller_name
|
|
66
|
+
|
|
67
|
+
# Build route pattern from request path, removing query parameters
|
|
68
|
+
route_path = request.path_info
|
|
69
|
+
|
|
70
|
+
{
|
|
71
|
+
route: route_path,
|
|
72
|
+
method: request.request_method.upcase,
|
|
73
|
+
controller: controller_name,
|
|
74
|
+
action: action
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
rescue => e
|
|
78
|
+
Rails.logger.warn "Routes::Analyzer: Failed to extract route info: #{e.message}"
|
|
79
|
+
nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def build_redis_key(route, method)
|
|
83
|
+
"#{configuration.redis_key_prefix}:#{method}:#{route.gsub('/', ':')}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def configuration
|
|
87
|
+
Routes::Analyzer.configuration
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Routes
|
|
2
|
+
module Analyzer
|
|
3
|
+
class Railtie < ::Rails::Railtie
|
|
4
|
+
initializer "routes_analyzer.configure_middleware" do |app|
|
|
5
|
+
app.middleware.use Routes::Analyzer::Middleware
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
rake_tasks do
|
|
9
|
+
load "tasks/routes/analyzer_tasks.rake"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
generators do
|
|
13
|
+
require "generators/routes/analyzer/install_generator"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Routes
|
|
2
|
+
module Analyzer
|
|
3
|
+
class RouteUsageTracker
|
|
4
|
+
def initialize(configuration)
|
|
5
|
+
@configuration = configuration
|
|
6
|
+
@redis = configuration.redis_client
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def get_usage_stats
|
|
10
|
+
cutoff_time = @configuration.timeframe.days.ago.to_i
|
|
11
|
+
|
|
12
|
+
# Get all route keys
|
|
13
|
+
pattern = "#{@configuration.redis_key_prefix}:*"
|
|
14
|
+
keys = @redis.keys(pattern)
|
|
15
|
+
|
|
16
|
+
usage_stats = []
|
|
17
|
+
|
|
18
|
+
keys.each do |key|
|
|
19
|
+
route_data = @redis.hgetall(key)
|
|
20
|
+
next if route_data.empty?
|
|
21
|
+
|
|
22
|
+
# Filter by timeframe if last_accessed is available
|
|
23
|
+
last_accessed = route_data["last_accessed"]&.to_i
|
|
24
|
+
next if last_accessed && last_accessed < cutoff_time
|
|
25
|
+
|
|
26
|
+
usage_stats << {
|
|
27
|
+
route: route_data["route"],
|
|
28
|
+
method: route_data["method"],
|
|
29
|
+
count: route_data["count"]&.to_i || 0,
|
|
30
|
+
last_accessed: last_accessed ? Time.at(last_accessed) : nil
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Sort by count (descending)
|
|
35
|
+
usage_stats.sort_by { |stat| -stat[:count] }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def get_all_defined_routes
|
|
39
|
+
return [] unless defined?(Rails) && Rails.application
|
|
40
|
+
|
|
41
|
+
Rails.application.routes.routes.map do |route|
|
|
42
|
+
{
|
|
43
|
+
route: route.path.spec.to_s.gsub(/\(\.:format\)$/, ""),
|
|
44
|
+
method: route.verb,
|
|
45
|
+
controller: route.defaults[:controller],
|
|
46
|
+
action: route.defaults[:action],
|
|
47
|
+
name: route.name
|
|
48
|
+
}
|
|
49
|
+
end.compact.uniq { |r| [ r[:route], r[:method] ] }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def merge_with_defined_routes(usage_stats)
|
|
53
|
+
defined_routes = get_all_defined_routes
|
|
54
|
+
usage_by_route_method = usage_stats.index_by { |stat| "#{stat[:method]}:#{stat[:route]}" }
|
|
55
|
+
|
|
56
|
+
merged_routes = []
|
|
57
|
+
|
|
58
|
+
defined_routes.each do |defined_route|
|
|
59
|
+
route_key = "#{defined_route[:method]}:#{defined_route[:route]}"
|
|
60
|
+
usage_stat = usage_by_route_method[route_key]
|
|
61
|
+
|
|
62
|
+
if usage_stat
|
|
63
|
+
merged_routes << defined_route.merge(usage_stat)
|
|
64
|
+
else
|
|
65
|
+
merged_routes << defined_route.merge(
|
|
66
|
+
count: 0,
|
|
67
|
+
last_accessed: nil
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Add any tracked routes that aren't in the defined routes (in case of dynamic routes)
|
|
73
|
+
usage_stats.each do |usage_stat|
|
|
74
|
+
route_key = "#{usage_stat[:method]}:#{usage_stat[:route]}"
|
|
75
|
+
unless defined_routes.any? { |dr| "#{dr[:method]}:#{dr[:route]}" == route_key }
|
|
76
|
+
merged_routes << usage_stat.merge(
|
|
77
|
+
controller: nil,
|
|
78
|
+
action: nil,
|
|
79
|
+
name: nil
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Sort by count (descending), then by route name
|
|
85
|
+
merged_routes.sort_by { |route| [ -route[:count], route[:route] ] }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require "routes/analyzer/version"
|
|
2
|
+
require "routes/analyzer/railtie"
|
|
3
|
+
require "routes/analyzer/configuration"
|
|
4
|
+
require "routes/analyzer/middleware"
|
|
5
|
+
require "routes/analyzer/route_usage_tracker"
|
|
6
|
+
|
|
7
|
+
module Routes
|
|
8
|
+
module Analyzer
|
|
9
|
+
class << self
|
|
10
|
+
def configuration
|
|
11
|
+
@configuration ||= Configuration.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def configure
|
|
15
|
+
yield(configuration)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def track_usage?
|
|
19
|
+
configuration.valid?
|
|
20
|
+
rescue
|
|
21
|
+
false
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
namespace :routes do
|
|
2
|
+
namespace :analyzer do
|
|
3
|
+
desc "Show routes usage statistics"
|
|
4
|
+
task usage: :environment do
|
|
5
|
+
begin
|
|
6
|
+
config = Routes::Analyzer.configuration
|
|
7
|
+
tracker = Routes::Analyzer::RouteUsageTracker.new(config)
|
|
8
|
+
|
|
9
|
+
puts "Routes Usage Analysis (#{config.timeframe} days)"
|
|
10
|
+
puts "=" * 80
|
|
11
|
+
puts
|
|
12
|
+
|
|
13
|
+
if config.valid?
|
|
14
|
+
usage_stats = tracker.get_usage_stats
|
|
15
|
+
merged_routes = tracker.merge_with_defined_routes(usage_stats)
|
|
16
|
+
else
|
|
17
|
+
puts "Warning: Redis not configured. Showing defined routes only."
|
|
18
|
+
puts
|
|
19
|
+
merged_routes = tracker.get_all_defined_routes.map do |route|
|
|
20
|
+
route.merge(count: 0, last_accessed: nil)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if merged_routes.empty?
|
|
25
|
+
puts "No routes found."
|
|
26
|
+
next
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Print header
|
|
30
|
+
printf "%-8s %-40s %-15s %-20s %s\n", "COUNT", "ROUTE", "METHOD", "CONTROLLER#ACTION", "LAST ACCESSED"
|
|
31
|
+
puts "-" * 80
|
|
32
|
+
|
|
33
|
+
merged_routes.each do |route|
|
|
34
|
+
controller_action = if route[:controller] && route[:action]
|
|
35
|
+
"#{route[:controller]}##{route[:action]}"
|
|
36
|
+
else
|
|
37
|
+
"N/A"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
last_accessed = if route[:last_accessed]
|
|
41
|
+
route[:last_accessed].strftime("%Y-%m-%d %H:%M")
|
|
42
|
+
else
|
|
43
|
+
"Never"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
printf "%-8d %-40s %-15s %-20s %s\n",
|
|
47
|
+
route[:count],
|
|
48
|
+
route[:route].to_s[0, 40],
|
|
49
|
+
route[:method],
|
|
50
|
+
controller_action[0, 20],
|
|
51
|
+
last_accessed
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
puts
|
|
55
|
+
puts "Total routes: #{merged_routes.count}"
|
|
56
|
+
puts "Used routes: #{merged_routes.count { |r| r[:count] > 0 }}"
|
|
57
|
+
puts "Unused routes: #{merged_routes.count { |r| r[:count] == 0 }}"
|
|
58
|
+
|
|
59
|
+
rescue => e
|
|
60
|
+
puts "Error: #{e.message}"
|
|
61
|
+
puts "Make sure you have configured the routes analyzer properly."
|
|
62
|
+
puts "Example configuration in config/initializers/routes_analyzer.rb:"
|
|
63
|
+
puts
|
|
64
|
+
puts "Routes::Analyzer.configure do |config|"
|
|
65
|
+
puts " config.redis_url = 'redis://localhost:6379/0'"
|
|
66
|
+
puts " config.redis_key_prefix = 'routes_analyzer'"
|
|
67
|
+
puts " config.timeframe = 30"
|
|
68
|
+
puts "end"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
desc "Clear all routes usage data"
|
|
73
|
+
task clear: :environment do
|
|
74
|
+
begin
|
|
75
|
+
config = Routes::Analyzer.configuration
|
|
76
|
+
redis = config.redis_client
|
|
77
|
+
pattern = "#{config.redis_key_prefix}:*"
|
|
78
|
+
keys = redis.keys(pattern)
|
|
79
|
+
|
|
80
|
+
if keys.any?
|
|
81
|
+
redis.del(*keys)
|
|
82
|
+
puts "Cleared #{keys.count} route usage records."
|
|
83
|
+
else
|
|
84
|
+
puts "No route usage data found to clear."
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
rescue => e
|
|
88
|
+
puts "Error: #{e.message}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
desc "Show configuration"
|
|
93
|
+
task config: :environment do
|
|
94
|
+
config = Routes::Analyzer.configuration
|
|
95
|
+
puts "Routes Analyzer Configuration:"
|
|
96
|
+
puts "- Redis URL: #{config.redis_url || 'Not configured'}"
|
|
97
|
+
puts "- Redis Key Prefix: #{config.redis_key_prefix}"
|
|
98
|
+
puts "- Timeframe: #{config.timeframe} days"
|
|
99
|
+
|
|
100
|
+
begin
|
|
101
|
+
if config.redis_client
|
|
102
|
+
config.redis_client.ping
|
|
103
|
+
puts "- Redis Connection: ✓ Connected"
|
|
104
|
+
else
|
|
105
|
+
puts "- Redis Connection: ✗ Not configured"
|
|
106
|
+
end
|
|
107
|
+
rescue => e
|
|
108
|
+
puts "- Redis Connection: ✗ Failed (#{e.message})"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: routes-analyzer
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Gregorio Galante
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-06-19 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 6.1.5
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 6.1.5
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: redis
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 4.5.1
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 4.5.1
|
|
41
|
+
description: A Rails plugin that automatically tracks route usage patterns and provides
|
|
42
|
+
insights into which routes are being used and which are not. Uses Redis for efficient
|
|
43
|
+
storage and includes rake tasks for analysis.
|
|
44
|
+
email:
|
|
45
|
+
- me@gregoriogalante.com
|
|
46
|
+
executables: []
|
|
47
|
+
extensions: []
|
|
48
|
+
extra_rdoc_files: []
|
|
49
|
+
files:
|
|
50
|
+
- MIT-LICENSE
|
|
51
|
+
- README.md
|
|
52
|
+
- Rakefile
|
|
53
|
+
- config/initializers/routes_analyzer.rb.example
|
|
54
|
+
- lib/generators/routes/analyzer/install_generator.rb
|
|
55
|
+
- lib/generators/routes/analyzer/templates/routes_analyzer.rb
|
|
56
|
+
- lib/routes/analyzer.rb
|
|
57
|
+
- lib/routes/analyzer/configuration.rb
|
|
58
|
+
- lib/routes/analyzer/middleware.rb
|
|
59
|
+
- lib/routes/analyzer/railtie.rb
|
|
60
|
+
- lib/routes/analyzer/route_usage_tracker.rb
|
|
61
|
+
- lib/routes/analyzer/version.rb
|
|
62
|
+
- lib/tasks/routes/analyzer_tasks.rake
|
|
63
|
+
homepage: https://github.com/gregogalante/routes-analyzer
|
|
64
|
+
licenses:
|
|
65
|
+
- MIT
|
|
66
|
+
metadata:
|
|
67
|
+
homepage_uri: https://github.com/gregogalante/routes-analyzer
|
|
68
|
+
source_code_uri: https://github.com/gregogalante/routes-analyzer
|
|
69
|
+
changelog_uri: https://github.com/gregogalante/routes-analyzer/blob/main/CHANGELOG.md
|
|
70
|
+
post_install_message:
|
|
71
|
+
rdoc_options: []
|
|
72
|
+
require_paths:
|
|
73
|
+
- lib
|
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
|
+
requirements:
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '0'
|
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
84
|
+
requirements: []
|
|
85
|
+
rubygems_version: 3.4.1
|
|
86
|
+
signing_key:
|
|
87
|
+
specification_version: 4
|
|
88
|
+
summary: Track and analyze Ruby on Rails route usage with Redis
|
|
89
|
+
test_files: []
|