routes-analyzer 0.1.4 → 0.1.5

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: 79618d6dca3b9246b8023943d2863bc985e3d9674fd56e51aec84ad0c10ebf62
4
- data.tar.gz: 07043a67f3c6011aacc34097476444e45c7749ed8134bd71b48bb29ffe47cdff
3
+ metadata.gz: 3cae3e4dca655b13929955c6357b4351607dd32ae29331c643666e6d05562e32
4
+ data.tar.gz: 3b533755ae2bc4b810708ac9d705085f5467c3cb94714a458baa6aaa8f5eaebf
5
5
  SHA512:
6
- metadata.gz: 86f3904c67a101f44776d2d583c1f818340b5f79218a9ac0297a3eca2045faaff4142218cdb8e8a1bc4ec68ac122918aa237845dd2ad8a068a4ee2a45804058b
7
- data.tar.gz: 43bf9d60f739f769efe4bd35a628a30537298226da1b5b8457f98e726168718361f292663eaa47a1d811baac744d489278a0a969bcb0e13f3792c791be5a9b78
6
+ metadata.gz: d47e92d852c70f7ce7108777532aea5f5650f562fe31607d522b178bc206ed8430f713408cb8e28989d38ff8c66c3c5f64bf4ac87407e928bff9f450b7189255
7
+ data.tar.gz: d3c981912a2b4894895f4f19d80842d878f6aa1edb1ec157a34e05d54e8ef67cd7c490ed055ef7d1bfd8187277e0f4d7eb923d52d127669eeaedcdcf3dea8a03
data/README.md CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/routes-analyzer.svg)](https://badge.fury.io/rb/routes-analyzer)
4
4
 
5
- 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.
5
+ A Ruby on Rails plugin that tracks and analyzes controller action usage in your application. It uses Redis to store action access patterns and provides insights into which controller actions are being used and which are not.
6
6
 
7
7
  ## Features
8
8
 
9
- - **Route Usage Tracking**: Automatically tracks which routes are accessed and how often
10
- - **Smart Parameter Handling**: Routes with parameters (like `/users/:id`) are properly grouped under a single pattern instead of creating separate entries for each parameter value
9
+ - **Controller Action Tracking**: Automatically tracks which controller actions are accessed and how often
10
+ - **Method-Agnostic Tracking**: Actions are tracked by controller#action regardless of HTTP method (GET, POST, etc.)
11
11
  - **Redis Storage**: Uses Redis to store usage statistics efficiently
12
12
  - **Configurable Timeframe**: Set custom analysis periods (default 30 days)
13
- - **Comprehensive Reporting**: Shows both used and unused routes
13
+ - **Comprehensive Reporting**: Shows both used and unused controller actions
14
14
  - **Rake Tasks**: Easy-to-use commands for viewing statistics and managing data
15
15
 
16
16
  ## Installation
@@ -41,10 +41,10 @@ Routes::Analyzer.configure do |config|
41
41
  # Redis connection URL
42
42
  config.redis_url = ENV['REDIS_URL'] || 'redis://localhost:6379/0'
43
43
 
44
- # Redis key prefix for storing route usage data
44
+ # Redis key prefix for storing action usage data
45
45
  config.redis_key_prefix = 'routes_analyzer'
46
46
 
47
- # Timeframe for route usage analysis in days
47
+ # Timeframe for action usage analysis in days
48
48
  config.timeframe = 30
49
49
  end
50
50
  ```
@@ -59,38 +59,39 @@ Alternatively, you can create the configuration file manually in `config/initial
59
59
 
60
60
  ## Usage
61
61
 
62
- Once installed and configured, the middleware will automatically start tracking route usage. No additional code changes are required.
62
+ Once installed and configured, the middleware will automatically start tracking controller action usage. No additional code changes are required.
63
63
 
64
64
  ### Rake Tasks
65
65
 
66
- #### View Route Usage Statistics
66
+ #### View Controller Action Usage Statistics
67
67
 
68
68
  ```bash
69
69
  bundle exec rake routes:analyzer:usage
70
70
  ```
71
71
 
72
72
  This command shows:
73
- - All routes defined in your `routes.rb` file
74
- - Usage count for each route in the specified timeframe
73
+ - All controller actions available in your application
74
+ - Usage count for each action in the specified timeframe
75
75
  - Last access timestamp
76
- - Summary statistics (total, used, unused routes)
76
+ - Summary statistics (total, used, unused actions)
77
77
 
78
78
  Example output:
79
79
  ```
80
- Routes Usage Analysis (30 days)
80
+ Controller Action Usage Analysis (30 days)
81
81
  ================================================================================
82
82
 
83
- COUNT ROUTE METHOD CONTROLLER#ACTION LAST ACCESSED
83
+ COUNT CONTROLLER#ACTION METHOD LAST ACCESSED
84
84
  --------------------------------------------------------------------------------
85
- 45 /users GET users#index 2025-06-18 14:30
86
- 23 /users/:id GET users#show 2025-06-18 12:15
87
- 12 /posts GET posts#index 2025-06-17 09:45
88
- 0 /admin/reports GET admin/reports#index Never
89
- 0 /api/v1/health GET api/v1/health#check Never
90
-
91
- Total routes: 5
92
- Used routes: 3
93
- Unused routes: 2
85
+ 45 users#index GET 2025-06-18 14:30
86
+ 23 users#show GET 2025-06-18 12:15
87
+ 12 posts#index GET 2025-06-17 09:45
88
+ 5 posts#create POST 2025-06-16 16:20
89
+ 0 admin/reports#index GET Never
90
+ 0 api/v1/health#check GET Never
91
+
92
+ Total actions: 6
93
+ Used actions: 4
94
+ Unused actions: 2
94
95
  ```
95
96
 
96
97
  #### Clear Usage Data
@@ -99,7 +100,7 @@ Unused routes: 2
99
100
  bundle exec rake routes:analyzer:clear
100
101
  ```
101
102
 
102
- Removes all stored route usage data from Redis.
103
+ Removes all stored controller action usage data from Redis.
103
104
 
104
105
  #### Check Configuration
105
106
 
@@ -112,13 +113,13 @@ Displays current configuration and tests Redis connectivity.
112
113
  ## How It Works
113
114
 
114
115
  1. **Middleware Integration**: The plugin automatically adds middleware to your Rails application
115
- 2. **Route Filtering**: Only routes explicitly defined in `routes.rb` are tracked. This ensures that:
116
+ 2. **Controller Action Filtering**: Only controller actions from defined routes in `routes.rb` are tracked. This ensures that:
116
117
  - Undefined routes (404 errors) are not tracked
117
118
  - Catch-all routes that handle unknown paths don't pollute the data
118
- - Only legitimate application routes are analyzed
119
- 3. **Request Tracking**: Each valid HTTP request is analyzed to extract route information
119
+ - Only legitimate application controller actions are analyzed
120
+ 3. **Request Tracking**: Each valid HTTP request is analyzed to extract controller and action information
120
121
  4. **Redis Storage**: Usage data is stored in Redis with the following structure:
121
- - Route path and HTTP method
122
+ - Controller name and action name
122
123
  - Access count within the timeframe
123
124
  - Last access timestamp
124
125
  5. **Data Expiration**: Redis keys automatically expire after the configured timeframe plus a buffer period
@@ -133,35 +134,36 @@ The middleware uses Rails' routing system to determine if a route is valid:
133
134
 
134
135
  This approach ensures that only routes you've intentionally defined are included in the usage analysis.
135
136
 
136
- ## Parameterized Route Handling
137
+ ## Controller Action Tracking
137
138
 
138
- The gem intelligently handles routes with parameters by tracking them under their route pattern rather than individual parameter values:
139
+ The gem tracks usage by controller and action rather than by specific URL paths. This provides more meaningful insights into which parts of your application are being used:
139
140
 
140
- - **Route Definition**: `/users/:id` in `routes.rb`
141
- - **Actual Requests**: `/users/123`, `/users/456`, `/users/789`
142
- - **Tracked As**: Single entry for `/users/:id` with combined statistics
141
+ - **Route Definition**: `resources :users` in `routes.rb`
142
+ - **Actual Requests**: `GET /users/123`, `GET /users/456`, `POST /users/789/update`
143
+ - **Tracked As**: Separate entries for `users#show` and `users#update` actions
143
144
 
144
- This means that accessing `/users/123` three times and `/users/456` two times will show up as 5 total accesses to the `/users/:id` route pattern, not as separate routes.
145
+ This means that accessing different user IDs (e.g., `/users/123`, `/users/456`, `/users/789`) will all be counted under the same `users#show` action, giving you a clear picture of which controller actions are being utilized.
145
146
 
146
147
  **Example Output:**
147
148
  ```
148
- COUNT ROUTE METHOD CONTROLLER#ACTION
149
+ COUNT CONTROLLER#ACTION METHOD LAST ACCESSED
149
150
  --------------------------------------------------------------
150
- 15 /users/:id GET users#show
151
- 8 /users/:id/profile GET users#profile
152
- 23 /posts/:id GET posts#show
151
+ 15 users#show GET 2025-06-18 14:30
152
+ 8 users#update PATCH 2025-06-18 12:15
153
+ 23 posts#show GET 2025-06-17 09:45
153
154
  ```
154
155
 
155
- This grouping provides much more meaningful insights into which route patterns are being used in your application.
156
+ This grouping provides much more meaningful insights into which parts of your application are being used.
156
157
 
157
158
  ## Data Structure
158
159
 
159
- For each tracked route, the following data is stored in Redis:
160
+ For each tracked controller action, the following data is stored in Redis:
160
161
 
161
162
  ```ruby
162
163
  {
163
- route: "/users/:id", # Route pattern
164
- method: "GET", # HTTP method
164
+ controller: "users", # Controller name
165
+ action: "show", # Action name
166
+ method: "GET", # HTTP method (for reference)
165
167
  count: 15, # Number of accesses
166
168
  last_accessed: 1718721600 # Unix timestamp
167
169
  }
@@ -38,7 +38,7 @@ module Routes
38
38
 
39
39
  return unless route_info
40
40
 
41
- redis_key = build_redis_key(route_info[:route], route_info[:method])
41
+ redis_key = build_redis_key(route_info[:controller], route_info[:action])
42
42
  current_time = Time.current
43
43
 
44
44
  configuration.redis_client.multi do |redis|
@@ -48,8 +48,9 @@ module Routes
48
48
  # Update last accessed timestamp
49
49
  redis.hset(redis_key, "last_accessed", current_time.to_i)
50
50
 
51
- # Set route and method info (in case it's the first time)
52
- redis.hset(redis_key, "route", route_info[:route])
51
+ # Set controller#action and method info (in case it's the first time)
52
+ redis.hset(redis_key, "controller", route_info[:controller])
53
+ redis.hset(redis_key, "action", route_info[:action])
53
54
  redis.hset(redis_key, "method", route_info[:method])
54
55
 
55
56
  # Set expiration based on timeframe (add some buffer)
@@ -94,17 +95,10 @@ module Routes
94
95
  controller_name = controller.controller_name
95
96
  method = request.request_method.upcase
96
97
 
97
- # Find the route pattern instead of using the actual path
98
- route_path = find_route_pattern(controller_name, action, method)
99
-
100
- # Fallback to actual path if pattern not found
101
- route_path ||= request.path_info
102
-
103
98
  {
104
- route: route_path,
105
- method: method,
106
99
  controller: controller_name,
107
- action: action
100
+ action: action,
101
+ method: method
108
102
  }
109
103
  end
110
104
  rescue => e
@@ -112,25 +106,8 @@ module Routes
112
106
  nil
113
107
  end
114
108
 
115
- def find_route_pattern(controller_name, action, method)
116
- return nil unless defined?(Rails) && Rails.application
117
-
118
- Rails.application.routes.routes.each do |route|
119
- if route.defaults[:controller] == controller_name &&
120
- route.defaults[:action] == action &&
121
- route.verb == method
122
- return route.path.spec.to_s.gsub(/\(\.:format\)$/, "")
123
- end
124
- end
125
-
126
- nil
127
- rescue => e
128
- Rails.logger.warn "Routes::Analyzer: Failed to find route pattern: #{e.message}"
129
- nil
130
- end
131
-
132
- def build_redis_key(route, method)
133
- "#{configuration.redis_key_prefix}:#{method}:#{route.gsub('/', ':')}"
109
+ def build_redis_key(controller, action)
110
+ "#{configuration.redis_key_prefix}:#{controller}##{action}"
134
111
  end
135
112
 
136
113
  def configuration
@@ -9,7 +9,7 @@ module Routes
9
9
  def get_usage_stats
10
10
  cutoff_time = @configuration.timeframe.days.ago.to_i
11
11
 
12
- # Get all route keys
12
+ # Get all controller#action keys
13
13
  pattern = "#{@configuration.redis_key_prefix}:*"
14
14
  keys = @redis.keys(pattern)
15
15
 
@@ -24,7 +24,8 @@ module Routes
24
24
  next if last_accessed && last_accessed < cutoff_time
25
25
 
26
26
  usage_stats << {
27
- route: route_data["route"],
27
+ controller: route_data["controller"],
28
+ action: route_data["action"],
28
29
  method: route_data["method"],
29
30
  count: route_data["count"]&.to_i || 0,
30
31
  last_accessed: last_accessed ? Time.at(last_accessed) : nil
@@ -35,54 +36,59 @@ module Routes
35
36
  usage_stats.sort_by { |stat| -stat[:count] }
36
37
  end
37
38
 
38
- def get_all_defined_routes
39
+ def get_all_defined_actions
39
40
  return [] unless defined?(Rails) && Rails.application
40
41
 
41
- Rails.application.routes.routes.map do |route|
42
- {
43
- route: route.path.spec.to_s.gsub(/\(\.:format\)$/, ""),
44
- method: route.verb,
42
+ actions = []
43
+ Rails.application.routes.routes.each do |route|
44
+ next unless route.defaults[:controller] && route.defaults[:action]
45
+
46
+ actions << {
45
47
  controller: route.defaults[:controller],
46
48
  action: route.defaults[:action],
49
+ method: route.verb,
50
+ route: route.path.spec.to_s.gsub(/\(\.:format\)$/, ""),
47
51
  name: route.name
48
52
  }
49
- end.compact.uniq { |r| [ r[:route], r[:method] ] }
53
+ end
54
+
55
+ # Remove duplicates based on controller#action (not method-specific)
56
+ actions.uniq { |a| "#{a[:controller]}##{a[:action]}" }
50
57
  end
51
58
 
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]}" }
59
+ def merge_with_defined_actions(usage_stats)
60
+ defined_actions = get_all_defined_actions
61
+ usage_by_action = usage_stats.index_by { |stat| "#{stat[:controller]}##{stat[:action]}" }
55
62
 
56
- merged_routes = []
63
+ merged_actions = []
57
64
 
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]
65
+ defined_actions.each do |defined_action|
66
+ action_key = "#{defined_action[:controller]}##{defined_action[:action]}"
67
+ usage_stat = usage_by_action[action_key]
61
68
 
62
69
  if usage_stat
63
- merged_routes << defined_route.merge(usage_stat)
70
+ merged_actions << defined_action.merge(usage_stat)
64
71
  else
65
- merged_routes << defined_route.merge(
72
+ merged_actions << defined_action.merge(
66
73
  count: 0,
67
74
  last_accessed: nil
68
75
  )
69
76
  end
70
77
  end
71
78
 
72
- # Add any tracked routes that aren't in the defined routes (in case of dynamic routes)
79
+ # Add any tracked actions that aren't in the defined actions (in case of dynamic actions)
73
80
  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,
81
+ action_key = "#{usage_stat[:controller]}##{usage_stat[:action]}"
82
+ unless defined_actions.any? { |da| "#{da[:controller]}##{da[:action]}" == action_key }
83
+ merged_actions << usage_stat.merge(
84
+ route: nil,
79
85
  name: nil
80
86
  )
81
87
  end
82
88
  end
83
89
 
84
- # Sort by count (descending), then by route name
85
- merged_routes.sort_by { |route| [ -route[:count], route[:route] ] }
90
+ # Sort by count (descending), then by controller#action name
91
+ merged_actions.sort_by { |action| [ -action[:count], "#{action[:controller]}##{action[:action]}" ] }
86
92
  end
87
93
  end
88
94
  end
@@ -1,5 +1,5 @@
1
1
  module Routes
2
2
  module Analyzer
3
- VERSION = "0.1.4"
3
+ VERSION = "0.1.5"
4
4
  end
5
5
  end
@@ -1,60 +1,59 @@
1
1
  namespace :routes do
2
2
  namespace :analyzer do
3
- desc "Show routes usage statistics"
3
+ desc "Show controller action usage statistics"
4
4
  task usage: :environment do
5
5
  begin
6
6
  config = Routes::Analyzer.configuration
7
7
  tracker = Routes::Analyzer::RouteUsageTracker.new(config)
8
8
 
9
- puts "Routes Usage Analysis (#{config.timeframe} days)"
9
+ puts "Controller Action Usage Analysis (#{config.timeframe} days)"
10
10
  puts "=" * 80
11
11
  puts
12
12
 
13
13
  if config.valid?
14
14
  usage_stats = tracker.get_usage_stats
15
- merged_routes = tracker.merge_with_defined_routes(usage_stats)
15
+ merged_actions = tracker.merge_with_defined_actions(usage_stats)
16
16
  else
17
- puts "Warning: Redis not configured. Showing defined routes only."
17
+ puts "Warning: Redis not configured. Showing defined actions only."
18
18
  puts
19
- merged_routes = tracker.get_all_defined_routes.map do |route|
20
- route.merge(count: 0, last_accessed: nil)
19
+ merged_actions = tracker.get_all_defined_actions.map do |action|
20
+ action.merge(count: 0, last_accessed: nil)
21
21
  end
22
22
  end
23
23
 
24
- if merged_routes.empty?
25
- puts "No routes found."
24
+ if merged_actions.empty?
25
+ puts "No controller actions found."
26
26
  next
27
27
  end
28
28
 
29
29
  # Print header
30
- printf "%-8s %-40s %-15s %-20s %s\n", "COUNT", "ROUTE", "METHOD", "CONTROLLER#ACTION", "LAST ACCESSED"
30
+ printf "%-8s %-40s %-15s %s\n", "COUNT", "CONTROLLER#ACTION", "METHOD", "LAST ACCESSED"
31
31
  puts "-" * 80
32
32
 
33
- merged_routes.each do |route|
34
- controller_action = if route[:controller] && route[:action]
35
- "#{route[:controller]}##{route[:action]}"
33
+ merged_actions.each do |action|
34
+ controller_action = if action[:controller] && action[:action]
35
+ "#{action[:controller]}##{action[:action]}"
36
36
  else
37
37
  "N/A"
38
38
  end
39
39
 
40
- last_accessed = if route[:last_accessed]
41
- route[:last_accessed].strftime("%Y-%m-%d %H:%M")
40
+ last_accessed = if action[:last_accessed]
41
+ action[:last_accessed].strftime("%Y-%m-%d %H:%M")
42
42
  else
43
43
  "Never"
44
44
  end
45
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],
46
+ printf "%-8d %-40s %-15s %s\n",
47
+ action[:count],
48
+ controller_action[0, 40],
49
+ action[:method],
51
50
  last_accessed
52
51
  end
53
52
 
54
53
  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 }}"
54
+ puts "Total actions: #{merged_actions.count}"
55
+ puts "Used actions: #{merged_actions.count { |a| a[:count] > 0 }}"
56
+ puts "Unused actions: #{merged_actions.count { |a| a[:count] == 0 }}"
58
57
 
59
58
  rescue => e
60
59
  puts "Error: #{e.message}"
@@ -69,7 +68,7 @@ namespace :routes do
69
68
  end
70
69
  end
71
70
 
72
- desc "Clear all routes usage data"
71
+ desc "Clear all controller action usage data"
73
72
  task clear: :environment do
74
73
  begin
75
74
  config = Routes::Analyzer.configuration
@@ -79,9 +78,9 @@ namespace :routes do
79
78
 
80
79
  if keys.any?
81
80
  redis.del(*keys)
82
- puts "Cleared #{keys.count} route usage records."
81
+ puts "Cleared #{keys.count} controller action usage records."
83
82
  else
84
- puts "No route usage data found to clear."
83
+ puts "No controller action usage data found to clear."
85
84
  end
86
85
 
87
86
  rescue => e
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: routes-analyzer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregorio Galante