ruby_routes 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/lib/ruby_routes/constant.rb +57 -0
- data/lib/ruby_routes/lru_strategies/hit_strategy.rb +13 -0
- data/lib/ruby_routes/lru_strategies/miss_strategy.rb +10 -0
- data/lib/ruby_routes/node.rb +32 -0
- data/lib/ruby_routes/radix_tree.rb +18 -33
- data/lib/ruby_routes/route/small_lru.rb +43 -0
- data/lib/ruby_routes/route.rb +47 -59
- data/lib/ruby_routes/route_set.rb +37 -13
- data/lib/ruby_routes/segment.rb +20 -0
- data/lib/ruby_routes/segments/base_segment.rb +21 -0
- data/lib/ruby_routes/segments/dynamic_segment.rb +23 -0
- data/lib/ruby_routes/segments/static_segment.rb +18 -0
- data/lib/ruby_routes/segments/wildcard_segment.rb +27 -0
- data/lib/ruby_routes/version.rb +1 -1
- metadata +10 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02d39aae113654256dae0905438ac98a540854a5d131dea6c4b2b606b51bf4ba
|
4
|
+
data.tar.gz: ba01a85fc21713387e76c183a41e786106f1a053e2485d294895ac36027d94a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3b230ef35186d4f96714ba708b933cf7897166a74b7162949a3144aa21dc79aab2446a0b14d1e0504ea30f941146a7e0f1841d5908e6a6175d771a9b4a658ad
|
7
|
+
data.tar.gz: 9d476bdbaf46cef516cd51adb218097137863fb12976e98d87a7c8d7f77e3609421d81ba14ed77d67cd59054d4356690d088b619e8e089964cd4fdd6055225ac
|
data/README.md
CHANGED
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative 'segments/dynamic_segment'
|
2
|
+
require_relative 'segments/static_segment'
|
3
|
+
require_relative 'segments/wildcard_segment'
|
4
|
+
require_relative 'lru_strategies/hit_strategy'
|
5
|
+
require_relative 'lru_strategies/miss_strategy'
|
6
|
+
|
7
|
+
module RubyRoutes
|
8
|
+
module Constant
|
9
|
+
SEGMENTS = {
|
10
|
+
42 => RubyRoutes::Segments::WildcardSegment, # '*'
|
11
|
+
58 => RubyRoutes::Segments::DynamicSegment, # ':'
|
12
|
+
:default => RubyRoutes::Segments::StaticSegment
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
SEGMENT_MATCHERS = {
|
16
|
+
static: lambda do |node, segment, _idx, _segments, _params|
|
17
|
+
child = node.static_children[segment]
|
18
|
+
child ? [child, false] : nil
|
19
|
+
end,
|
20
|
+
|
21
|
+
dynamic: lambda do |node, segment, _idx, _segments, params|
|
22
|
+
return nil unless node.dynamic_child
|
23
|
+
nxt = node.dynamic_child
|
24
|
+
params[nxt.param_name.to_s] = segment if params && nxt.param_name
|
25
|
+
[nxt, false]
|
26
|
+
end,
|
27
|
+
|
28
|
+
wildcard: lambda do |node, _segment, idx, segments, params|
|
29
|
+
return nil unless node.wildcard_child
|
30
|
+
nxt = node.wildcard_child
|
31
|
+
params[nxt.param_name.to_s] = segments[idx..-1].join('/') if params && nxt.param_name
|
32
|
+
[nxt, true]
|
33
|
+
end,
|
34
|
+
|
35
|
+
# default returns nil (no match). RadixTree#find will then return [nil, {}].
|
36
|
+
default: lambda { |_node, _segment, _idx, _segments, _params| nil }
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
# singleton instances to avoid per-LRU allocations
|
40
|
+
LRU_HIT_STRATEGY = RubyRoutes::LruStrategies::HitStrategy.new.freeze
|
41
|
+
LRU_MISS_STRATEGY = RubyRoutes::LruStrategies::MissStrategy.new.freeze
|
42
|
+
|
43
|
+
# Descriptor factories for segment classification (O(1) dispatch by first byte).
|
44
|
+
DESCRIPTOR_FACTORIES = {
|
45
|
+
42 => ->(s) { { type: :splat, name: (s[1..-1] || 'splat') } }, # '*'
|
46
|
+
58 => ->(s) { { type: :param, name: s[1..-1] } }, # ':'
|
47
|
+
:default => ->(s) { { type: :static, value: s } }
|
48
|
+
}.freeze
|
49
|
+
|
50
|
+
def self.segment_descriptor(raw)
|
51
|
+
s = raw.to_s
|
52
|
+
key = s.empty? ? :default : s.getbyte(0)
|
53
|
+
factory = DESCRIPTOR_FACTORIES[key] || DESCRIPTOR_FACTORIES[:default]
|
54
|
+
factory.call(s)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/ruby_routes/node.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'segment'
|
2
|
+
|
1
3
|
module RubyRoutes
|
2
4
|
class Node
|
3
5
|
attr_accessor :static_children, :dynamic_child, :wildcard_child,
|
@@ -12,6 +14,36 @@ module RubyRoutes
|
|
12
14
|
@is_endpoint = false
|
13
15
|
end
|
14
16
|
|
17
|
+
# Traverse for a single segment using the matcher registry.
|
18
|
+
# Returns [next_node_or_nil, should_break_bool] or [nil, false] if no match.
|
19
|
+
def traverse_for(segment, index, segments, params)
|
20
|
+
# Prefer static children first (exact match).
|
21
|
+
if @static_children.key?(segment)
|
22
|
+
return [@static_children[segment], false]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Then dynamic param child (single segment)
|
26
|
+
if @dynamic_child
|
27
|
+
next_node = @dynamic_child
|
28
|
+
if params
|
29
|
+
params[next_node.param_name.to_s] = segment
|
30
|
+
end
|
31
|
+
return [next_node, false]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Then wildcard child (consume remainder)
|
35
|
+
if @wildcard_child
|
36
|
+
next_node = @wildcard_child
|
37
|
+
if params
|
38
|
+
params[next_node.param_name.to_s] = segments[index..-1].join('/')
|
39
|
+
end
|
40
|
+
return [next_node, true]
|
41
|
+
end
|
42
|
+
|
43
|
+
# No match at this node
|
44
|
+
[nil, false]
|
45
|
+
end
|
46
|
+
|
15
47
|
def add_handler(method, handler)
|
16
48
|
@handlers[method.to_s] = handler
|
17
49
|
@is_endpoint = true
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'segment'
|
2
|
+
|
1
3
|
module RubyRoutes
|
2
4
|
class RadixTree
|
3
5
|
class << self
|
@@ -23,47 +25,30 @@ module RubyRoutes
|
|
23
25
|
end
|
24
26
|
|
25
27
|
def add(path, methods, handler)
|
26
|
-
segments = split_path(path)
|
27
28
|
current = @root
|
28
|
-
|
29
|
-
|
30
|
-
if
|
31
|
-
current.wildcard_child ||= Node.new
|
32
|
-
current = current.wildcard_child
|
33
|
-
current.param_name = segment[1..-1] || 'splat'
|
34
|
-
break
|
35
|
-
elsif segment.start_with?(':')
|
36
|
-
current.dynamic_child ||= Node.new
|
37
|
-
current = current.dynamic_child
|
38
|
-
current.param_name = segment[1..-1]
|
39
|
-
else
|
40
|
-
current.static_children[segment] ||= Node.new
|
41
|
-
current = current.static_children[segment]
|
42
|
-
end
|
29
|
+
parse_segments(path).each do |seg|
|
30
|
+
current = seg.ensure_child(current)
|
31
|
+
break if seg.wildcard?
|
43
32
|
end
|
44
33
|
|
45
34
|
methods.each { |method| current.add_handler(method, handler) }
|
46
35
|
end
|
47
36
|
|
48
|
-
def
|
37
|
+
def parse_segments(path)
|
38
|
+
split_path(path).map { |s| RubyRoutes::Segment.for(s) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def find(path, method, params_out = nil)
|
49
42
|
segments = split_path(path)
|
50
43
|
current = @root
|
51
|
-
params = {}
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
params[current.param_name.to_s] = segment
|
60
|
-
elsif current.wildcard_child
|
61
|
-
current = current.wildcard_child
|
62
|
-
params[current.param_name.to_s] = segments[index..-1].join('/')
|
63
|
-
break
|
64
|
-
else
|
65
|
-
return [nil, {}]
|
66
|
-
end
|
44
|
+
params = params_out || {}
|
45
|
+
params.clear if params_out
|
46
|
+
|
47
|
+
segments.each_with_index do |text, idx|
|
48
|
+
next_node, should_break = current.traverse_for(text, idx, segments, params)
|
49
|
+
return [nil, {}] unless next_node
|
50
|
+
current = next_node
|
51
|
+
break if should_break
|
67
52
|
end
|
68
53
|
|
69
54
|
handler = current.get_handler(method)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RubyRoutes
|
2
|
+
class Route
|
3
|
+
# small LRU used for path generation cache
|
4
|
+
class SmallLru
|
5
|
+
attr_reader :hits, :misses, :evictions
|
6
|
+
|
7
|
+
# larger default to reduce eviction likelihood in benchmarks
|
8
|
+
def initialize(max_size = 1024)
|
9
|
+
@max_size = max_size
|
10
|
+
@h = {}
|
11
|
+
@hits = 0
|
12
|
+
@misses = 0
|
13
|
+
@evictions = 0
|
14
|
+
|
15
|
+
@hit_strategy = RubyRoutes::Constant::LRU_HIT_STRATEGY
|
16
|
+
@miss_strategy = RubyRoutes::Constant::LRU_MISS_STRATEGY
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(key)
|
20
|
+
strategy = @h.key?(key) ? @hit_strategy : @miss_strategy
|
21
|
+
strategy.call(self, key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def set(key, val)
|
25
|
+
@h.delete(key) if @h.key?(key)
|
26
|
+
@h[key] = val
|
27
|
+
if @h.size > @max_size
|
28
|
+
@h.shift
|
29
|
+
@evictions += 1
|
30
|
+
end
|
31
|
+
val
|
32
|
+
end
|
33
|
+
|
34
|
+
def increment_hits
|
35
|
+
@hits += 1
|
36
|
+
end
|
37
|
+
|
38
|
+
def increment_misses
|
39
|
+
@misses += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/ruby_routes/route.rb
CHANGED
@@ -1,43 +1,8 @@
|
|
1
1
|
require 'uri'
|
2
|
+
require_relative 'route/small_lru'
|
2
3
|
|
3
4
|
module RubyRoutes
|
4
5
|
class Route
|
5
|
-
# small LRU used for path generation cache
|
6
|
-
class SmallLru
|
7
|
-
attr_reader :hits, :misses, :evictions
|
8
|
-
|
9
|
-
# larger default to reduce eviction likelihood in benchmarks
|
10
|
-
def initialize(max_size = 1024)
|
11
|
-
@max_size = max_size
|
12
|
-
@h = {}
|
13
|
-
@hits = 0
|
14
|
-
@misses = 0
|
15
|
-
@evictions = 0
|
16
|
-
end
|
17
|
-
|
18
|
-
def get(key)
|
19
|
-
if @h.key?(key)
|
20
|
-
@hits += 1
|
21
|
-
val = @h.delete(key)
|
22
|
-
@h[key] = val
|
23
|
-
val
|
24
|
-
else
|
25
|
-
@misses += 1
|
26
|
-
nil
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def set(key, val)
|
31
|
-
@h.delete(key) if @h.key?(key)
|
32
|
-
@h[key] = val
|
33
|
-
if @h.size > @max_size
|
34
|
-
@h.shift
|
35
|
-
@evictions += 1
|
36
|
-
end
|
37
|
-
val
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
6
|
attr_reader :path, :methods, :controller, :action, :name, :constraints, :defaults
|
42
7
|
|
43
8
|
def initialize(path, options = {})
|
@@ -58,17 +23,29 @@ module RubyRoutes
|
|
58
23
|
!!extract_path_params(request_path)
|
59
24
|
end
|
60
25
|
|
61
|
-
def extract_params(request_path)
|
26
|
+
def extract_params(request_path, parsed_qp = nil)
|
62
27
|
path_params = extract_path_params(request_path)
|
63
28
|
return {} unless path_params
|
64
29
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
defaults.each { |k, v| params[k] = v unless params.key?(k) }
|
30
|
+
# Reuse a thread-local hash to reduce allocations; return a dup to callers.
|
31
|
+
tmp = Thread.current[:ruby_routes_params] ||= {}
|
32
|
+
tmp.clear
|
69
33
|
|
70
|
-
|
71
|
-
|
34
|
+
# start with path params (they take precedence)
|
35
|
+
path_params.each { |k, v| tmp[k] = v }
|
36
|
+
|
37
|
+
# use provided parsed_qp if available, otherwise parse lazily only if needed
|
38
|
+
qp = parsed_qp
|
39
|
+
if qp.nil? && request_path.include?('?')
|
40
|
+
qp = query_params(request_path)
|
41
|
+
end
|
42
|
+
qp.each { |k, v| tmp[k] = v } if qp && !qp.empty?
|
43
|
+
|
44
|
+
# only set defaults for keys not already present
|
45
|
+
defaults.each { |k, v| tmp[k] = v unless tmp.key?(k) } if defaults
|
46
|
+
|
47
|
+
validate_constraints!(tmp)
|
48
|
+
tmp.dup
|
72
49
|
end
|
73
50
|
|
74
51
|
def named?
|
@@ -83,18 +60,21 @@ module RubyRoutes
|
|
83
60
|
!resource?
|
84
61
|
end
|
85
62
|
|
63
|
+
def parse_query_params(path)
|
64
|
+
query_params(path)
|
65
|
+
end
|
66
|
+
|
86
67
|
# Fast path generator: uses precompiled token list and a small LRU.
|
87
68
|
# Avoids unbounded cache growth and skips URI-encoding for safe values.
|
88
69
|
def generate_path(params = {})
|
89
70
|
return '/' if path == '/'
|
90
71
|
|
91
|
-
# build merged for only relevant param names
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
72
|
+
# build merged for only relevant param names, reusing a thread-local hash
|
73
|
+
tmp = Thread.current[:ruby_routes_merged] ||= {}
|
74
|
+
tmp.clear
|
75
|
+
defaults.each { |k, v| tmp[k] = v } if defaults
|
76
|
+
params.each { |k, v| tmp[k.to_s] = v } if params
|
77
|
+
merged = tmp
|
98
78
|
|
99
79
|
missing = compiled_required_params - merged.keys
|
100
80
|
raise RubyRoutes::RouteNotFound, "Missing params: #{missing.join(', ')}" unless missing.empty?
|
@@ -135,13 +115,7 @@ module RubyRoutes
|
|
135
115
|
[]
|
136
116
|
else
|
137
117
|
path.split('/').reject(&:empty?).map do |seg|
|
138
|
-
|
139
|
-
{ type: :param, name: seg[1..-1] }
|
140
|
-
elsif seg.start_with?('*')
|
141
|
-
{ type: :splat, name: (seg[1..-1] || 'splat') }
|
142
|
-
else
|
143
|
-
{ type: :static, value: seg }
|
144
|
-
end
|
118
|
+
RubyRoutes::Constant.segment_descriptor(seg)
|
145
119
|
end
|
146
120
|
end
|
147
121
|
end
|
@@ -157,7 +131,21 @@ module RubyRoutes
|
|
157
131
|
def cache_key_for(merged)
|
158
132
|
# build key in route token order (parameters & splat) to avoid sorting/inspect
|
159
133
|
names = compiled_param_names
|
160
|
-
|
134
|
+
# build with single string buffer to avoid temporary arrays
|
135
|
+
buf = +""
|
136
|
+
names.each_with_index do |n, i|
|
137
|
+
val = merged[n]
|
138
|
+
part = if val.nil?
|
139
|
+
''
|
140
|
+
elsif val.is_a?(Array)
|
141
|
+
val.map!(&:to_s) && val.join('/')
|
142
|
+
else
|
143
|
+
val.to_s
|
144
|
+
end
|
145
|
+
buf << '|' unless i.zero?
|
146
|
+
buf << part
|
147
|
+
end
|
148
|
+
buf
|
161
149
|
end
|
162
150
|
|
163
151
|
def compiled_param_names
|
@@ -223,7 +211,7 @@ module RubyRoutes
|
|
223
211
|
qidx = path.index('?')
|
224
212
|
return {} unless qidx
|
225
213
|
qs = path[(qidx + 1)..-1] || ''
|
226
|
-
Rack::Utils.parse_query(qs)
|
214
|
+
Rack::Utils.parse_query(qs)
|
227
215
|
end
|
228
216
|
|
229
217
|
def validate_constraints!(params)
|
@@ -5,7 +5,10 @@ module RubyRoutes
|
|
5
5
|
def initialize
|
6
6
|
@tree = RubyRoutes::RadixTree.new
|
7
7
|
@named_routes = {}
|
8
|
-
@routes = []
|
8
|
+
@routes = [] # keep list for specs / iteration / size
|
9
|
+
@recognition_cache = {} # simple bounded cache: key -> [route, params]
|
10
|
+
@recognition_cache_order = []
|
11
|
+
@recognition_cache_max = 4096
|
9
12
|
end
|
10
13
|
|
11
14
|
def add_route(route)
|
@@ -26,19 +29,40 @@ module RubyRoutes
|
|
26
29
|
end
|
27
30
|
|
28
31
|
def match(request_method, request_path)
|
29
|
-
#
|
30
|
-
|
32
|
+
# Normalize method once and attempt recognition cache hit
|
33
|
+
method_up = request_method.to_s.upcase
|
34
|
+
cache_key = "#{method_up}:#{request_path}"
|
35
|
+
if (cached = @recognition_cache[cache_key])
|
36
|
+
# Return cached params (frozen) directly to avoid heavy dup allocations.
|
37
|
+
cached_route, cached_params = cached
|
38
|
+
return { route: cached_route, params: cached_params, controller: cached_route.controller, action: cached_route.action }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Use a thread-local hash as output for RadixTree to avoid allocating a params Hash
|
42
|
+
tmp = Thread.current[:ruby_routes_params] ||= {}
|
43
|
+
handler, _ = @tree.find(request_path, method_up, tmp)
|
31
44
|
return nil unless handler
|
32
|
-
|
33
45
|
route = handler
|
34
46
|
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
47
|
+
# tmp now contains path params (filled by RadixTree). Merge defaults and query params in-place.
|
48
|
+
# defaults first (only set missing keys)
|
49
|
+
if route.defaults
|
50
|
+
route.defaults.each { |k, v| tmp[k] = v unless tmp.key?(k) }
|
51
|
+
end
|
52
|
+
if request_path.include?('?')
|
53
|
+
qp = route.parse_query_params(request_path)
|
54
|
+
qp.each { |k, v| tmp[k] = v } unless qp.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
params = tmp.dup
|
58
|
+
|
59
|
+
# insert into bounded recognition cache (store frozen params to reduce accidental mutation)
|
60
|
+
@recognition_cache[cache_key] = [route, params.freeze]
|
61
|
+
@recognition_cache_order << cache_key
|
62
|
+
if @recognition_cache_order.size > @recognition_cache_max
|
63
|
+
oldest = @recognition_cache_order.shift
|
64
|
+
@recognition_cache.delete(oldest)
|
65
|
+
end
|
42
66
|
|
43
67
|
{
|
44
68
|
route: route,
|
@@ -58,8 +82,8 @@ module RubyRoutes
|
|
58
82
|
end
|
59
83
|
|
60
84
|
def generate_path_from_route(route, params = {})
|
61
|
-
|
62
|
-
|
85
|
+
# Delegate to Route#generate_path which uses precompiled segments + cache
|
86
|
+
route.generate_path(params)
|
63
87
|
end
|
64
88
|
|
65
89
|
def clear!
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'segments/base_segment'
|
2
|
+
require_relative 'segments/dynamic_segment'
|
3
|
+
require_relative 'segments/static_segment'
|
4
|
+
require_relative 'segments/wildcard_segment'
|
5
|
+
require_relative 'constant'
|
6
|
+
|
7
|
+
module RubyRoutes
|
8
|
+
class Segment
|
9
|
+
def self.for(text)
|
10
|
+
t = text.to_s
|
11
|
+
key = t.empty? ? :default : t.getbyte(0)
|
12
|
+
segment = RubyRoutes::Constant::SEGMENTS[key] || RubyRoutes::Constant::SEGMENTS[:default]
|
13
|
+
segment.new(t)
|
14
|
+
end
|
15
|
+
|
16
|
+
def wildcard?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RubyRoutes
|
2
|
+
module Segments
|
3
|
+
class BaseSegment
|
4
|
+
def initialize(text = nil)
|
5
|
+
@text = text.to_s if text
|
6
|
+
end
|
7
|
+
|
8
|
+
def wildcard?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
def ensure_child(current)
|
13
|
+
raise NotImplementedError, "#{self.class}#ensure_child must be implemented"
|
14
|
+
end
|
15
|
+
|
16
|
+
def match(_node, _text, _idx, _segments, _params)
|
17
|
+
raise NotImplementedError, "#{self.class}#match must be implemented"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RubyRoutes
|
2
|
+
module Segments
|
3
|
+
class DynamicSegment < BaseSegment
|
4
|
+
def initialize(text)
|
5
|
+
@name = text[1..-1]
|
6
|
+
end
|
7
|
+
|
8
|
+
def ensure_child(current)
|
9
|
+
current.dynamic_child ||= Node.new
|
10
|
+
current = current.dynamic_child
|
11
|
+
current.param_name = @name
|
12
|
+
current
|
13
|
+
end
|
14
|
+
|
15
|
+
def match(node, text, _idx, _segments, params)
|
16
|
+
return [nil, false] unless node.dynamic_child
|
17
|
+
nxt = node.dynamic_child
|
18
|
+
params[nxt.param_name.to_s] = text if params
|
19
|
+
[nxt, false]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RubyRoutes
|
2
|
+
module Segments
|
3
|
+
class StaticSegment < BaseSegment
|
4
|
+
def initialize(text)
|
5
|
+
@text = text
|
6
|
+
end
|
7
|
+
|
8
|
+
def ensure_child(current)
|
9
|
+
current.static_children[@text] ||= Node.new
|
10
|
+
current.static_children[@text]
|
11
|
+
end
|
12
|
+
|
13
|
+
def match(node, text, _idx, _segments, _params)
|
14
|
+
[node.static_children[text], false]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RubyRoutes
|
2
|
+
module Segments
|
3
|
+
class WildcardSegment < BaseSegment
|
4
|
+
def initialize(text)
|
5
|
+
@name = (text[1..-1] || 'splat')
|
6
|
+
end
|
7
|
+
|
8
|
+
def ensure_child(current)
|
9
|
+
current.wildcard_child ||= Node.new
|
10
|
+
current = current.wildcard_child
|
11
|
+
current.param_name = @name
|
12
|
+
current
|
13
|
+
end
|
14
|
+
|
15
|
+
def wildcard?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def match(node, _text, idx, segments, params)
|
20
|
+
return [nil, false] unless node.wildcard_child
|
21
|
+
nxt = node.wildcard_child
|
22
|
+
params[nxt.param_name.to_s] = segments[idx..-1].join('/') if params
|
23
|
+
[nxt, true]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/ruby_routes/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_routes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yosef Benny Widyokarsono
|
@@ -62,11 +62,20 @@ files:
|
|
62
62
|
- LICENSE
|
63
63
|
- README.md
|
64
64
|
- lib/ruby_routes.rb
|
65
|
+
- lib/ruby_routes/constant.rb
|
66
|
+
- lib/ruby_routes/lru_strategies/hit_strategy.rb
|
67
|
+
- lib/ruby_routes/lru_strategies/miss_strategy.rb
|
65
68
|
- lib/ruby_routes/node.rb
|
66
69
|
- lib/ruby_routes/radix_tree.rb
|
67
70
|
- lib/ruby_routes/route.rb
|
71
|
+
- lib/ruby_routes/route/small_lru.rb
|
68
72
|
- lib/ruby_routes/route_set.rb
|
69
73
|
- lib/ruby_routes/router.rb
|
74
|
+
- lib/ruby_routes/segment.rb
|
75
|
+
- lib/ruby_routes/segments/base_segment.rb
|
76
|
+
- lib/ruby_routes/segments/dynamic_segment.rb
|
77
|
+
- lib/ruby_routes/segments/static_segment.rb
|
78
|
+
- lib/ruby_routes/segments/wildcard_segment.rb
|
70
79
|
- lib/ruby_routes/string_extensions.rb
|
71
80
|
- lib/ruby_routes/url_helpers.rb
|
72
81
|
- lib/ruby_routes/version.rb
|