ruby_routes 2.2.0 → 2.4.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 +240 -163
- data/lib/ruby_routes/constant.rb +137 -18
- data/lib/ruby_routes/lru_strategies/hit_strategy.rb +31 -4
- data/lib/ruby_routes/lru_strategies/miss_strategy.rb +21 -0
- data/lib/ruby_routes/node.rb +86 -36
- data/lib/ruby_routes/radix_tree/finder.rb +213 -0
- data/lib/ruby_routes/radix_tree/inserter.rb +96 -0
- data/lib/ruby_routes/radix_tree.rb +65 -230
- data/lib/ruby_routes/route/check_helpers.rb +115 -0
- data/lib/ruby_routes/route/constraint_validator.rb +173 -0
- data/lib/ruby_routes/route/param_support.rb +200 -0
- data/lib/ruby_routes/route/path_builder.rb +84 -0
- data/lib/ruby_routes/route/path_generation.rb +87 -0
- data/lib/ruby_routes/route/query_helpers.rb +56 -0
- data/lib/ruby_routes/route/segment_compiler.rb +166 -0
- data/lib/ruby_routes/route/small_lru.rb +93 -18
- data/lib/ruby_routes/route/validation_helpers.rb +174 -0
- data/lib/ruby_routes/route/warning_helpers.rb +57 -0
- data/lib/ruby_routes/route.rb +127 -501
- data/lib/ruby_routes/route_set/cache_helpers.rb +76 -0
- data/lib/ruby_routes/route_set/collection_helpers.rb +125 -0
- data/lib/ruby_routes/route_set.rb +140 -132
- data/lib/ruby_routes/router/build_helpers.rb +99 -0
- data/lib/ruby_routes/router/builder.rb +97 -0
- data/lib/ruby_routes/router/http_helpers.rb +135 -0
- data/lib/ruby_routes/router/resource_helpers.rb +137 -0
- data/lib/ruby_routes/router/scope_helpers.rb +127 -0
- data/lib/ruby_routes/router.rb +196 -182
- data/lib/ruby_routes/segment.rb +28 -8
- data/lib/ruby_routes/segments/base_segment.rb +40 -4
- data/lib/ruby_routes/segments/dynamic_segment.rb +48 -12
- data/lib/ruby_routes/segments/static_segment.rb +43 -7
- data/lib/ruby_routes/segments/wildcard_segment.rb +58 -12
- data/lib/ruby_routes/string_extensions.rb +52 -15
- data/lib/ruby_routes/url_helpers.rb +106 -24
- data/lib/ruby_routes/utility/inflector_utility.rb +35 -0
- data/lib/ruby_routes/utility/key_builder_utility.rb +171 -77
- data/lib/ruby_routes/utility/method_utility.rb +137 -0
- data/lib/ruby_routes/utility/path_utility.rb +75 -28
- data/lib/ruby_routes/utility/route_utility.rb +30 -2
- data/lib/ruby_routes/version.rb +3 -1
- data/lib/ruby_routes.rb +68 -11
- metadata +27 -7
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../constant'
|
4
|
+
require_relative 'build_helpers'
|
5
|
+
|
6
|
+
module RubyRoutes
|
7
|
+
class Router
|
8
|
+
# Builder
|
9
|
+
#
|
10
|
+
# Records routing DSL invocations without mutating a live Router.
|
11
|
+
# Later, `#build` replays the recorded calls on a fresh Router,
|
12
|
+
# finalizes it (immutability), and returns the frozen instance.
|
13
|
+
#
|
14
|
+
# Benefits:
|
15
|
+
# - Decouples route declaration time from Router instantiation.
|
16
|
+
# - Enables reuse (same blueprint -> multiple routers).
|
17
|
+
# - Safe to construct on boot and share across threads after build.
|
18
|
+
#
|
19
|
+
# Usage:
|
20
|
+
# builder = RubyRoutes::Router::Builder.new do
|
21
|
+
# namespace :api do
|
22
|
+
# resources :users
|
23
|
+
# end
|
24
|
+
# get '/health', to: 'system#health'
|
25
|
+
# end
|
26
|
+
# router = builder.build # finalized (router.frozen? == true)
|
27
|
+
#
|
28
|
+
# Supported DSL methods are mirrored here. Blocks are stored as Procs;
|
29
|
+
# serialization of blocks is not supported.
|
30
|
+
#
|
31
|
+
# @api internal
|
32
|
+
class Builder
|
33
|
+
include RubyRoutes::Router::BuildHelpers
|
34
|
+
|
35
|
+
# Array of recorded calls: [method_symbol, args_array, block].
|
36
|
+
#
|
37
|
+
# Each tuple contains:
|
38
|
+
# - The method name (as a Symbol).
|
39
|
+
# - The arguments (as an Array).
|
40
|
+
# - The block (as a Proc or `nil`).
|
41
|
+
#
|
42
|
+
# @return [Array<Array(Symbol, Array, Proc|NilClass)>]
|
43
|
+
# A snapshot of the recorded calls to avoid external mutation.
|
44
|
+
def recorded_calls
|
45
|
+
@recorded_calls.dup.freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
# Initialize the Builder.
|
49
|
+
#
|
50
|
+
# This method initializes the `@recorded_calls` array and optionally
|
51
|
+
# evaluates the provided block in the context of the Builder instance.
|
52
|
+
#
|
53
|
+
# @yield [definition_block] Runs the routing DSL in a recording context (optional).
|
54
|
+
# @return [void]
|
55
|
+
def initialize(&definition_block)
|
56
|
+
@recorded_calls = []
|
57
|
+
instance_eval(&definition_block) if definition_block
|
58
|
+
end
|
59
|
+
|
60
|
+
# ---- DSL Recording -------------------------------------------------
|
61
|
+
# Dynamically define methods for all DSL methods specified in
|
62
|
+
# `RubyRoutes::Constant::RECORDED_METHODS`. Each method records its
|
63
|
+
# invocation (method name, arguments, and block) in `@recorded_calls`.
|
64
|
+
#
|
65
|
+
# The dynamically defined methods accept arbitrary arguments and an optional block,
|
66
|
+
# which are recorded for later processing by the router.
|
67
|
+
#
|
68
|
+
# @return [nil]
|
69
|
+
RubyRoutes::Constant::RECORDED_METHODS.each do |method_name|
|
70
|
+
define_method(method_name) do |*arguments, &definition_block|
|
71
|
+
@recorded_calls << [__method__, arguments, definition_block]
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Validate the recorded calls.
|
79
|
+
#
|
80
|
+
# This method ensures that all recorded calls use valid router methods
|
81
|
+
# as defined in `RubyRoutes::Constant::RECORDED_METHODS`.
|
82
|
+
#
|
83
|
+
# @param recorded_calls [Array<Array(Symbol, Array, Proc|NilClass)>]
|
84
|
+
# The recorded calls to validate.
|
85
|
+
# @raise [ArgumentError] If any recorded call uses an invalid method.
|
86
|
+
# @return [void]
|
87
|
+
def validate_calls(recorded_calls)
|
88
|
+
allowed_router_methods = RubyRoutes::Constant::RECORDED_METHODS
|
89
|
+
recorded_calls.each do |(router_method, _arguments, _definition_block)|
|
90
|
+
unless router_method.is_a?(Symbol) && allowed_router_methods.include?(router_method)
|
91
|
+
raise ArgumentError, "Invalid router method: #{router_method.inspect}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'build_helpers'
|
4
|
+
require_relative 'scope_helpers'
|
5
|
+
require_relative 'resource_helpers'
|
6
|
+
|
7
|
+
module RubyRoutes
|
8
|
+
class Router
|
9
|
+
# HttpHelpers
|
10
|
+
#
|
11
|
+
# DSL methods exposing HTTP verb helpers (`get`, `post`, `put`, `patch`, `delete`, `match`)
|
12
|
+
# and small wiring helpers. Resource- and nested-related logic is delegated
|
13
|
+
# to `Router::ResourceHelpers` to keep this module focused.
|
14
|
+
#
|
15
|
+
# This module provides methods to define routes for various HTTP verbs, apply
|
16
|
+
# scopes, and manage route definitions. It also includes helper methods for
|
17
|
+
# defining singular resource routes.
|
18
|
+
module HttpHelpers
|
19
|
+
include RubyRoutes::Router::BuildHelpers
|
20
|
+
include RubyRoutes::Router::ScopeHelpers
|
21
|
+
include RubyRoutes::Router::ResourceHelpers
|
22
|
+
|
23
|
+
# ---- HTTP Verb Helpers -------------------------------------------------
|
24
|
+
|
25
|
+
# Define a GET route.
|
26
|
+
#
|
27
|
+
# @param path [String] The path for the route.
|
28
|
+
# @param options [Hash] The options for the route.
|
29
|
+
# @return [Router] Returns self for chaining.
|
30
|
+
def get(path, options = {})
|
31
|
+
add_route(path, build_route_options(options, :get))
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# Define a POST route.
|
36
|
+
#
|
37
|
+
# @param path [String] The path for the route.
|
38
|
+
# @param options [Hash] The options for the route.
|
39
|
+
# @return [Router] Returns self for chaining.
|
40
|
+
def post(path, options = {})
|
41
|
+
add_route(path, build_route_options(options, :post))
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# Define a PUT route.
|
46
|
+
#
|
47
|
+
# @param path [String] The path for the route.
|
48
|
+
# @param options [Hash] The options for the route.
|
49
|
+
# @return [Router] Returns self for chaining.
|
50
|
+
def put(path, options = {})
|
51
|
+
add_route(path, build_route_options(options, :put))
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
# Define a PATCH route.
|
56
|
+
#
|
57
|
+
# @param path [String] The path for the route.
|
58
|
+
# @param options [Hash] The options for the route.
|
59
|
+
# @return [Router] Returns self for chaining.
|
60
|
+
def patch(path, options = {})
|
61
|
+
add_route(path, build_route_options(options, :patch))
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
# Define a DELETE route.
|
66
|
+
#
|
67
|
+
# @param path [String] The path for the route.
|
68
|
+
# @param options [Hash] The options for the route.
|
69
|
+
# @return [Router] Returns self for chaining.
|
70
|
+
def delete(path, options = {})
|
71
|
+
add_route(path, build_route_options(options, :delete))
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# Define a route for multiple HTTP methods.
|
76
|
+
#
|
77
|
+
# @param path [String] The path for the route.
|
78
|
+
# @param options [Hash] The options for the route.
|
79
|
+
# - `:via` [Array<Symbol>] The HTTP methods to allow (e.g., `[:get, :post]`).
|
80
|
+
# @raise [ArgumentError] If `:via` is not provided or is empty.
|
81
|
+
# @return [Router] Returns self for chaining.
|
82
|
+
def match(path, options = {})
|
83
|
+
via = options[:via]
|
84
|
+
raise ArgumentError, 'match requires :via (e.g., via: [:get, :post])' if via.nil? || Array(via).empty?
|
85
|
+
|
86
|
+
add_route(path, options)
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Add a route to the router.
|
93
|
+
#
|
94
|
+
# This method applies the current scope to the route and defines it using
|
95
|
+
# the route utilities.
|
96
|
+
#
|
97
|
+
# @param path [String] The path for the route.
|
98
|
+
# @param options [Hash] The options for the route.
|
99
|
+
# @return [void]
|
100
|
+
def add_route(path, options = {})
|
101
|
+
ensure_unfrozen!
|
102
|
+
scoped = apply_scope(path, options)
|
103
|
+
@route_utils.define(scoped[:path], scoped)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Ensure the router is not frozen.
|
107
|
+
#
|
108
|
+
# @raise [RuntimeError] If the router is frozen.
|
109
|
+
# @return [void]
|
110
|
+
def ensure_unfrozen!
|
111
|
+
raise 'Router finalized (immutable)' if @frozen || frozen?
|
112
|
+
end
|
113
|
+
|
114
|
+
# Define routes for a singular resource.
|
115
|
+
#
|
116
|
+
# This method defines routes for a singular resource (e.g., `/profile`),
|
117
|
+
# including standard RESTful actions like `show`, `new`, `create`, `edit`,
|
118
|
+
# `update`, and `destroy`.
|
119
|
+
#
|
120
|
+
# @param singular [String] The name of the singular resource.
|
121
|
+
# @param controller [String] The name of the controller handling the resource.
|
122
|
+
# @param options [Hash] Additional options for the routes.
|
123
|
+
# @return [void]
|
124
|
+
def define_singular_routes(singular, controller, options)
|
125
|
+
get "/#{singular}", options.merge(to: "#{controller}#show")
|
126
|
+
get "/#{singular}/new", options.merge(to: "#{controller}#new")
|
127
|
+
post "/#{singular}", options.merge(to: "#{controller}#create")
|
128
|
+
get "/#{singular}/edit", options.merge(to: "#{controller}#edit")
|
129
|
+
put "/#{singular}", options.merge(to: "#{controller}#update")
|
130
|
+
patch "/#{singular}", options.merge(to: "#{controller}#update")
|
131
|
+
delete "/#{singular}", options.merge(to: "#{controller}#destroy")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'build_helpers'
|
4
|
+
|
5
|
+
module RubyRoutes
|
6
|
+
class Router
|
7
|
+
# ResourceHelpers: resource / nested-resource related helpers extracted
|
8
|
+
# from HttpHelpers to reduce method length and ABC complexity.
|
9
|
+
#
|
10
|
+
# This module provides methods for defining RESTful routes for resources,
|
11
|
+
# handling nested resources, and generating metadata for resource paths
|
12
|
+
# and controller actions.
|
13
|
+
module ResourceHelpers
|
14
|
+
include RubyRoutes::Router::BuildHelpers
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Define RESTful routes for a resource.
|
19
|
+
#
|
20
|
+
# @param resource_name [Symbol, String] The name of the resource.
|
21
|
+
# @param options [Hash] Options for customizing the resource routes.
|
22
|
+
# - `:path` [String] Custom path for the resource.
|
23
|
+
# - `:controller` [String] Custom controller name.
|
24
|
+
# - `:nested` [Symbol, String] Name of the nested resource.
|
25
|
+
# @param nested_block [Proc] A block for defining nested routes.
|
26
|
+
# @return [void]
|
27
|
+
def define_resource_routes(resource_name, options = {}, &nested_block)
|
28
|
+
meta = resource_meta(resource_name, options)
|
29
|
+
opts = prepare_options(options)
|
30
|
+
|
31
|
+
push_scope(path: "/#{meta[:resource_path]}") do
|
32
|
+
build_routes(opts, meta)
|
33
|
+
handle_nested_option(options, opts)
|
34
|
+
apply_nested_block(nested_block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Prepare options by removing the `:to` key if present.
|
39
|
+
#
|
40
|
+
# @param options [Hash] The options hash.
|
41
|
+
# @return [Hash] The prepared options.
|
42
|
+
def prepare_options(options)
|
43
|
+
options.key?(:to) ? options.dup.tap { |h| h.delete(:to) } : options
|
44
|
+
end
|
45
|
+
|
46
|
+
# Build collection and member routes for a resource.
|
47
|
+
#
|
48
|
+
# @param opts [Hash] The options hash.
|
49
|
+
# @param meta [Hash] The resource metadata.
|
50
|
+
# @return [void]
|
51
|
+
def build_routes(opts, meta)
|
52
|
+
build_collection_routes(opts, meta[:to_index], meta[:to_new], meta[:to_create])
|
53
|
+
build_member_routes(opts, meta[:to_show], meta[:to_edit], meta[:to_update], meta[:to_destroy])
|
54
|
+
end
|
55
|
+
|
56
|
+
# Apply a nested block of routes within the scope of a resource.
|
57
|
+
#
|
58
|
+
# @param nested_block [Proc] The block defining nested routes.
|
59
|
+
# @return [void]
|
60
|
+
def apply_nested_block(nested_block)
|
61
|
+
return unless nested_block
|
62
|
+
|
63
|
+
push_scope(path: '/:id') { instance_eval(&nested_block) }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Prepare resource metadata (path/controller/action strings).
|
67
|
+
#
|
68
|
+
# @param resource_name [Symbol, String] The name of the resource.
|
69
|
+
# @param options [Hash] Options for customizing the resource metadata.
|
70
|
+
# @return [Hash] The resource metadata.
|
71
|
+
def resource_meta(resource_name, options)
|
72
|
+
base_name = resource_name.to_s
|
73
|
+
resource_path = options[:path] ? options[:path].to_s : RubyRoutes::Utility::InflectorUtility.pluralize(base_name)
|
74
|
+
controller = options[:controller] || RubyRoutes::Utility::InflectorUtility.pluralize(base_name)
|
75
|
+
build_meta_hash(resource_path, controller)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Build a metadata hash for a resource.
|
79
|
+
#
|
80
|
+
# @param resource_path [String] The resource path.
|
81
|
+
# @param controller [String] The controller name.
|
82
|
+
# @return [Hash] The metadata hash.
|
83
|
+
def build_meta_hash(resource_path, controller)
|
84
|
+
actions = %w[index new create show edit update destroy]
|
85
|
+
meta_hash = actions.each_with_object({}) do |action, hash|
|
86
|
+
hash[:"to_#{action}"] = "#{controller}##{action}"
|
87
|
+
end
|
88
|
+
|
89
|
+
meta_hash.merge(
|
90
|
+
resource_path: resource_path,
|
91
|
+
controller: controller
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Handle the `nested:` shorthand option for resources.
|
96
|
+
#
|
97
|
+
# @param options [Hash] The options hash.
|
98
|
+
# @param opts [Hash] The prepared options hash.
|
99
|
+
# @return [void]
|
100
|
+
def handle_nested_option(options, opts)
|
101
|
+
return unless options[:nested]
|
102
|
+
|
103
|
+
nested_name = options[:nested].to_s
|
104
|
+
nested_path = RubyRoutes::Utility::InflectorUtility.pluralize(nested_name)
|
105
|
+
build_nested_routes(nested_path, opts)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Build nested resource routes.
|
109
|
+
#
|
110
|
+
# @param nested_path [String] The path for the nested resource.
|
111
|
+
# @param opts [Hash] The options hash.
|
112
|
+
# @return [void]
|
113
|
+
def build_nested_routes(nested_path, opts)
|
114
|
+
push_scope(path: '/:id') do
|
115
|
+
push_scope(path: "/#{nested_path}") do
|
116
|
+
add_nested_routes(nested_path, opts)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Add routes for a nested resource.
|
122
|
+
#
|
123
|
+
# @param nested_path [String] The path for the nested resource.
|
124
|
+
# @param opts [Hash] The options hash.
|
125
|
+
# @return [void]
|
126
|
+
def add_nested_routes(nested_path, opts)
|
127
|
+
add_route('', build_route_options(opts, :get, "#{nested_path}#index"))
|
128
|
+
add_route('/new', build_route_options(opts, :get, "#{nested_path}#new"))
|
129
|
+
add_route('', build_route_options(opts, :post, "#{nested_path}#create"))
|
130
|
+
add_route('/:nested_id', build_route_options(opts, :get, "#{nested_path}#show"))
|
131
|
+
add_route('/:nested_id/edit', build_route_options(opts, :get, "#{nested_path}#edit"))
|
132
|
+
add_route('/:nested_id', opts.merge(via: %i[put patch], to: "#{nested_path}#update"))
|
133
|
+
add_route('/:nested_id', build_route_options(opts, :delete, "#{nested_path}#destroy"))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyRoutes
|
4
|
+
class Router
|
5
|
+
# ScopeHelpers: encapsulate scope application logic
|
6
|
+
#
|
7
|
+
# This module provides methods for managing and applying scopes in the routing DSL.
|
8
|
+
# Scopes allow you to define nested paths, modules, defaults, and constraints
|
9
|
+
# that apply to a group of routes.
|
10
|
+
module ScopeHelpers
|
11
|
+
private
|
12
|
+
|
13
|
+
# Push a scope onto the scope stack.
|
14
|
+
#
|
15
|
+
# This method temporarily adds a scope entry to the scope stack, executes the
|
16
|
+
# given block, and ensures the scope is removed afterward.
|
17
|
+
#
|
18
|
+
# @param scope_entry [Hash] The scope entry to push onto the stack.
|
19
|
+
# @yield The block to execute within the scope.
|
20
|
+
# @return [void]
|
21
|
+
def push_scope(scope_entry)
|
22
|
+
ensure_unfrozen!
|
23
|
+
return unless block_given?
|
24
|
+
|
25
|
+
@scope_stack.push(scope_entry)
|
26
|
+
|
27
|
+
begin
|
28
|
+
yield
|
29
|
+
ensure
|
30
|
+
@scope_stack.pop
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Apply all scopes to the given path and options.
|
35
|
+
#
|
36
|
+
# This method iterates through the scope stack in reverse order, applying
|
37
|
+
# path, module, defaults, and constraints from each scope to the given path
|
38
|
+
# and options.
|
39
|
+
#
|
40
|
+
# @param path [String] The base path to apply scopes to.
|
41
|
+
# @param options [Hash] The options to apply scopes to.
|
42
|
+
# @return [Hash] The scoped options, including the updated path.
|
43
|
+
def apply_scope(path, options)
|
44
|
+
scoped_options = options.dup
|
45
|
+
scoped_path = path.to_s.dup
|
46
|
+
|
47
|
+
@scope_stack.reverse_each do |scope|
|
48
|
+
apply_path_scope(scope, scoped_path)
|
49
|
+
apply_module_scope(scope, scoped_options)
|
50
|
+
apply_defaults_scope(scope, scoped_options)
|
51
|
+
apply_constraints_scope(scope, scoped_options)
|
52
|
+
end
|
53
|
+
|
54
|
+
scoped_options[:path] = scoped_path
|
55
|
+
scoped_options
|
56
|
+
end
|
57
|
+
|
58
|
+
# Apply the path from a scope to the given path.
|
59
|
+
#
|
60
|
+
# @param scope [Hash] The scope containing the path.
|
61
|
+
# @param scoped_path [String] The path to prepend the scope's path to.
|
62
|
+
# @return [void]
|
63
|
+
def apply_path_scope(scope, scoped_path)
|
64
|
+
path = scope[:path]&.to_s
|
65
|
+
return if path.nil? || path.empty?
|
66
|
+
|
67
|
+
if path.end_with?('/')
|
68
|
+
if scoped_path.start_with?('/')
|
69
|
+
scoped_path.prepend(path.chomp('/'))
|
70
|
+
else
|
71
|
+
scoped_path.prepend(path)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
scoped_path.prepend(scoped_path.start_with?('/') ? path : "#{path}/")
|
75
|
+
end
|
76
|
+
|
77
|
+
# Normalize: Ensure the final path starts with '/'
|
78
|
+
scoped_path.prepend('/') unless scoped_path.start_with?('/')
|
79
|
+
end
|
80
|
+
|
81
|
+
# Apply the module from a scope to the given options.
|
82
|
+
#
|
83
|
+
# This method updates the `:to` or `:controller` option to include the module
|
84
|
+
# from the scope.
|
85
|
+
#
|
86
|
+
# @param scope [Hash] The scope containing the module.
|
87
|
+
# @param scoped_options [Hash] The options to update with the module.
|
88
|
+
# @return [void]
|
89
|
+
def apply_module_scope(scope, scoped_options)
|
90
|
+
module_string = scope[:module]&.to_s
|
91
|
+
return if module_string.nil? || module_string.empty?
|
92
|
+
|
93
|
+
if (to_val = scoped_options[:to])
|
94
|
+
controller, action = to_val.to_s.split('#', 2)
|
95
|
+
return if controller.nil? || controller.empty?
|
96
|
+
scoped_options[:to] = action && !action.empty? ? "#{module_string}/#{controller}##{action}" : "#{module_string}/#{controller}"
|
97
|
+
elsif (controller = scoped_options[:controller])
|
98
|
+
controller_string = controller.to_s
|
99
|
+
return if controller_string.empty?
|
100
|
+
scoped_options[:controller] = "#{module_string}/#{controller_string}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Apply the defaults from a scope to the given options.
|
105
|
+
#
|
106
|
+
# @param scope [Hash] The scope containing the defaults.
|
107
|
+
# @param scoped_options [Hash] The options to update with the defaults.
|
108
|
+
# @return [void]
|
109
|
+
def apply_defaults_scope(scope, scoped_options)
|
110
|
+
return unless scope[:defaults]
|
111
|
+
|
112
|
+
scoped_options[:defaults] = scope[:defaults].merge(scoped_options[:defaults] || {})
|
113
|
+
end
|
114
|
+
|
115
|
+
# Apply the constraints from a scope to the given options.
|
116
|
+
#
|
117
|
+
# @param scope [Hash] The scope containing the constraints.
|
118
|
+
# @param scoped_options [Hash] The options to update with the constraints.
|
119
|
+
# @return [void]
|
120
|
+
def apply_constraints_scope(scope, scoped_options)
|
121
|
+
return unless scope[:constraints]
|
122
|
+
|
123
|
+
scoped_options[:constraints] = scope[:constraints].merge(scoped_options[:constraints] || {})
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|