Class: Rage::Router::DSL::Handler

Overview

This class implements routing logic for your application, providing an API similar to Rails.

Compared to the Rails router, the most notable difference is that a wildcard segment can only be in the last section of the path and cannot be named. Example:

get "/photos/*"

Also, as this is an API-only framework, route helpers, like photos_path or photos_url are not being generated.

Constraints

Currently, the only constraint supported is the host constraint. The constraint value can be either string or a regular expression. Example:

get "/photos", to: "photos#index", constraints: { host: "myhost.com" }

Parameter constraints are likely to be added in the future versions. Custom/lambda constraints are unlikely to be ever added.

Examples:

Set up a root handler

root to: "pages#main"

Set up multiple resources

resources :magazines do
  resources :ads
end

Scope a set of routes to the given default options.

scope path: ":account_id" do
  resources :projects
end

Scope routes to a specific namespace.

namespace :admin do
  resources :posts
end

Instance Method Summary collapse

Instance Method Details

#collection(&block) ⇒ Object

Add a route to the collection.

Examples:

Add a photos/search path instead of photos/:photo_id/search

resources :photos do
  collection do
    get "search"
  end
end


297
298
299
300
301
302
# File 'lib/rage/router/dsl.rb', line 297

def collection(&block)
  orig_path_prefixes = @path_prefixes
  @path_prefixes = @path_prefixes[0...-1] if @path_prefixes.last&.start_with?(":")
  instance_eval(&block)
  @path_prefixes = orig_path_prefixes
end

#controller(controller, &block) ⇒ Object

Scopes routes to a specific controller.

Examples:

controller "photos" do
  post "like"
  post "dislike"
end


283
284
285
286
287
# File 'lib/rage/router/dsl.rb', line 283

def controller(controller, &block)
  @controllers << controller
  instance_eval(&block)
  @controllers.pop
end

#defaults(defaults, &block) ⇒ Object

Specify default parameters for a set of routes.

Examples:

defaults id: "-1", format: "jpg" do
  get "photos/(:id)", to: "photos#index"
end

Parameters:

  • defaults (Hash)

    a hash of default parameters



270
271
272
273
274
# File 'lib/rage/router/dsl.rb', line 270

def defaults(defaults, &block)
  @defaults << defaults
  instance_eval(&block)
  @defaults.pop
end

#delete(path, to: nil, constraints: nil, defaults: nil, on: nil) ⇒ Object

Register a new DELETE route.

Examples:

delete "/photos/:id", to: "photos#destroy", constraints: { host: /myhost/ }
delete "/photos(/:id)", to: "photos#destroy", defaults: { id: "-1" }

Parameters:

  • path (String)

    the path for the route handler

  • to (String) (defaults to: nil)

    the route handler in the format of "controller#action"

  • constraints (Hash) (defaults to: nil)

    a hash of constraints for the route

  • defaults (Hash) (defaults to: nil)

    a hash of default parameters for the route

  • on (nil, :member, :collection) (defaults to: nil)

    a shorthand for wrapping routes in a specific RESTful context



145
146
147
# File 'lib/rage/router/dsl.rb', line 145

def delete(path, to: nil, constraints: nil, defaults: nil, on: nil)
  __with_on_scope(on) { __on("DELETE", path, to, constraints, defaults) }
end

#get(path, to: nil, constraints: nil, defaults: nil, on: nil) ⇒ Object

Register a new GET route.

Examples:

get "/photos/:id", to: "photos#show", constraints: { host: /myhost/ }
get "/photos(/:id)", to: "photos#show", defaults: { id: "-1" }

Parameters:

  • path (String)

    the path for the route handler

  • to (String) (defaults to: nil)

    the route handler in the format of "controller#action"

  • constraints (Hash) (defaults to: nil)

    a hash of constraints for the route

  • defaults (Hash) (defaults to: nil)

    a hash of default parameters for the route

  • on (nil, :member, :collection) (defaults to: nil)

    a shorthand for wrapping routes in a specific RESTful context



85
86
87
# File 'lib/rage/router/dsl.rb', line 85

