From 46129fd1b2bfc5e45806f71041bea0596236e138 Mon Sep 17 00:00:00 2001 From: Alfonso Uceda Pompa Date: Thu, 4 Dec 2014 17:04:50 +0100 Subject: [PATCH 1/4] Added hability to inspect nested routes [resolve #34] --- lib/lotus/router.rb | 4 ++ lib/lotus/routing/routes_inspector.rb | 59 +++++++++++++++++++++++---- test/routing/routes_inspector_test.rb | 42 ++++++++++++++++++- 3 files changed, 95 insertions(+), 10 deletions(-) diff --git a/lib/lotus/router.rb b/lib/lotus/router.rb index 2f90309..8c932c1 100644 --- a/lib/lotus/router.rb +++ b/lib/lotus/router.rb @@ -170,6 +170,10 @@ def initialize(options = {}, &blk) define(&blk) end + def routes + self + end + # To support defining routes in the `define` wrapper. # # @param blk [Proc] the block to define the routes diff --git a/lib/lotus/routing/routes_inspector.rb b/lib/lotus/routing/routes_inspector.rb index bf9e9e9..1c777d5 100644 --- a/lib/lotus/routing/routes_inspector.rb +++ b/lib/lotus/routing/routes_inspector.rb @@ -23,6 +23,7 @@ def initialize(routes) # Return a formatted string that describes all the routes # # @param formatter [String] the optional formatter for a route + # @param base_path [String] the base path of a nested route # # @return [String] routes pretty print # @@ -63,17 +64,57 @@ def initialize(routes) # | 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) + # + # @example Nested routes + # require 'lotus/router' + # + # class AdminLotusApp + # def call(env) + # end + # def routes + # Lotus::Router.new { + # get '/home', to: 'home#index' + # } + # end + # end + # + # router = Lotus::Router.new { + # get '/fakeroute', to: 'fake#index' + # mount AdminLotusApp, at: '/admin' + # mount Lotus::Router.new { + # get '/posts', to: 'posts#index' + # mount Lotus::Router.new { + # get '/comments', to: 'comments#index' + # }, at: '/second_mount' + # }, at: '/api' + # } + # + # formatter = "| %{methods} | %{name} | %{path} | %{endpoint} |\n" + # + # puts router.inspector.to_s(formatter) + # # => | GET, HEAD | | /fakeroute | Fake::Index | + # | GET, HEAD | | /admin/home | Home::Index | + # | GET, HEAD | | /api/posts | Posts::Index | + # | GET, HEAD | | /api/second_mount/comments | Comments::Index | + def to_s(formatter = FORMATTER, base_path = '') + @routes.each_with_object("") do |route, result| + destination = destination(route) + result << if destination && destination.respond_to?(:routes) + destination.routes.inspector.to_s(formatter, [base_path, route.path_for_generation].join) + else + formatter % inspect_route(route, base_path) + end end - - result end private + + def destination(route) + route.dest.__getobj__ + rescue + nil + end + # Return a Hash compatible with formatter # # @return [Hash] serialized route @@ -83,11 +124,11 @@ def to_s(formatter = FORMATTER) # # @see Lotus::Routing::RoutesInspector#FORMATTER # @see Lotus::Routing::RoutesInspector#to_s - def inspect_route(route) + def inspect_route(route, base_path = '') Hash[ name: route.name, methods: route.request_methods.to_a.join(", "), - path: route.original_path, + path: [base_path, route.original_path].join, endpoint: route.dest.inspect ] end diff --git a/test/routing/routes_inspector_test.rb b/test/routing/routes_inspector_test.rb index 6628f1a..8442ac4 100644 --- a/test/routing/routes_inspector_test.rb +++ b/test/routing/routes_inspector_test.rb @@ -202,5 +202,45 @@ class Index end end end + + describe 'nested routes' do + before do + class AdminLotusApp + def call(env) + end + def routes + Lotus::Router.new do + get '/home', to: 'home#index' + end + end + end + + @router = Lotus::Router.new do + get '/fakeroute', to: 'fake#index' + mount AdminLotusApp, at: '/admin' + mount Lotus::Router.new { + get '/posts', to: 'posts#index' + mount Lotus::Router.new { + get '/comments', to: 'comments#index' + }, at: '/second_mount' + }, at: '/api' + end + end + + it 'inspect routes' do + formatter = "| %{methods} | %{name} | %{path} | %{endpoint} |\n" + expectations = [ + %(| GET, HEAD | | /fakeroute | Fake::Index |), + %(| GET, HEAD | | /admin/home | Home::Index |), + %(| GET, HEAD | | /api/posts | Posts::Index |), + %(| GET, HEAD | | /api/second_mount/comments | Comments::Index |) + ] + + actual = @router.inspector.to_s(formatter) + expectations.each do |expectation| + actual.must_include(expectation) + end + end + end end -end +end \ No newline at end of file From 8b17e6268eaf1d47e311bdcf80c632863128c35a Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Sun, 7 Dec 2014 18:06:53 +0100 Subject: [PATCH 2/4] Inspector cleanup. Ref #35 --- lib/lotus/routing/route.rb | 9 ++++ lib/lotus/routing/routes_inspector.rb | 67 ++++++++++++++++++++------- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/lib/lotus/routing/route.rb b/lib/lotus/routing/route.rb index 8853467..2495f07 100644 --- a/lib/lotus/routing/route.rb +++ b/lib/lotus/routing/route.rb @@ -44,6 +44,15 @@ def generate(resolver, options = {}, &endpoint) self end + # Introspect the given route to understand if there is a wrapped + # Lotus::Router + # + # @since x.x.x + # @api private + def nested_router + dest.routes if dest.respond_to?(:routes) + end + private def to=(dest = nil, &blk) self.to dest, &blk diff --git a/lib/lotus/routing/routes_inspector.rb b/lib/lotus/routing/routes_inspector.rb index 1c777d5..70b5c04 100644 --- a/lib/lotus/routing/routes_inspector.rb +++ b/lib/lotus/routing/routes_inspector.rb @@ -1,3 +1,5 @@ +require 'lotus/utils/path_prefix' + module Lotus module Routing # Routes inspector @@ -10,6 +12,12 @@ class RoutesInspector # @api private FORMATTER = "%20s %-10s %-30s %-30s\n".freeze + # Default HTTP methods separator + # + # @since x.x.x + # @api private + HTTP_METHODS_SEPARATOR = ', '.freeze + # Instantiate a new inspector # # @return [Lotus::Routing::RoutesInspector] the new instance @@ -96,42 +104,65 @@ def initialize(routes) # | GET, HEAD | | /admin/home | Home::Index | # | GET, HEAD | | /api/posts | Posts::Index | # | GET, HEAD | | /api/second_mount/comments | Comments::Index | - def to_s(formatter = FORMATTER, base_path = '') - @routes.each_with_object("") do |route, result| - destination = destination(route) - result << if destination && destination.respond_to?(:routes) - destination.routes.inspector.to_s(formatter, [base_path, route.path_for_generation].join) + def to_s(formatter = FORMATTER, base_path = nil) + base_path = Utils::PathPrefix.new(base_path) + result = '' + + # TODO refactoring: replace conditional with polymorphism + # We're exposing too much knowledge from Routing::Route: + # #path_for_generation and #base_path + @routes.each do |route| + result << if router = route.nested_router + inspect_router(formatter, router, route, base_path) else - formatter % inspect_route(route, base_path) + inspect_route(formatter, route, base_path) end end + + result end private - def destination(route) - route.dest.__getobj__ - rescue - nil - end - - # Return a Hash compatible with formatter + # Returns a string representation of the given route + # + # @param formatter [String] the template for the output + # @param route [Lotus::Routing::Route] a route + # @param base_path [Lotus::Utils::PathPrefix] the base path # - # @return [Hash] serialized route + # @return [String] serialized route # # @since x.x.x # @api private # # @see Lotus::Routing::RoutesInspector#FORMATTER # @see Lotus::Routing::RoutesInspector#to_s - def inspect_route(route, base_path = '') - Hash[ + def inspect_route(formatter, route, base_path) + formatter % Hash[ name: route.name, - methods: route.request_methods.to_a.join(", "), - path: [base_path, route.original_path].join, + methods: route.request_methods.to_a.join(HTTP_METHODS_SEPARATOR), + path: base_path.join(route.path_for_generation), endpoint: route.dest.inspect ] end + + # Returns a string representation of the given router + # + # @param formatter [String] the template for the output + # @param route [Lotus::Routing::Route] a route + # @param route [Lotus::Router] a router + # @param base_path [Lotus::Utils::PathPrefix] the base path + # + # @return [String] serialized routes from router + # + # @since x.x.x + # @api private + # + # @see Lotus::Routing::RoutesInspector#FORMATTER + # @see Lotus::Routing::RoutesInspector#to_s + def inspect_router(formatter, router, route, base_path) + router.inspector.to_s(formatter, base_path.join(route.path_for_generation)) + end end end end From 4b9dc52e94315667d02345ebbe10c18a34052b75 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Sun, 7 Dec 2014 18:27:04 +0100 Subject: [PATCH 3/4] More Inspector tests --- test/routing/routes_inspector_test.rb | 30 ++++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/test/routing/routes_inspector_test.rb b/test/routing/routes_inspector_test.rb index 8442ac4..ea74547 100644 --- a/test/routing/routes_inspector_test.rb +++ b/test/routing/routes_inspector_test.rb @@ -208,22 +208,29 @@ class Index class AdminLotusApp def call(env) end + def routes - Lotus::Router.new do + Lotus::Router.new do get '/home', to: 'home#index' end end end + inner_router = Lotus::Router.new { + get '/comments', to: 'comments#index' + } + nested_router = Lotus::Router.new { + get '/posts', to: 'posts#index' + mount inner_router, at: '/second_mount' + } + @router = Lotus::Router.new do get '/fakeroute', to: 'fake#index' - mount AdminLotusApp, at: '/admin' - mount Lotus::Router.new { - get '/posts', to: 'posts#index' - mount Lotus::Router.new { - get '/comments', to: 'comments#index' - }, at: '/second_mount' - }, at: '/api' + mount AdminLotusApp, at: '/admin' + mount nested_router, at: '/api' + mount RackMiddleware, at: '/class' + mount RackMiddlewareInstanceMethod, at: '/instance_from_class' + mount RackMiddlewareInstanceMethod.new, at: '/instance' end end @@ -233,7 +240,10 @@ def routes %(| GET, HEAD | | /fakeroute | Fake::Index |), %(| GET, HEAD | | /admin/home | Home::Index |), %(| GET, HEAD | | /api/posts | Posts::Index |), - %(| GET, HEAD | | /api/second_mount/comments | Comments::Index |) + %(| GET, HEAD | | /api/second_mount/comments | Comments::Index |), + %(| | | /class | RackMiddleware |), + %(| | | /instance_from_class | # |), + %(| | | /instance | # |) ] actual = @router.inspector.to_s(formatter) @@ -243,4 +253,4 @@ def routes end end end -end \ No newline at end of file +end From c9854ab46c94c89e604845bd1e711223d82c5e4f Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Thu, 11 Dec 2014 09:36:44 +0100 Subject: [PATCH 4/4] Drop Rubinius support for now :broken_heart: --- .travis.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 492cfd5..df66795 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ rvm: - 2.1.4 - 2.1.5 - 2.2 - - rbx-2 matrix: include: @@ -21,3 +20,4 @@ matrix: env: JRUBY_OPTS="--2.1" allow_failures: - rvm: jruby-head + - rbx-2 diff --git a/README.md b/README.md index 78aa210..c8203bc 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Rack compatible, lightweight and fast HTTP Router for [Lotus](http://lotusrb.org ## Rubies -__Lotus::Router__ supports Ruby (MRI) 2+, JRuby 1.7 (with 2.0 mode) and Rubinius 2.3.0+. +__Lotus::Router__ supports Ruby (MRI) 2+, JRuby 1.7 (with 2.0 mode) ## Installation