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 +4 -4
 - data/README.md +43 -41
 - data/lib/routes/analyzer/middleware.rb +8 -31
 - data/lib/routes/analyzer/route_usage_tracker.rb +31 -25
 - data/lib/routes/analyzer/version.rb +1 -1
 - data/lib/tasks/routes/analyzer_tasks.rake +24 -25
 - metadata +1 -1
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 3cae3e4dca655b13929955c6357b4351607dd32ae29331c643666e6d05562e32
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 3b533755ae2bc4b810708ac9d705085f5467c3cb94714a458baa6aaa8f5eaebf
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: d47e92d852c70f7ce7108777532aea5f5650f562fe31607d522b178bc206ed8430f713408cb8e28989d38ff8c66c3c5f64bf4ac87407e928bff9f450b7189255
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: d3c981912a2b4894895f4f19d80842d878f6aa1edb1ec157a34e05d54e8ef67cd7c490ed055ef7d1bfd8187277e0f4d7eb923d52d127669eeaedcdcf3dea8a03
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -2,15 +2,15 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            [](https://badge.fury.io/rb/routes-analyzer)
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            A Ruby on Rails plugin that tracks and analyzes  
     | 
| 
      
 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 
     | 
    
         
            -
            - ** 
     | 
| 
       10 
     | 
    
         
            -
            - ** 
     | 
| 
      
 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  
     | 
| 
      
 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  
     | 
| 
      
 44 
     | 
    
         
            +
              # Redis key prefix for storing action usage data
         
     | 
| 
       45 
45 
     | 
    
         
             
              config.redis_key_prefix = 'routes_analyzer'
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
              # Timeframe for  
     | 
| 
      
 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  
     | 
| 
      
 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  
     | 
| 
      
 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  
     | 
| 
       74 
     | 
    
         
            -
            - Usage count for each  
     | 
| 
      
 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  
     | 
| 
      
 76 
     | 
    
         
            +
            - Summary statistics (total, used, unused actions)
         
     | 
| 
       77 
77 
     | 
    
         | 
| 
       78 
78 
     | 
    
         
             
            Example output:
         
     | 
| 
       79 
79 
     | 
    
         
             
            ```
         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
      
 80 
     | 
    
         
            +
            Controller Action Usage Analysis (30 days)
         
     | 
| 
       81 
81 
     | 
    
         
             
            ================================================================================
         
     | 
| 
       82 
82 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
            COUNT     
     | 
| 
      
 83 
     | 
    
         
            +
            COUNT    CONTROLLER#ACTION                        METHOD          LAST ACCESSED
         
     | 
| 
       84 
84 
     | 
    
         
             
            --------------------------------------------------------------------------------
         
     | 
| 
       85 
     | 
    
         
            -
            45        
     | 
| 
       86 
     | 
    
         
            -
            23        
     | 
| 
       87 
     | 
    
         
            -
            12        
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
            0        / 
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
      
 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  
     | 
| 
      
 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. ** 
     | 
| 
      
 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  
     | 
| 
       119 
     | 
    
         
            -
            3. **Request Tracking**: Each valid HTTP request is analyzed to extract  
     | 
| 
      
 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 
     | 
    
         
            -
               -  
     | 
| 
      
 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 
     | 
    
         
            -
            ##  
     | 
| 
      
 137 
     | 
    
         
            +
            ## Controller Action Tracking
         
     | 
| 
       137 
138 
     | 
    
         | 
| 
       138 
     | 
    
         
            -
            The gem  
     | 
| 
      
 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**:  
     | 
| 
       141 
     | 
    
         
            -
            - **Actual Requests**:  
     | 
| 
       142 
     | 
    
         
            -
            - **Tracked As**:  
     | 
| 
      
 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 
     | 
| 
      
 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     
     | 
| 
      
 149 
     | 
    
         
            +
            COUNT    CONTROLLER#ACTION       METHOD    LAST ACCESSED
         
     | 
| 
       149 
150 
     | 
    
         
             
            --------------------------------------------------------------
         
     | 
| 
       150 
     | 
    
         
            -
            15        
     | 
| 
       151 
     | 
    
         
            -
            8         
     | 
| 
       152 
     | 
    
         
            -
            23        
     | 
| 
      
 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  
     | 
| 
      
 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  
     | 
| 
      
 160 
     | 
    
         
            +
            For each tracked controller action, the following data is stored in Redis:
         
     | 
| 
       160 
161 
     | 
    
         | 
| 
       161 
162 
     | 
    
         
             
            ```ruby
         
     | 
| 
       162 
163 
     | 
    
         
             
            {
         
     | 
| 
       163 
     | 
    
         
            -
               
     | 
| 
       164 
     | 
    
         
            -
               
     | 
| 
      
 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[: 
     | 
| 
      
 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  
     | 
| 
       52 
     | 
    
         
            -
                      redis.hset(redis_key, " 
     | 
| 
      
 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  
     | 
| 
       116 
     | 
    
         
            -
                     
     | 
| 
       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  
     | 
| 
      
 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 
     | 
    
         
            -
                         
     | 
| 
      
 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  
     | 
| 
      
 39 
     | 
    
         
            +
                  def get_all_defined_actions
         
     | 
| 
       39 
40 
     | 
    
         
             
                    return [] unless defined?(Rails) && Rails.application
         
     | 
| 
       40 
41 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
                     
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
      
 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 
     | 
| 
      
 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  
     | 
| 
       53 
     | 
    
         
            -
                     
     | 
| 
       54 
     | 
    
         
            -
                     
     | 
| 
      
 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 
     | 
    
         
            -
                     
     | 
| 
      
 63 
     | 
    
         
            +
                    merged_actions = []
         
     | 
| 
       57 
64 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
                     
     | 
| 
       59 
     | 
    
         
            -
                       
     | 
| 
       60 
     | 
    
         
            -
                      usage_stat =  
     | 
| 
      
 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 
     | 
    
         
            -
                         
     | 
| 
      
 70 
     | 
    
         
            +
                        merged_actions << defined_action.merge(usage_stat)
         
     | 
| 
       64 
71 
     | 
    
         
             
                      else
         
     | 
| 
       65 
     | 
    
         
            -
                         
     | 
| 
      
 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  
     | 
| 
      
 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 
     | 
    
         
            -
                       
     | 
| 
       75 
     | 
    
         
            -
                      unless  
     | 
| 
       76 
     | 
    
         
            -
                         
     | 
| 
       77 
     | 
    
         
            -
                           
     | 
| 
       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  
     | 
| 
       85 
     | 
    
         
            -
                     
     | 
| 
      
 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,60 +1,59 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            namespace :routes do
         
     | 
| 
       2 
2 
     | 
    
         
             
              namespace :analyzer do
         
     | 
| 
       3 
     | 
    
         
            -
                desc "Show  
     | 
| 
      
 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 " 
     | 
| 
      
 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 
     | 
    
         
            -
                       
     | 
| 
      
 15 
     | 
    
         
            +
                      merged_actions = tracker.merge_with_defined_actions(usage_stats)
         
     | 
| 
       16 
16 
     | 
    
         
             
                    else
         
     | 
| 
       17 
     | 
    
         
            -
                      puts "Warning: Redis not configured. Showing defined  
     | 
| 
      
 17 
     | 
    
         
            +
                      puts "Warning: Redis not configured. Showing defined actions only."
         
     | 
| 
       18 
18 
     | 
    
         
             
                      puts
         
     | 
| 
       19 
     | 
    
         
            -
                       
     | 
| 
       20 
     | 
    
         
            -
                         
     | 
| 
      
 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  
     | 
| 
       25 
     | 
    
         
            -
                      puts "No  
     | 
| 
      
 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  
     | 
| 
      
 30 
     | 
    
         
            +
                    printf "%-8s %-40s %-15s %s\n", "COUNT", "CONTROLLER#ACTION", "METHOD", "LAST ACCESSED"
         
     | 
| 
       31 
31 
     | 
    
         
             
                    puts "-" * 80
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
                     
     | 
| 
       34 
     | 
    
         
            -
                      controller_action = if  
     | 
| 
       35 
     | 
    
         
            -
                        "#{ 
     | 
| 
      
 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  
     | 
| 
       41 
     | 
    
         
            -
                         
     | 
| 
      
 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  
     | 
| 
       47 
     | 
    
         
            -
                              
     | 
| 
       48 
     | 
    
         
            -
                              
     | 
| 
       49 
     | 
    
         
            -
                              
     | 
| 
       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  
     | 
| 
       56 
     | 
    
         
            -
                    puts "Used  
     | 
| 
       57 
     | 
    
         
            -
                    puts "Unused  
     | 
| 
      
 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  
     | 
| 
      
 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}  
     | 
| 
      
 81 
     | 
    
         
            +
                      puts "Cleared #{keys.count} controller action usage records."
         
     | 
| 
       83 
82 
     | 
    
         
             
                    else
         
     | 
| 
       84 
     | 
    
         
            -
                      puts "No  
     | 
| 
      
 83 
     | 
    
         
            +
                      puts "No controller action usage data found to clear."
         
     | 
| 
       85 
84 
     | 
    
         
             
                    end
         
     | 
| 
       86 
85 
     | 
    
         | 
| 
       87 
86 
     | 
    
         
             
                  rescue => e
         
     |