def get(path, to: nil, constraints: nil, defaults: nil, on: nil)
  __with_on_scope(on) { __on("GET", path, to, constraints, defaults) }
end

#match(path, to:, constraints: {}, defaults: nil, via: :all) ⇒ Object

Match a URL pattern to one or more routes.

Examples:

match "/photos/:id", to: "photos#show", via: [:get, :post]
match "/photos/:id", to: "photos#show", via: :all
match "/health", to: -> (env) { [200, {}, ["healthy"]] }

Parameters:

  • path (String)

    the path for the route handler

  • to (String, #call)

    the route handler in the format of "controller#action" or a callable

  • constraints (Hash) (defaults to: {})

    a hash of constraints for the route

  • defaults (Hash) (defaults to: nil)

    a hash of default parameters for the route

  • via (Symbol, Array<Symbol>) (defaults to: :all)

    an array of HTTP methods to accept



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/rage/router/dsl.rb', line 171

def match(path, to:, constraints: {}, defaults: nil, via: :all)
  # via is either nil, or an array of symbols or its :all
  http_methods = via
  # if its :all or nil, then we use the default HTTP methods
  if via == :all || via.nil?
    http_methods = @default_match_methods
  else
    # if its an array of symbols, then we use the symbols as HTTP methods
    http_methods = Array(via)
    # then we check if the HTTP methods are valid
    http_methods.each do |method|
      raise ArgumentError, "Invalid HTTP method: #{method}" unless @default_match_methods.include?(method)
    end
  end

  http_methods.each do |method|
    __on(method.to_s.upcase, path, to, constraints, defaults)
  end
end

#member(&block) ⇒ Object

Add a member route.

Examples:

Add a photos/:id/preview path instead of photos/:photo_id/preview

resources :photos do
  member do
    get "preview"
  end
end


312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/rage/router/dsl.rb', line 312

def member(&block)
  orig_path_prefixes = @path_prefixes

  if (param_prefix = @path_prefixes.last)&.start_with?(":") && @controllers.any?
    member_prefix = param_prefix.delete_prefix(":#{to_singular(@controllers.last)}_")
    @path_prefixes = [*@path_prefixes[0...-1], ":#{member_prefix}"]
  end

  instance_eval(&block)

  @path_prefixes = orig_path_prefixes
end

#mount(app, at:, via: :all) ⇒ Object

Mount a Rack-based application to be used within the application.

Examples:

mount Sidekiq::Web => "/sidekiq"
mount Sidekiq::Web, at: "/sidekiq", via: :get


413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/rage/router/dsl.rb', line 413

def mount(app, at:, via: :all)
  at = "/#{at}" unless at.start_with?("/")
  at = at.delete_suffix("/") if at.end_with?("/")

  http_methods = if via == :all || via.nil?
    @default_match_methods.map { |method| method.to_s.upcase! }
  else
    Array(via).map! do |method|
      raise ArgumentError, "Invalid HTTP method: #{method}" unless @default_match_methods.include?(method)
      method.to_s.upcase!
    end
  end

  @router.mount(at, app, http_methods)
end

#namespace(path, **options, &block) ⇒ Object

Register a new namespace.

Examples:

namespace :admin do
  get "/photos", to: "photos#index"
end
namespace :admin, path: "panel" do
  get "/photos", to: "photos#index"
end
namespace :admin, module: "admin" do
  get "/photos", to: "photos#index"
end

Parameters:

  • path (String)

    the path for the namespace

  • options (Hash)

    a hash of options for the namespace

Options Hash (**options):

  • :module (String)

    the module name for the namespace

  • :path (String)

    the path for the namespace



209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/rage/router/dsl.rb', line 209

def namespace(path, **options, &block)
  path_prefix = options[:path] || path
  module_prefix = options[:module] || path

  @path_prefixes << path_prefix
  @module_prefixes << module_prefix

  instance_eval(&block)

  @path_prefixes.pop
  @module_prefixes.pop
end

#patch(path, to: nil, constraints: nil, defaults: nil, on: nil) ⇒ Object

Register a new PATCH route.

Examples:

patch "/photos/:id", to: "photos#update", constraints: { host: /myhost/ }
patch "/photos(/:id)", to: "photos#update", defaults: { id: "-1" }

Parameters:

  • path (String)

    the path for the route handler

  • to (String) (defaults to: nil)

    the route handler in the format of "controller#action"

  • constraints (Hash) (defaults to: nil)

    a hash of constraints for the route

  • defaults (Hash) (defaults to: nil)

    a hash of default parameters for the route

  • on (nil, :member, :collection) (defaults to: nil)

    a shorthand for wrapping routes in a specific RESTful context



130
131
132
# File 'lib/rage/router/dsl.rb', line 130

def patch(path, to: nil, constraints: nil, defaults: nil, on: nil)
  __with_on_scope(on) { __on("PATCH", path, to, constraints, defaults) }
end

#post(path, to: nil, constraints: nil, defaults: nil, on: nil) ⇒ Object

Register a new POST route.

Examples:

post "/photos", to: "photos#create", constraints: { host: /myhost/ }
post "/photos", to: "photos#create", defaults: { format: "jpg" }

Parameters:

  • path (String)

    the path for the route handler

  • to (String) (defaults to: nil)

    the route handler in the format of "controller#action"

  • constraints (Hash) (defaults to: nil)

    a hash of constraints for the route

  • defaults (Hash) (defaults to: nil)

    a hash of default parameters for the route

  • on (nil, :member, :collection) (defaults to: nil)

    a shorthand for wrapping routes in a specific RESTful context



100
101
102
# File 'lib/rage/router/dsl.rb', line 100

def post(path, to: nil, constraints: nil, defaults: nil, on: nil)
  __with_on_scope(on) { __on("POST", path, to, constraints, defaults) }
end

#put(path, to: nil, constraints: nil, defaults: nil, on: nil) ⇒ Object

Register a new PUT route.

Examples:

put "/photos/:id", to: "photos#update", constraints: { host: /myhost/ }
put "/photos(/:id)", to: "photos#update", defaults: { id: "-1" }

Parameters:

  • path (String)

    the path for the route handler

  • to (String) (defaults to: nil)

    the route handler in the format of "controller#action"

  • constraints (Hash) (defaults to: nil)

    a hash of constraints for the route

  • defaults (Hash) (defaults to: nil)

    a hash of default parameters for the route

  • on (nil, :member, :collection) (defaults to: nil)

    a shorthand for wrapping routes in a specific RESTful context



115
116
117
# File 'lib/rage/router/dsl.rb', line 115

def put(path, to: nil, constraints: nil, defaults: nil, on: nil)
  __with_on_scope(on) { __on("PUT", path, to, constraints, defaults) }
end

#resource(*_resources, **opts, &block) ⇒ Object

Note:

This helper doesn't generate the new and edit routes.

Note:

:param is not supported for singular resources.

Automatically create REST routes for a singular resource.

Examples:

Create singular routes mapped to a plural controller:

resource :photo
# POST   /photo => photos#create
# GET    /photo => photos#show
# PATCH  /photo => photos#update
# PUT    /photo => photos#update
# DELETE /photo => photos#destroy

Parameters:

  • opts (Hash)

    resource options

Options Hash (**opts):

  • :module (String)

    the namespace for the controller

  • :path (String)

    the path prefix for the routes

  • :only (Symbol, Array<Symbol>)

    only generate routes for the given actions

  • :except (Symbol, Array<Symbol>)

    generate all routes except for the given actions



384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/rage/router/dsl.rb', line 384

def resource(*_resources, **opts, &block)
  if _resources.length > 1
    _resources.each { |_resource| resource(_resource, **opts, &block) }
    return
  end

  _module, _path, _only, _except = opts.values_at(:module, :path, :only, :except)

  actions = __filter_actions(%i(create show update destroy), _only, _except)

  resource_name = _resources[0].to_s
  controller_name = to_plural(resource_name)
  __resource_scope(resource_name, _path, _module) do
    post("/", to: "#{controller_name}#create") if actions.include?(:create)
    get("/", to: "#{controller_name}#show") if actions.include?(:show)
    patch("/", to: "#{controller_name}#update") if actions.include?(:update)
    put("/", to: "#{controller_name}#update") if actions.include?(:update)
    delete("/", to: "#{controller_name}#destroy") if actions.include?(:destroy)

    scope(controller: controller_name, &block) if block
  end
end

#resources(*_resources, **opts, &block) ⇒ Object

Note:

This helper doesn't generate the new and edit routes.

Automatically create REST routes for a resource.

Examples:

Create five REST routes, all mapping to the Photos controller:

resources :photos
# GET       /photos       => photos#index
# POST      /photos       => photos#create
# GET       /photos/:id   => photos#show
# PATCH/PUT /photos/:id   => photos#update
# DELETE    /photos/:id   => photos#destroy

Parameters:

  • opts (Hash)

    resource options

Options Hash (**opts):

  • :module (String)

    the namespace for the controller

  • :path (String)

    the path prefix for the routes

  • :only (Symbol, Array<Symbol>)

    only generate routes for the given actions

  • :except (Symbol, Array<Symbol>)

    generate all routes except for the given actions

  • :param (String)

    overrides the default param name of :id in the URL

Raises:

  • (ArgumentError)


341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/rage/router/dsl.rb', line 341

def resources(*_resources, **opts, &block)
  # support calls with multiple resources, e.g. `resources :albums, :photos`
  if _resources.length > 1
    _resources.each { |_resource| resources(_resource, **opts, &block) }
    return
  end

  _module, _path, _only, _except, _param = opts.values_at(:module, :path, :only, :except, :param)
  raise ArgumentError, ":param option can't contain colons" if _param.to_s.include?(":")

  actions = __filter_actions(@default_actions, _only, _except)

  resource = _resources[0].to_s
  _param ||= "id"

  __resource_scope(resource, _path, _module) do
    get("/", to: "#{resource}#index") if actions.include?(:index)
    post("/", to: "#{resource}#create") if actions.include?(:create)
    get("/:#{_param}", to: "#{resource}#show") if actions.include?(:show)
    patch("/:#{_param}", to: "#{resource}#update") if actions.include?(:update)
    put("/:#{_param}", to: "#{resource}#update") if actions.include?(:update)
    delete("/:#{_param}", to: "#{resource}#destroy") if actions.include?(:destroy)

    scope(path: ":#{to_singular(resource)}_#{_param}", controller: resource, &block) if block
  end
end

#root(to:) ⇒ Object

Register a new route pointing to '/'.

Examples:

root to: "photos#index"

Parameters:

  • to (String)

    the route handler in the format of "controller#action"



154
155
156
# File 'lib/rage/router/dsl.rb', line 154

def root(to:)
  __on("GET", "/", to, nil, nil)
end

#scope(opts, &block) ⇒ Object

Scopes a set of routes to the given default options.

Examples:

Route /photos to Api::PhotosController

scope module: "api" do
  get "photos", to: "photos#index"
end

Route admin/photos to PhotosController

scope path: "admin" do
  get "photos", to: "photos#index"
end

Route /like to photos#like and /dislike to photos#dislike

scope controller: "photos" do
  post "like"
  post "dislike"
end

Nested calls

scope module: "admin" do
  get "photos", to: "photos#index"

  scope path: "api", module: "api" do
    get "photos/:id", to: "photos#show"
  end
end

Parameters:

  • opts (Hash)

    scope options.

Options Hash (opts):

  • :module (String)

    the namespace for the controller

  • :path (String)

    the path prefix for the routes

  • :controller (String)

    scopes routes to a specific controller

Raises:

  • (ArgumentError)


249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/rage/router/dsl.rb', line 249

def scope(opts, &block)
  raise ArgumentError, "only :module, :path, and :controller options are accepted" if (opts.keys - @scope_opts).any?

  @path_prefixes << opts[:path].delete_prefix("/").delete_suffix("/") if opts[:path]
  @module_prefixes << opts[:module] if opts[:module]
  @controllers << opts[:controller] if opts[:controller]

  instance_eval(&block)

  @path_prefixes.pop if opts[:path]
  @module_prefixes.pop if opts[:module]
  @controllers.pop if opts[:controller]
end