hanami/lib/hanami/routes.rb

115 lines
2.9 KiB
Ruby

# frozen_string_literal: true
require_relative "constants"
require_relative "errors"
require_relative "slice/router"
module Hanami
# App routes
#
# Users are expected to inherit from this class to define their app
# routes.
#
# @example
# # config/routes.rb
# # frozen_string_literal: true
#
# require "hanami/routes"
#
# module MyApp
# class Routes < Hanami::Routes
# root to: "home.show"
# end
# end
#
# See {Hanami::Slice::Router} for the syntax allowed within the `define` block.
#
# @see Hanami::Slice::Router
# @since 2.0.0
class Routes
# Error raised when no action could be found in an app or slice container for the key given in a
# routes file.
#
# @api public
# @since 2.0.0
class MissingActionError < Hanami::Error
# @api private
def initialize(action_key, slice)
action_path = action_key.gsub(CONTAINER_KEY_DELIMITER, PATH_DELIMITER)
action_constant = slice.inflector.camelize(
"#{slice.inflector.underscore(slice.namespace.to_s)}#{PATH_DELIMITER}#{action_path}"
)
action_file = slice.root.join("#{action_path}#{RB_EXT}")
super(<<~MSG)
Could not find action with key #{action_key.inspect} in #{slice}
To fix this, define the action class #{action_constant} in #{action_file}
MSG
end
end
# Error raised when a given routes endpoint does not implement the `#call` interface required
# for Rack.
#
# @api public
# @since 2.0.0
class NotCallableEndpointError < Hanami::Error
# @api private
def initialize(endpoint)
super("#{endpoint.inspect} is not compatible with Rack. Please make sure it implements #call.")
end
end
# @api private
def self.routes
@routes ||= build_routes
end
class << self
# @api private
def build_routes(definitions = self.definitions)
return if definitions.empty?
proc do
definitions.each do |(name, args, kwargs, block)|
if block
public_send(name, *args, **kwargs, &block)
else
public_send(name, *args, **kwargs)
end
end
end
end
# @api private
def definitions
@definitions ||= []
end
private
# @api private
def supported_methods
@supported_methods ||= Slice::Router.public_instance_methods
end
# @api private
def respond_to_missing?(name, include_private = false)
supported_methods.include?(name) || super
end
# Capture all method calls that are supported by the router DSL
# so that it can be evaluated lazily during configuration/boot
# process
#
# @api private
def method_missing(name, *args, **kwargs, &block)
return super unless respond_to?(name)
definitions << [name, args, kwargs, block]
self
end
end
end
end