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

Inherits:
Object
  • Object
show all
Defined in:
lib/rage/router/dsl.rb

Overview

This class implements routing logic for your application, providing 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


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

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


268
269
270
271
272
# File 'lib/rage/router/dsl.rb', line 268

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



255
256
257
258
259
# File 'lib/rage/router/dsl.rb', line 255

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

#delete(path, to: nil, constraints: nil, defaults: 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



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

def delete(path, to: nil, constraints: nil, defaults: nil)
  __on("DELETE", path, to, constraints, defaults)
end

#get(path, to: nil, constraints: nil, defaults: 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



74
75
76
# File 'lib/rage/router/dsl.rb', line 74

def get(path, to: nil, constraints: nil, defaults: nil)
  __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



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/rage/router/dsl.rb', line 156

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


297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/rage/router/dsl.rb', line 297

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(*args) ⇒ Object

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

Examples:

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


361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/rage/router/dsl.rb', line 361

def mount(*args)
  if args.first.is_a?(Hash)
    app = args.first.keys.first
    at = args.first.values.first
    via = args[0][:via]
  else
    app = args.first
    at = args[1][:at]
    via = args[1][:via]
  end

  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



194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/rage/router/dsl.rb', line 194

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) ⇒ 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



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

def patch(path, to: nil, constraints: nil, defaults: nil)
  __on("PATCH", path, to, constraints, defaults)
end

#post(path, to: nil, constraints: nil, defaults: 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



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

def post(path, to: nil, constraints: nil, defaults: nil)
  __on("POST", path, to, constraints, defaults)
end

#put(path, to: nil, constraints: nil, defaults: 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



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

def put(path, to: nil, constraints: nil, defaults: nil)
  __on("PUT", path, to, constraints, defaults)
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


320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/rage/router/dsl.rb', line 320

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 ":param option can't contain colons" if _param.to_s.include?(":")

  _only = Array(_only) if _only
  _except = Array(_except) if _except
  actions = @default_actions.select do |action|
    (_only.nil? || _only.include?(action)) && (_except.nil? || !_except.include?(action))
  end

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

  scope_opts = { path: _path }
  scope_opts[:module] = _module if _module

  scope(scope_opts) 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”



139
140
141
# File 'lib/rage/router/dsl.rb', line 139

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)

    module option

  • :path (String)

    path option

  • :controller (String)

    controller option

Raises:

  • (ArgumentError)


234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/rage/router/dsl.rb', line 234

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