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


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

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


285
286
287
288
289
# File 'lib/rage/router/dsl.rb', line 285

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



272
273
274
275
276
# File 'lib/rage/router/dsl.rb', line 272

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



147
148
149
# File 'lib/rage/router/dsl.rb', line 147

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



87
88
89
# File 'lib/rage/router/dsl.rb', line 87

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



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

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


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

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


418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/rage/router/dsl.rb', line 418

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



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

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



132
133
134
# File 'lib/rage/router/dsl.rb', line 132

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



102
103
104
# File 'lib/rage/router/dsl.rb', line 102

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



117
118
119
# File 'lib/rage/router/dsl.rb', line 117

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:

: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



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/rage/router/dsl.rb', line 387

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(@default_actions - [:index], _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)
    get("/new", to: "#{controller_name}#new") if actions.include?(:new)
    get("/edit", to: "#{controller_name}#edit") if actions.include?(:edit)
    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)


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

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)
    get("/new", to: "#{resource}#new") if actions.include?(:new)
    get("/:#{_param}/edit", to: "#{resource}#edit") if actions.include?(:edit)
    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"



156
157
158
# File 'lib/rage/router/dsl.rb', line 156

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)


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

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