115 lines
2.9 KiB
Ruby
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
|