diff --git a/lib/lotus/router.rb b/lib/lotus/router.rb index abb218a..8a83e0e 100644 --- a/lib/lotus/router.rb +++ b/lib/lotus/router.rb @@ -934,5 +934,31 @@ def path(route, *args) def url(route, *args) @router.url(route, *args) end + + # Returns an routes inspector + # + # @since x.x.x + # + # @see Lotus::Routing::RoutesInspector + # + # @example + # require 'lotus/router' + # + # router = Lotus::Router.new do + # get '/', to: 'home#index' + # get '/login', to: 'sessions#new', as: :login + # post '/login', to: 'sessions#create' + # delete '/logout', to: 'sessions#destroy', as: :logout + # end + # + # puts router.inspector + # # => GET, HEAD / Home::Index + # login GET, HEAD /login Sessions::New + # POST /login Sessions::Create + # logout GET, HEAD /logout Sessions::Destroy + def inspector + require 'lotus/routing/routes_inspector' + Routing::RoutesInspector.new(@router.routes) + end end end diff --git a/lib/lotus/routing/endpoint.rb b/lib/lotus/routing/endpoint.rb index 7f2df97..2db82b0 100644 --- a/lib/lotus/routing/endpoint.rb +++ b/lib/lotus/routing/endpoint.rb @@ -30,6 +30,20 @@ class EndpointNotFound < ::StandardError # get '/rack-app', to: RackApp.new # end class Endpoint < SimpleDelegator + # @since x.x.x + def inspect + case __getobj__ + when Proc + source, line = __getobj__.source_location + lambda_inspector = " (lambda)" if __getobj__.lambda? + + "#" + when Class + __getobj__ + else + "#<#{ __getobj__.class }>" + end + end end # Routing endpoint @@ -103,9 +117,31 @@ def call(env) obj.call(env) end + # @since x.x.x + def inspect + # TODO review this implementation once the namespace feature will be + # cleaned up. + result = klass rescue nil + + if result.nil? + result = @name + result = "#{ @namespace }::#{ result }" if @namespace != Object + end + + result + end + private + # @since 0.1.0 + # @api private def obj - Utils::Class.load!(@name, @namespace).new + klass.new + end + + # @since x.x.x + # @api private + def klass + Utils::Class.load!(@name, @namespace) rescue NameError => e raise EndpointNotFound.new(e.message) end diff --git a/lib/lotus/routing/routes_inspector.rb b/lib/lotus/routing/routes_inspector.rb new file mode 100644 index 0000000..bf9e9e9 --- /dev/null +++ b/lib/lotus/routing/routes_inspector.rb @@ -0,0 +1,96 @@ +module Lotus + module Routing + # Routes inspector + # + # @since x.x.x + class RoutesInspector + # Default route formatter + # + # @since x.x.x + # @api private + FORMATTER = "%20s %-10s %-30s %-30s\n".freeze + + # Instantiate a new inspector + # + # @return [Lotus::Routing::RoutesInspector] the new instance + # + # @since x.x.x + # @api private + def initialize(routes) + @routes = routes + end + + # Return a formatted string that describes all the routes + # + # @param formatter [String] the optional formatter for a route + # + # @return [String] routes pretty print + # + # @since x.x.x + # + # @see Lotus::Routing::RoutesInspector::FORMATTER + # + # @example Default formatter + # require 'lotus/router' + # + # router = Lotus::Router.new do + # get '/', to: 'home#index' + # get '/login', to: 'sessions#new', as: :login + # post '/login', to: 'sessions#create' + # delete '/logout', to: 'sessions#destroy', as: :logout + # end + # + # puts router.inspector.to_s + # # => GET, HEAD / Home::Index + # login GET, HEAD /login Sessions::New + # POST /login Sessions::Create + # logout GET, HEAD /logout Sessions::Destroy + # + # @example Custom formatter + # require 'lotus/router' + # + # router = Lotus::Router.new do + # get '/', to: 'home#index' + # get '/login', to: 'sessions#new', as: :login + # post '/login', to: 'sessions#create' + # delete '/logout', to: 'sessions#destroy', as: :logout + # end + # + # formatter = "| %{methods} | %{name} | %{path} | %{endpoint} |\n" + # + # puts router.inspector.to_s(formatter) + # # => | GET, HEAD | | / | Home::Index | + # | GET, HEAD | login | /login | Sessions::New | + # | POST | | /login | Sessions::Create | + # | GET, HEAD | logout | /logout | Sessions::Destroy | + def to_s(formatter = FORMATTER) + result = "" + + @routes.each do |route| + result << formatter % inspect_route(route) + end + + result + end + + private + # Return a Hash compatible with formatter + # + # @return [Hash] serialized route + # + # @since x.x.x + # @api private + # + # @see Lotus::Routing::RoutesInspector#FORMATTER + # @see Lotus::Routing::RoutesInspector#to_s + def inspect_route(route) + Hash[ + name: route.name, + methods: route.request_methods.to_a.join(", "), + path: route.original_path, + endpoint: route.dest.inspect + ] + end + end + end +end diff --git a/test/routing/routes_inspector_test.rb b/test/routing/routes_inspector_test.rb new file mode 100644 index 0000000..30dbf5c --- /dev/null +++ b/test/routing/routes_inspector_test.rb @@ -0,0 +1,182 @@ +require 'test_helper' +require 'lotus/routing/routes_inspector' + +describe Lotus::Routing::RoutesInspector do + describe '#to_s' do + before do + @path = ::File.expand_path(__FILE__) + end + + describe 'named routes with procs' do + before do + @router = Lotus::Router.new do + get '/login', to: ->(env) { }, as: :login + get '/logout', to: Proc.new {|env| }, as: :logout + end + end + + it 'inspects routes' do + expected = <<-ROUTES + login GET, HEAD /login # + logout GET, HEAD /logout # +ROUTES + + @router.inspector.to_s.must_equal(expected) + end + end + + describe 'controller action syntax' do + before do + @router = Lotus::Router.new do + get '/controller/action', to: 'welcome#index' + end + end + + it 'inspects routes' + # it 'inspects routes' do + # expected = <<-ROUTES + # GET, HEAD /controller/action WelcomeController::Index +# ROUTES + + # @router.inspector.to_s.must_equal(expected) + # end + end + + describe 'lazy controller and action' do + before do + @router = Lotus::Router.new do + get '/lazy', to: 'sleepy#index' + end + + module SleepyController + class Index + end + end + end + + after do + Object.__send__(:remove_const, :SleepyController) + end + + it 'inspects routes' + # it 'inspects routes' do + # expected = <<-ROUTES + # GET, HEAD /lazy LazyController::Index +# ROUTES + + # @router.inspector.to_s.must_equal(expected) + # end + end + + describe 'missing controller and action' do + before do + @router = Lotus::Router.new do + get '/missing', to: 'missing#index' + end + end + + it 'inspects routes' do + expected = <<-ROUTES + GET, HEAD /missing Missing(::Controller::|Controller::)Index +ROUTES + + @router.inspector.to_s.must_equal(expected) + end + end + + describe 'class' do + before do + @router = Lotus::Router.new do + get '/class', to: RackMiddleware + end + end + + it 'inspects routes' + # it 'inspects routes' do + # expected = <<-ROUTES + # GET, HEAD /class RackMiddleware +# ROUTES + + # @router.inspector.to_s.must_equal(expected) + # end + end + + describe 'object' do + before do + @router = Lotus::Router.new do + get '/class', to: RackMiddlewareInstanceMethod + get '/object', to: RackMiddlewareInstanceMethod.new + end + end + + it 'inspects routes' do + expected = <<-ROUTES + GET, HEAD /class # + GET, HEAD /object # +ROUTES + + @router.inspector.to_s.must_equal(expected) + end + end + + describe 'resource' do + before do + @router = Lotus::Router.new do + resource 'identity' + end + end + + it 'inspects routes' do + expected = <<-ROUTES + new_identity GET, HEAD /identity/new Identity(::Controller::|Controller::)New + identity POST /identity Identity(::Controller::|Controller::)Create + identity GET, HEAD /identity Identity(::Controller::|Controller::)Show + edit_identity GET, HEAD /identity/edit Identity(::Controller::|Controller::)Edit + identity PATCH /identity Identity(::Controller::|Controller::)Update + identity DELETE /identity Identity(::Controller::|Controller::)Destroy +ROUTES + + @router.inspector.to_s.must_equal(expected) + end + end + + describe 'resources' do + before do + @router = Lotus::Router.new do + resources 'books' + end + end + + it 'inspects routes' do + expected = <<-ROUTES + books GET, HEAD /books Books(::Controller::|Controller::)Index + new_books GET, HEAD /books/new Books(::Controller::|Controller::)New + books POST /books Books(::Controller::|Controller::)Create + books GET, HEAD /books/:id Books(::Controller::|Controller::)Show + edit_books GET, HEAD /books/:id/edit Books(::Controller::|Controller::)Edit + books PATCH /books/:id Books(::Controller::|Controller::)Update + books DELETE /books/:id Books(::Controller::|Controller::)Destroy +ROUTES + + @router.inspector.to_s.must_equal(expected) + end + end + + describe 'with custom formatter' do + before do + @router = Lotus::Router.new do + get '/login', to: ->(env) { }, as: :login + end + end + + it 'inspects routes' do + formatter = "| %{methods} | %{name} | %{path} | %{endpoint} |\n" + expected = <<-ROUTES +| GET, HEAD | login | /login | # | +ROUTES + + @router.inspector.to_s(formatter).must_equal(expected) + end + end + end +end diff --git a/test/support/fixtures.rb b/test/support/fixtures.rb index 38c9235..eecabc2 100644 --- a/test/support/fixtures.rb +++ b/test/support/fixtures.rb @@ -225,3 +225,18 @@ def parse(body) result end end + +class RackMiddleware + def self.call(env) + end +end + +class RackMiddlewareInstanceMethod + def call(env) + end +end + +module WelcomeController + class Index + end +end