ruby_routes 2.1.0 → 2.3.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +232 -162
  3. data/lib/ruby_routes/constant.rb +137 -18
  4. data/lib/ruby_routes/lru_strategies/hit_strategy.rb +31 -4
  5. data/lib/ruby_routes/lru_strategies/miss_strategy.rb +21 -0
  6. data/lib/ruby_routes/node.rb +82 -41
  7. data/lib/ruby_routes/radix_tree/finder.rb +164 -0
  8. data/lib/ruby_routes/radix_tree/inserter.rb +98 -0
  9. data/lib/ruby_routes/radix_tree.rb +83 -142
  10. data/lib/ruby_routes/route/check_helpers.rb +109 -0
  11. data/lib/ruby_routes/route/constraint_validator.rb +159 -0
  12. data/lib/ruby_routes/route/param_support.rb +202 -0
  13. data/lib/ruby_routes/route/path_builder.rb +86 -0
  14. data/lib/ruby_routes/route/path_generation.rb +102 -0
  15. data/lib/ruby_routes/route/query_helpers.rb +56 -0
  16. data/lib/ruby_routes/route/segment_compiler.rb +163 -0
  17. data/lib/ruby_routes/route/small_lru.rb +96 -17
  18. data/lib/ruby_routes/route/validation_helpers.rb +151 -0
  19. data/lib/ruby_routes/route/warning_helpers.rb +54 -0
  20. data/lib/ruby_routes/route.rb +121 -451
  21. data/lib/ruby_routes/route_set/cache_helpers.rb +174 -0
  22. data/lib/ruby_routes/route_set/collection_helpers.rb +127 -0
  23. data/lib/ruby_routes/route_set.rb +126 -148
  24. data/lib/ruby_routes/router/build_helpers.rb +100 -0
  25. data/lib/ruby_routes/router/builder.rb +96 -0
  26. data/lib/ruby_routes/router/http_helpers.rb +135 -0
  27. data/lib/ruby_routes/router/resource_helpers.rb +137 -0
  28. data/lib/ruby_routes/router/scope_helpers.rb +109 -0
  29. data/lib/ruby_routes/router.rb +196 -179
  30. data/lib/ruby_routes/segment.rb +28 -8
  31. data/lib/ruby_routes/segments/base_segment.rb +40 -4
  32. data/lib/ruby_routes/segments/dynamic_segment.rb +48 -12
  33. data/lib/ruby_routes/segments/static_segment.rb +43 -7
  34. data/lib/ruby_routes/segments/wildcard_segment.rb +56 -12
  35. data/lib/ruby_routes/string_extensions.rb +52 -15
  36. data/lib/ruby_routes/url_helpers.rb +106 -24
  37. data/lib/ruby_routes/utility/inflector_utility.rb +35 -0
  38. data/lib/ruby_routes/utility/key_builder_utility.rb +179 -0
  39. data/lib/ruby_routes/utility/method_utility.rb +137 -0
  40. data/lib/ruby_routes/utility/path_utility.rb +89 -0
  41. data/lib/ruby_routes/utility/route_utility.rb +49 -0
  42. data/lib/ruby_routes/version.rb +3 -1
  43. data/lib/ruby_routes.rb +68 -11
  44. metadata +30 -7
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyRoutes
4
+ class RouteSet
5
+ # CacheHelpers: extracted cache, request-key, and eviction logic to reduce
6
+ # the size of the main RouteSet class.
7
+ #
8
+ # This module provides methods for managing caches, request keys, and
9
+ # implementing eviction policies for route recognition.
10
+ module CacheHelpers
11
+ # Recognition cache statistics.
12
+ #
13
+ # @return [Hash] A hash containing:
14
+ # - `:hits` [Integer] The number of cache hits.
15
+ # - `:misses` [Integer] The number of cache misses.
16
+ # - `:hit_rate` [Float] The cache hit rate as a percentage.
17
+ # - `:size` [Integer] The current size of the recognition cache.
18
+ def cache_stats
19
+ total_requests = @cache_hits + @cache_misses
20
+ {
21
+ hits: @cache_hits,
22
+ misses: @cache_misses,
23
+ hit_rate: total_requests.zero? ? 0.0 : (@cache_hits.to_f / total_requests * 100.0),
24
+ size: @recognition_cache.size
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ # Set up caches and request-key ring.
31
+ #
32
+ # Initializes the internal data structures for managing routes, named routes,
33
+ # recognition cache, and request-key ring buffer.
34
+ #
35
+ # @return [void]
36
+ def setup_caches
37
+ @routes = []
38
+ @named_routes = {}
39
+ @recognition_cache = {}
40
+ @recognition_cache_max = 2048
41
+ @cache_hits = 0
42
+ @cache_misses = 0
43
+ @request_key_pool = {}
44
+ @request_key_ring = Array.new(RubyRoutes::Constant::REQUEST_KEY_CAPACITY)
45
+ @entry_count = 0
46
+ @ring_index = 0
47
+ end
48
+
49
+ # Fetch (or build) a composite request cache key with ring-buffer eviction.
50
+ #
51
+ # Ensures consistent use of frozen method/path keys to avoid mixed key space bugs.
52
+ #
53
+ # @param http_method [String, Symbol] The HTTP method (e.g., `:get`, `:post`).
54
+ # @param request_path [String] The request path.
55
+ # @return [String] The composite request key.
56
+ def fetch_request_key(http_method, request_path)
57
+ method_key, path_key = normalize_keys(http_method, request_path)
58
+ composite_key = build_composite_key(method_key, path_key)
59
+
60
+ return composite_key if handle_cache_hit(method_key, path_key, composite_key)
61
+
62
+ handle_cache_miss(method_key, path_key, composite_key)
63
+ composite_key
64
+ end
65
+
66
+ # Normalize keys.
67
+ #
68
+ # Converts the HTTP method and request path into frozen strings for consistent
69
+ # key usage.
70
+ #
71
+ # @param http_method [String, Symbol] The HTTP method.
72
+ # @param request_path [String] The request path.
73
+ # @return [Array<String>] An array containing the normalized method and path keys.
74
+ def normalize_keys(http_method, request_path)
75
+ method_key = http_method.is_a?(String) ? http_method.upcase.freeze : http_method.to_s.upcase.freeze
76
+ path_key = request_path.is_a?(String) ? request_path.freeze : request_path.to_s.freeze
77
+ [method_key, path_key]
78
+ end
79
+
80
+ # Build composite key.
81
+ #
82
+ # Combines the HTTP method and path into a single composite key.
83
+ #
84
+ # @param method_key [String] The normalized HTTP method key.
85
+ # @param path_key [String] The normalized path key.
86
+ # @return [String] The composite key.
87
+ def build_composite_key(method_key, path_key)
88
+ "#{method_key}:#{path_key}".freeze
89
+ end
90
+
91
+ # Handle cache hit.
92
+ #
93
+ # Checks if the composite key already exists in the request key pool.
94
+ #
95
+ # @param method_key [String] The normalized HTTP method key.
96
+ # @param path_key [String] The normalized path key.
97
+ # @param _composite_key [String] The composite key (unused).
98
+ # @return [Boolean] `true` if the key exists, `false` otherwise.
99
+ def handle_cache_hit(method_key, path_key, _composite_key)
100
+ return true if @request_key_pool[method_key]&.key?(path_key)
101
+
102
+ false
103
+ end
104
+
105
+ # Handle cache miss.
106
+ #
107
+ # Adds the composite key to the request key pool and manages the ring buffer
108
+ # for eviction.
109
+ #
110
+ # @param method_key [String] The normalized HTTP method key.
111
+ # @param path_key [String] The normalized path key.
112
+ # @param composite_key [String] The composite key.
113
+ # @return [void]
114
+ def handle_cache_miss(method_key, path_key, composite_key)
115
+ @request_key_pool[method_key][path_key] = composite_key if @request_key_pool[method_key]
116
+ @request_key_pool[method_key] = { path_key => composite_key } unless @request_key_pool[method_key]
117
+
118
+ if @entry_count < RubyRoutes::Constant::REQUEST_KEY_CAPACITY
119
+ @request_key_ring[@entry_count] = [method_key, path_key]
120
+ @entry_count += 1
121
+ else
122
+ evict_old_entry(method_key, path_key)
123
+ end
124
+ end
125
+
126
+ # Evict old entry.
127
+ #
128
+ # Removes the oldest entry from the request key pool and updates the ring buffer.
129
+ #
130
+ # @param method_key [String] The normalized HTTP method key.
131
+ # @param path_key [String] The normalized path key.
132
+ # @return [void]
133
+ def evict_old_entry(method_key, path_key)
134
+ evict_method, evict_path = @request_key_ring[@ring_index]
135
+ if (evict_bucket = @request_key_pool[evict_method]) && evict_bucket.delete(evict_path) && evict_bucket.empty?
136
+ @request_key_pool.delete(evict_method)
137
+ end
138
+ @request_key_ring[@ring_index] = [method_key, path_key]
139
+ @ring_index += 1
140
+ @ring_index = 0 if @ring_index == RubyRoutes::Constant::REQUEST_KEY_CAPACITY
141
+ end
142
+
143
+ # Fetch cached recognition entry while updating hit counter.
144
+ #
145
+ # @param lookup_key [String] The cache lookup key.
146
+ # @return [Hash, nil] The cached recognition entry, or `nil` if not found.
147
+ def fetch_cached_recognition(lookup_key)
148
+ if (cached_result = @recognition_cache[lookup_key])
149
+ @cache_hits += 1
150
+ return cached_result
151
+ end
152
+ @cache_misses += 1
153
+ nil
154
+ end
155
+
156
+ # Cache insertion with simple segment eviction (25% oldest).
157
+ #
158
+ # Adds a new entry to the recognition cache, evicting the oldest 25% of entries
159
+ # if the cache exceeds its maximum size.
160
+ #
161
+ # @param cache_key [String] The cache key.
162
+ # @param entry [Hash] The cache entry.
163
+ # @return [void]
164
+ def insert_cache_entry(cache_key, entry)
165
+ if @recognition_cache.size >= @recognition_cache_max
166
+ @recognition_cache.keys.first(@recognition_cache_max / 4).each do |evict_key|
167
+ @recognition_cache.delete(evict_key)
168
+ end
169
+ end
170
+ @recognition_cache[cache_key] = entry
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyRoutes
4
+ class RouteSet
5
+ # CollectionHelpers: extracted route collection and enumeration helpers
6
+ # to keep RouteSet implementation small.
7
+ #
8
+ # This module provides methods for managing and querying routes within a
9
+ # `RouteSet`. It includes functionality for adding, finding, clearing, and
10
+ # enumerating routes, as well as managing named routes and caches.
11
+ module CollectionHelpers
12
+ # Add a route object to internal structures.
13
+ #
14
+ # This method adds a route to the internal route collection, updates the
15
+ # radix tree for fast path/method lookups, and registers the route in the
16
+ # named routes collection if it has a name.
17
+ #
18
+ # @param route [Route] The route to add.
19
+ # @return [Route] The added route.
20
+ def add_to_collection(route)
21
+ @routes << route
22
+ @radix_tree.add(route.path, route.methods, route)
23
+ @named_routes[route.name] = route if route.named?
24
+ route
25
+ end
26
+ alias add_route add_to_collection
27
+
28
+ # Register a newly created Route (called from RouteUtility#define).
29
+ #
30
+ # This method initializes the route collection if it is not already set
31
+ # and adds the given route to the collection.
32
+ #
33
+ # @param route [Route] The route to register.
34
+ # @return [Route] The registered route.
35
+ def register(route)
36
+ (@routes ||= []) << route
37
+ route
38
+ end
39
+
40
+ # Find any route (no params) for a method/path.
41
+ #
42
+ # This method searches the radix tree for a route matching the given HTTP
43
+ # method and path.
44
+ #
45
+ # @param http_method [String, Symbol] The HTTP method (e.g., `:get`, `:post`).
46
+ # @param path [String] The path to match.
47
+ # @return [Route, nil] The matching route, or `nil` if no match is found.
48
+ def find_route(http_method, path)
49
+ route, _params = @radix_tree.find(path, http_method)
50
+ route
51
+ end
52
+
53
+ # Retrieve a named route.
54
+ #
55
+ # This method retrieves a route by its name from the named routes collection.
56
+ # If no route is found, it raises a `RouteNotFound` error.
57
+ #
58
+ # @param name [Symbol, String] The name of the route.
59
+ # @return [Route] The named route.
60
+ # @raise [RouteNotFound] If no route with the given name is found.
61
+ def find_named_route(name)
62
+ route = @named_routes[name]
63
+ raise RouteNotFound, "No route named '#{name}'" unless route
64
+
65
+ route
66
+ end
67
+
68
+ # Clear all routes and caches.
69
+ #
70
+ # This method clears the internal route collection, named routes, recognition
71
+ # cache, and radix tree. It also resets cache hit/miss counters and the
72
+ # request key pool.
73
+ #
74
+ # @return [void]
75
+ def clear!
76
+ @routes.clear
77
+ @named_routes.clear
78
+ @recognition_cache.clear
79
+ @cache_hits = 0
80
+ @cache_misses = 0
81
+ @radix_tree = RadixTree.new
82
+ @request_key_pool.clear
83
+ @request_key_ring.fill(nil)
84
+ @entry_count = 0
85
+ @ring_index = 0
86
+ end
87
+
88
+ # Get the number of routes.
89
+ #
90
+ # @return [Integer] The number of routes in the collection.
91
+ def size
92
+ @routes.size
93
+ end
94
+
95
+ # Check if the route collection is empty.
96
+ #
97
+ # @return [Boolean] `true` if the collection is empty, `false` otherwise.
98
+ def empty?
99
+ @routes.empty?
100
+ end
101
+
102
+ # Enumerate routes.
103
+ #
104
+ # This method yields each route in the collection to the given block. If no
105
+ # block is provided, it returns an enumerator.
106
+ #
107
+ # @yield [route] Yields each route in the collection.
108
+ # @return [Enumerator, self] An enumerator if no block is given, or `self`.
109
+ def each(&block)
110
+ return enum_for(:each) unless block
111
+
112
+ @routes.each(&block)
113
+ self
114
+ end
115
+
116
+ # Test membership.
117
+ #
118
+ # This method checks if the given route is included in the route collection.
119
+ #
120
+ # @param route [Route] The route to check.
121
+ # @return [Boolean] `true` if the route is in the collection, `false` otherwise.
122
+ def include?(route)
123
+ @routes.include?(route)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -1,192 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'utility/key_builder_utility'
4
+ require_relative 'utility/method_utility'
5
+ require_relative 'route_set/cache_helpers'
6
+ require_relative 'route_set/collection_helpers'
7
+ require_relative 'route/param_support'
8
+
1
9
  module RubyRoutes
10
+ # RouteSet
11
+ #
12
+ # Collection + lookup facade for Route instances.
13
+ #
14
+ # Responsibilities:
15
+ # - Hold all defined routes (ordered).
16
+ # - Index named routes.
17
+ # - Provide fast recognition (method + path → route, params) with
18
+ # a small in‑memory recognition cache.
19
+ # - Delegate structural path matching to an internal RadixTree.
20
+ #
21
+ # Thread safety: not thread‑safe; build during boot, read per request.
22
+ #
23
+ # @api public (primary integration surface)
2
24
  class RouteSet
3
25
  attr_reader :routes
4
26
 
5
- def initialize
6
- @tree = RubyRoutes::RadixTree.new
7
- @named_routes = {}
8
- @routes = []
9
- # Optimized recognition cache with better data structures
10
- @recognition_cache = {}
11
- @cache_hits = 0
12
- @cache_misses = 0
13
- @recognition_cache_max = 8192 # larger for better hit rates
14
- end
15
-
16
- def add_route(route)
17
- @routes << route
18
- @tree.add(route.path, route.methods, route)
19
- @named_routes[route.name] = route if route.named?
20
- # Clear recognition cache when routes change
21
- @recognition_cache.clear if @recognition_cache.size > 100
22
- route
23
- end
27
+ include RubyRoutes::Utility::KeyBuilderUtility
28
+ include RubyRoutes::Utility::MethodUtility
29
+ include RubyRoutes::RouteSet::CacheHelpers
30
+ include RubyRoutes::Route::ParamSupport
31
+ include RubyRoutes::RouteSet::CollectionHelpers
24
32
 
25
- def find_route(request_method, request_path)
26
- # Optimized: avoid repeated string allocation
27
- method_up = request_method.to_s.upcase
28
- handler, _params = @tree.find(request_path, method_up)
29
- handler
30
- end
31
-
32
- def find_named_route(name)
33
- route = @named_routes[name]
34
- return route if route
35
- raise RouteNotFound, "No route named '#{name}'"
33
+ # Initialize empty collection and caches.
34
+ #
35
+ # @return [void]
36
+ def initialize
37
+ setup_caches
38
+ setup_radix_tree
36
39
  end
37
40
 
38
- def match(request_method, request_path)
39
- # Fast path: normalize method once
40
- method_up = method_lookup(request_method)
41
+ # Recognize a request (method + path) returning route + params.
42
+ #
43
+ # @param http_method [String, Symbol] The HTTP method (e.g., "GET").
44
+ # @param path [String] The request path.
45
+ # @return [Hash, nil] A hash containing the matched route and parameters, or `nil` if no match is found.
46
+ def match(http_method, path)
47
+ normalized_method = normalize_method_for_match(http_method)
48
+ raw_path = path.to_s
49
+ lookup_key = cache_key_for_request(normalized_method, raw_path)
41
50
 
42
- # Optimized cache key: avoid string interpolation when possible
43
- cache_key = build_cache_key(method_up, request_path)
44
-
45
- # Cache hit: return immediately (cached result includes full structure)
46
- if (cached_result = @recognition_cache[cache_key])
47
- @cache_hits += 1
51
+ if (cached_result = fetch_cached_recognition(lookup_key))
48
52
  return cached_result
49
53
  end
50
54
 
51
- @cache_misses += 1
52
-
53
- # Use thread-local params to avoid allocations
54
- params = get_thread_local_params
55
- handler, _ = @tree.find(request_path, method_up, params)
56
- return nil unless handler
57
-
58
- route = handler
59
-
60
- # Fast path: merge defaults only if they exist
61
- merge_defaults(route, params) if route.defaults && !route.defaults.empty?
62
-
63
- # Fast path: parse query params only if needed
64
- if request_path.include?('?')
65
- merge_query_params(route, request_path, params)
66
- end
67
-
68
- # Create return hash and cache the complete result
69
- result_params = params.dup
70
- result = {
71
- route: route,
72
- params: result_params,
73
- controller: route.controller,
74
- action: route.action
75
- }.freeze
76
-
77
- insert_cache_entry(cache_key, result)
55
+ result = perform_match(normalized_method, raw_path)
56
+ insert_cache_entry(lookup_key, result) if result
78
57
  result
79
58
  end
80
59
 
81
- def recognize_path(path, method = :get)
60
+ # Convenience alias for Rack‑style recognizer.
61
+ #
62
+ # @param path [String] The request path.
63
+ # @param method [String, Symbol] The HTTP method (default: "GET").
64
+ # @return [Hash, nil] A hash containing the matched route and parameters, or `nil` if no match is found.
65
+ def recognize_path(path, method = 'GET')
82
66
  match(method, path)
83
67
  end
84
68
 
69
+ # Generate path via named route.
70
+ #
71
+ # @param name [Symbol, String] The name of the route.
72
+ # @param params [Hash] The parameters for path generation.
73
+ # @return [String] The generated path.
85
74
  def generate_path(name, params = {})
86
- route = @named_routes[name]
87
- if route
88
- route.generate_path(params)
89
- else
90
- raise RouteNotFound, "No route named '#{name}'"
91
- end
75
+ route = find_named_route(name)
76
+ route.generate_path(params)
92
77
  end
93
78
 
79
+ # Generate path from a direct route reference.
80
+ #
81
+ # @param route [Route] The route instance.
82
+ # @param params [Hash] The parameters for path generation.
83
+ # @return [String] The generated path.
94
84
  def generate_path_from_route(route, params = {})
95
85
  route.generate_path(params)
96
86
  end
97
87
 
98
- def clear!
99
- @routes.clear
100
- @named_routes.clear
101
- @recognition_cache.clear
102
- @tree = RadixTree.new
103
- @cache_hits = @cache_misses = 0
104
- end
88
+ private
105
89
 
106
- def size
107
- @routes.size
90
+ # Set up the radix tree for structural path matching.
91
+ #
92
+ # @return [void]
93
+ def setup_radix_tree
94
+ @radix_tree = RadixTree.new
108
95
  end
109
- alias_method :length, :size
110
96
 
111
- def empty?
112
- @routes.empty?
97
+ # Normalize the HTTP method for matching.
98
+ #
99
+ # @param http_method [String, Symbol] The HTTP method.
100
+ # @return [String] The normalized HTTP method.
101
+ def normalize_method_for_match(http_method)
102
+ if http_method.is_a?(String) && normalize_http_method(http_method).equal?(http_method)
103
+ http_method
104
+ else
105
+ normalize_http_method(http_method)
106
+ end
113
107
  end
114
108
 
115
- def each(&block)
116
- return enum_for(:each) unless block_given?
117
- @routes.each(&block)
118
- end
109
+ # Perform the route matching process.
110
+ #
111
+ # @param normalized_method [String] The normalized HTTP method.
112
+ # @param raw_path [String] The raw request path.
113
+ # @return [Hash, nil] A hash containing the matched route and parameters, or `nil` if no match is found.
114
+ def perform_match(normalized_method, raw_path)
115
+ path_without_query, _query = raw_path.split('?', 2)
116
+ matched_route, extracted_params = @radix_tree.find(path_without_query, normalized_method)
117
+ return nil unless matched_route
119
118
 
120
- def include?(route)
121
- @routes.include?(route)
122
- end
119
+ # Ensure we have a mutable hash for merging defaults / query params.
120
+ extracted_params = extracted_params.dup if extracted_params&.frozen?
123
121
 
124
- # Performance monitoring
125
- def cache_stats
126
- total = @cache_hits + @cache_misses
127
- hit_rate = total > 0 ? (@cache_hits.to_f / total * 100).round(2) : 0
128
- {
129
- hits: @cache_hits,
130
- misses: @cache_misses,
131
- hit_rate: "#{hit_rate}%",
132
- size: @recognition_cache.size
133
- }
122
+ merge_query_params(matched_route, raw_path, extracted_params)
123
+ merge_defaults(matched_route, extracted_params)
124
+ build_match_result(matched_route, extracted_params)
134
125
  end
135
126
 
136
- private
127
+ # Merge default parameters into the extracted parameters.
128
+ #
129
+ # @param matched_route [Route] The matched route.
130
+ # @param extracted_params [Hash] The extracted parameters.
131
+ # @return [void]
132
+ def merge_defaults(matched_route, extracted_params)
133
+ return unless matched_route.respond_to?(:defaults) && matched_route.defaults
137
134
 
138
- # Method lookup table to avoid repeated upcasing with interned strings
139
- def method_lookup(method)
140
- @method_cache ||= Hash.new { |h, k| h[k] = k.to_s.upcase.freeze }
141
- @method_cache[method]
135
+ matched_route.defaults.each { |key, value| extracted_params[key] = value unless extracted_params.key?(key) }
142
136
  end
143
137
 
144
- # Optimized cache key building - avoid string interpolation
145
- def build_cache_key(method, path)
146
- # String interpolation creates a new string directly without intermediate allocations
147
- "#{method}:#{path}"
138
+ # Build the match result hash.
139
+ #
140
+ # @param matched_route [Route] The matched route.
141
+ # @param extracted_params [Hash] The extracted parameters.
142
+ # @return [Hash] A hash containing the matched route, parameters, controller, and action.
143
+ def build_match_result(matched_route, extracted_params)
144
+ {
145
+ route: matched_route,
146
+ params: extracted_params,
147
+ controller: matched_route.controller,
148
+ action: matched_route.action
149
+ }
148
150
  end
149
151
 
150
- # Get thread-local params hash, reusing when possible
151
- def get_thread_local_params
152
- # Use single thread-local hash that gets cleared, avoiding pool management overhead
153
- hash = Thread.current[:ruby_routes_params_hash] ||= {}
154
- hash.clear
155
- hash
152
+ # Obtain a pooled hash for temporary parameters.
153
+ #
154
+ # @return [Hash] A thread-local hash for temporary parameter storage.
155
+ def thread_local_params
156
+ thread_params = Thread.current[:ruby_routes_params_pool] ||= []
157
+ thread_params.empty? ? {} : thread_params.pop.clear
156
158
  end
157
159
 
160
+ # Return a parameters hash to the thread-local pool.
161
+ #
162
+ # @param params [Hash] The parameters hash to return.
163
+ # @return [void]
158
164
  def return_params_to_pool(params)
159
- # No-op since we're using a single reusable hash per thread
160
- end
161
-
162
- # Fast defaults merging
163
- def merge_defaults(route, params)
164
- route.defaults.each do |key, value|
165
- params[key] = value unless params.key?(key)
166
- end
167
- end
168
-
169
- # Fast query params merging
170
- def merge_query_params(route, request_path, params)
171
- if route.respond_to?(:parse_query_params)
172
- qp = route.parse_query_params(request_path)
173
- params.merge!(qp) unless qp.empty?
174
- elsif route.respond_to?(:query_params)
175
- qp = route.query_params(request_path)
176
- params.merge!(qp) unless qp.empty?
177
- end
178
- end
179
-
180
- # Efficient cache insertion with LRU eviction
181
- def insert_cache_entry(cache_key, cache_entry)
182
- @recognition_cache[cache_key] = cache_entry
183
-
184
- # Simple eviction: clear cache when it gets too large
185
- if @recognition_cache.size > @recognition_cache_max
186
- # Keep most recently used half
187
- keys_to_delete = @recognition_cache.keys[0...(@recognition_cache_max / 2)]
188
- keys_to_delete.each { |k| @recognition_cache.delete(k) }
189
- end
165
+ params.clear
166
+ thread_pool = Thread.current[:ruby_routes_params_pool] ||= []
167
+ thread_pool << params if thread_pool.size < 10 # Limit pool size
190
168
  end
191
169
  end
192
170
  end