lotus-router 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 53b681f4148b22f7bbcd6521eeb8b241673d9253
4
- data.tar.gz: 94af70e6ee5f0d7a8e4ebf84c5ddc14a3bdba63d
3
+ metadata.gz: 385192d4a396ff6a0e91dabff73a8351c55848ed
4
+ data.tar.gz: efd06ad2632579b10f7cf6229aba7b3d760fbc08
5
5
  SHA512:
6
- metadata.gz: 98ae99df5474dbde90fc2c9fc8f27f09877cb1327a00a01ed7e3a897d37ce4501af4dce06a45bc78270e3a908fb6ac833d7784d24ea63be56aefa3c79b5a6599
7
- data.tar.gz: 6200a6ba3498a91a123161f8f23e0e79e92a2bc903647fe43ac644733564148de73571da35d49f04fada7a34ac278f7dd38ff3b833fdc136e2aebca5c9d66838
6
+ metadata.gz: a0c1078e04043023b44f1a72189e6890dd8e1ccfa53f966bb6624527f2444fa5825ede6944e17c0b171b2eecf393cb1f39573e65073a74b512813114c37a00e0
7
+ data.tar.gz: 662945b950952171f02220b318e808b87c44ad44b54034defff58772fd30f92d3ab9036737433888ae06c2b8be07ccbb52d6e4f1f8d739c2f33dffb5e1281d01
@@ -1,6 +1,21 @@
1
1
  # Lotus::Router
2
2
  Rack compatible HTTP router for Ruby
3
3
 
4
+ ## v0.5.0 - 2016-01-12
5
+ ### Added
6
+ - [Luca Guidi] Added `Lotus::Router#recognize` as a testing facility. Example `router.recognize('/') # => associated route`
7
+ - [Luca Guidi] Added `Lotus::Router.define` in order to wrap routes definitions in `config/routes.rb` when `Lotus::Router` is used outside of Lotus projects
8
+ - [David Strauß] Make `Lotus::Routing::Parsing::JsonParser` compatible with `application/vnd.api+json` MIME Type
9
+ - [Alfonso Uceda Pompa] Improved exception messages for `Lotus::Router#path` and `#url`
10
+
11
+ ### Fixed
12
+ - [Alfonso Uceda Pompa] Ensure `Lotus::Router#path` and `#url` to generate correct URL for mounted applications
13
+ - [Vladislav Zarakovsky] Ensure Force SSL mode to respect Rack SPEC
14
+
15
+ ### Changed
16
+ - [Alfonso Uceda Pompa] A failure for body parsers raises a `Lotus::Routing::Parsing::BodyParsingError` exception
17
+ - [Karim Tarek] Introduced `Lotus::Router::Error` and let all the framework exceptions to inherit from it.
18
+
4
19
  ## v0.4.3 - 2015-09-30
5
20
  ### Added
6
21
  - [Luca Guidi] Official support for JRuby 9k+
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2014-2015 Luca Guidi
1
+ Copyright © 2014-2016 Luca Guidi
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -568,18 +568,106 @@ router.path(:new_user_favorites, user_id: 1) # => /users/1/favorites/new
568
568
  router.path(:edit_user_favorites, user_id: 1, id: 2) # => /users/1/favorites/2/edit
569
569
  ```
570
570
 
571
+ ### Body Parsers
572
+
573
+ Rack ignores request bodies unless they come from a form submission.
574
+ If we have a JSON endpoint, the payload isn't available in the params hash:
575
+
576
+ ```ruby
577
+ Rack::Request.new(env).params # => {}
578
+ ```
579
+
580
+ This feature enables body parsing for specific MIME Types.
581
+ It comes with a built-in JSON parser and allows to pass custom parsers.
582
+
583
+ #### JSON Parsing
584
+
585
+ ```ruby
586
+ require 'lotus/router'
587
+
588
+ endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }
589
+
590
+ router = Lotus::Router.new(parsers: [:json]) do
591
+ patch '/books/:id', to: endpoint
592
+ end
593
+ ```
594
+
595
+ ```shell
596
+ curl http://localhost:2300/books/1 \
597
+ -H "Content-Type: application/json" \
598
+ -H "Accept: application/json" \
599
+ -d '{"published":"true"}' \
600
+ -X PATCH
601
+
602
+ # => [200, {}, ["{:published=>\"true\",:id=>\"1\"}"]]
603
+ ```
604
+
605
+ If the json can't be parsed an exception is raised:
606
+
607
+ ```ruby
608
+ Lotus::Routing::Parsing::BodyParsingError
609
+ ```
610
+
611
+ #### Custom Parsers
612
+
613
+ ```ruby
614
+ require 'lotus/router'
615
+
616
+ # See Lotus::Routing::Parsing::Parser
617
+ class XmlParser
618
+ def mime_types
619
+ ['application/xml', 'text/xml']
620
+ end
621
+
622
+ # Parse body and return a Hash
623
+ def parse(body)
624
+ # parse xml
625
+ rescue SomeXmlParsingError => e
626
+ raise Lotus::Routing::Parsing::BodyParsingError.new(e)
627
+ end
628
+ end
629
+
630
+ endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }
631
+
632
+ router = Lotus::Router.new(parsers: [XmlParser.new]) do
633
+ patch '/authors/:id', to: endpoint
634
+ end
635
+ ```
636
+
637
+ ```shell
638
+ curl http://localhost:2300/authors/1 \
639
+ -H "Content-Type: application/xml" \
640
+ -H "Accept: application/xml" \
641
+ -d '<name>LG</name>' \
642
+ -X PATCH
643
+
644
+ # => [200, {}, ["{:name=>\"LG\",:id=>\"1\"}"]]
645
+ ```
646
+
571
647
  ## Testing
572
648
 
573
649
  ```ruby
574
650
  require 'lotus/router'
575
- require 'rack/request'
576
651
 
577
652
  router = Lotus::Router.new do
578
- get '/', to: ->(env) { [200, {}, ['Hi!']] }
653
+ get '/books/:id', to: 'books#show', as: :book
579
654
  end
580
655
 
581
- app = Rack::MockRequest.new(router)
582
- app.get('/') # => #<Rack::MockResponse:0x007fc4540dc238 ...>
656
+ route = router.recognize('/books/23')
657
+ route.verb # "GET"
658
+ route.action # => "books#show"
659
+ route.params # => {:id=>"23"}
660
+ route.routable? # => true
661
+
662
+ route = router.recognize(:book, id: 23)
663
+ route.verb # "GET"
664
+ route.action # => "books#show"
665
+ route.params # => {:id=>"23"}
666
+ route.routable? # => true
667
+
668
+ route = router.recognize('/books/23', method: :post)
669
+ route.verb # "POST"
670
+ route.routable? # => false
583
671
  ```
584
672
 
585
673
  ## Versioning
@@ -601,4 +689,4 @@ Thanks to Joshua Hull ([@joshbuddy](https://github.com/joshbuddy)) for his
601
689
 
602
690
  ## Copyright
603
691
 
604
- Copyright © 2014-2015 Luca Guidi – Released under MIT License
692
+ Copyright © 2014-2016 Luca Guidi – Released under MIT License
@@ -1,7 +1,9 @@
1
+ require 'rack/request'
1
2
  require 'lotus/routing/http_router'
2
3
  require 'lotus/routing/namespace'
3
4
  require 'lotus/routing/resource'
4
5
  require 'lotus/routing/resources'
6
+ require 'lotus/routing/error'
5
7
 
6
8
  module Lotus
7
9
  # Rack compatible, lightweight and fast HTTP Router.
@@ -71,6 +73,57 @@ module Lotus
71
73
  #
72
74
  # # All the requests starting with "/api" will be forwarded to Api::App
73
75
  class Router
76
+ # This error is raised when <tt>#call</tt> is invoked on a non-routable
77
+ # recognized route.
78
+ #
79
+ # @since 0.5.0
80
+ #
81
+ # @see Lotus::Router#recognize
82
+ # @see Lotus::Routing::RecognizedRoute
83
+ # @see Lotus::Routing::RecognizedRoute#call
84
+ # @see Lotus::Routing::RecognizedRoute#routable?
85
+ class NotRoutableEndpointError < Lotus::Routing::Error
86
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
87
+ PATH_INFO = 'PATH_INFO'.freeze
88
+
89
+ def initialize(env)
90
+ super %(Cannot find routable endpoint for #{ env[REQUEST_METHOD] } "#{ env[PATH_INFO] }")
91
+ end
92
+ end
93
+
94
+ # Returns the given block as it is.
95
+ #
96
+ # When Lotus::Router is used as a standalone gem and the routes are defined
97
+ # into a configuration file, some systems could raise an exception.
98
+ #
99
+ # Imagine the following file into a Ruby on Rails application:
100
+ #
101
+ # get '/', to: 'api#index'
102
+ #
103
+ # Because Ruby on Rails in production mode use to eager load code and the
104
+ # routes file uses top level method calls, it crashes the application.
105
+ #
106
+ # If we wrap these routes with <tt>Lotus::Router.define</tt>, the block
107
+ # doesn't get yielded but just returned to the caller as it is.
108
+ #
109
+ # Usually the receiver of this block is <tt>Lotus::Router#initialize</tt>,
110
+ # which finally evaluates the block.
111
+ #
112
+ # @param blk [Proc] a set of route definitions
113
+ #
114
+ # @return [Proc] the given block
115
+ #
116
+ # @since 0.5.0
117
+ #
118
+ # @example
119
+ # # apps/web/config/routes.rb
120
+ # Lotus::Router.define do
121
+ # get '/', to: 'home#index'
122
+ # end
123
+ def self.define(&blk)
124
+ blk
125
+ end
126
+
74
127
  # Initialize the router.
75
128
  #
76
129
  # @param options [Hash] the options to initialize the router
@@ -878,6 +931,125 @@ module Lotus
878
931
  @router.call(env)
879
932
  end
880
933
 
934
+ # Recognize the given env, path, or name and return a route for testing
935
+ # inspection.
936
+ #
937
+ # If the route cannot be recognized, it still returns an object for testing
938
+ # inspection.
939
+ #
940
+ # @param env [Hash, String, Symbol] Rack env, path or route name
941
+ # @param options [Hash] a set of options for Rack env or route params
942
+ # @param params [Hash] a set of params
943
+ #
944
+ # @return [Lotus::Routing::RecognizedRoute] the recognized route
945
+ #
946
+ # @since 0.5.0
947
+ #
948
+ # @see Lotus::Router#env_for
949
+ # @see Lotus::Routing::RecognizedRoute
950
+ #
951
+ # @example Successful Path Recognition
952
+ # require 'lotus/router'
953
+ #
954
+ # router = Lotus::Router.new do
955
+ # get '/books/:id', to: 'books#show', as: :book
956
+ # end
957
+ #
958
+ # route = router.recognize('/books/23')
959
+ # route.verb # => "GET" (default)
960
+ # route.routable? # => true
961
+ # route.params # => {:id=>"23"}
962
+ #
963
+ # @example Successful Rack Env Recognition
964
+ # require 'lotus/router'
965
+ #
966
+ # router = Lotus::Router.new do
967
+ # get '/books/:id', to: 'books#show', as: :book
968
+ # end
969
+ #
970
+ # route = router.recognize(Rack::MockRequest.env_for('/books/23'))
971
+ # route.verb # => "GET" (default)
972
+ # route.routable? # => true
973
+ # route.params # => {:id=>"23"}
974
+ #
975
+ # @example Successful Named Route Recognition
976
+ # require 'lotus/router'
977
+ #
978
+ # router = Lotus::Router.new do
979
+ # get '/books/:id', to: 'books#show', as: :book
980
+ # end
981
+ #
982
+ # route = router.recognize(:book, id: 23)
983
+ # route.verb # => "GET" (default)
984
+ # route.routable? # => true
985
+ # route.params # => {:id=>"23"}
986
+ #
987
+ # @example Failing Recognition For Unknown Path
988
+ # require 'lotus/router'
989
+ #
990
+ # router = Lotus::Router.new do
991
+ # get '/books/:id', to: 'books#show', as: :book
992
+ # end
993
+ #
994
+ # route = router.recognize('/books')
995
+ # route.verb # => "GET" (default)
996
+ # route.routable? # => false
997
+ #
998
+ # @example Failing Recognition For Path With Wrong HTTP Verb
999
+ # require 'lotus/router'
1000
+ #
1001
+ # router = Lotus::Router.new do
1002
+ # get '/books/:id', to: 'books#show', as: :book
1003
+ # end
1004
+ #
1005
+ # route = router.recognize('/books/23', method: :post)
1006
+ # route.verb # => "POST"
1007
+ # route.routable? # => false
1008
+ #
1009
+ # @example Failing Recognition For Rack Env With Wrong HTTP Verb
1010
+ # require 'lotus/router'
1011
+ #
1012
+ # router = Lotus::Router.new do
1013
+ # get '/books/:id', to: 'books#show', as: :book
1014
+ # end
1015
+ #
1016
+ # route = router.recognize(Rack::MockRequest.env_for('/books/23', method: :post))
1017
+ # route.verb # => "POST"
1018
+ # route.routable? # => false
1019
+ #
1020
+ # @example Failing Recognition Named Route With Wrong Params
1021
+ # require 'lotus/router'
1022
+ #
1023
+ # router = Lotus::Router.new do
1024
+ # get '/books/:id', to: 'books#show', as: :book
1025
+ # end
1026
+ #
1027
+ # route = router.recognize(:book)
1028
+ # route.verb # => "GET" (default)
1029
+ # route.routable? # => false
1030
+ #
1031
+ # @example Failing Recognition Named Route With Wrong HTTP Verb
1032
+ # require 'lotus/router'
1033
+ #
1034
+ # router = Lotus::Router.new do
1035
+ # get '/books/:id', to: 'books#show', as: :book
1036
+ # end
1037
+ #
1038
+ # route = router.recognize(:book, {method: :post}, {id: 1})
1039
+ # route.verb # => "POST"
1040
+ # route.routable? # => false
1041
+ # route.params # => {:id=>"1"}
1042
+ def recognize(env, options = {}, params = nil)
1043
+ require 'lotus/routing/recognized_route'
1044
+
1045
+ env = env_for(env, options, params)
1046
+ responses, _ = *@router.recognize(env)
1047
+
1048
+ Routing::RecognizedRoute.new(
1049
+ responses.nil? ? responses : responses.first,
1050
+ env, @router)
1051
+ end
1052
+
881
1053
  # Generate an relative URL for a specified named route.
882
1054
  # The additional arguments will be used to compose the relative URL - in
883
1055
  # case it has tokens to match - and for compose the query string.
@@ -957,5 +1129,36 @@ module Lotus
957
1129
  require 'lotus/routing/routes_inspector'
958
1130
  Routing::RoutesInspector.new(@router.routes)
959
1131
  end
1132
+
1133
+ protected
1134
+
1135
+ # Fabricate Rack env for the given Rack env, path or named route
1136
+ #
1137
+ # @param env [Hash, String, Symbol] Rack env, path or route name
1138
+ # @param options [Hash] a set of options for Rack env or route params
1139
+ # @param params [Hash] a set of params
1140
+ #
1141
+ # @return [Hash] Rack env
1142
+ #
1143
+ # @since 0.5.0
1144
+ # @api private
1145
+ #
1146
+ # @see Lotus::Router#recognize
1147
+ # @see http://www.rubydoc.info/github/rack/rack/Rack%2FMockRequest.env_for
1148
+ def env_for(env, options = {}, params = nil)
1149
+ env = case env
1150
+ when String
1151
+ Rack::MockRequest.env_for(env, options)
1152
+ when Symbol
1153
+ begin
1154
+ url = path(env, params || options)
1155
+ return env_for(url, options)
1156
+ rescue Lotus::Routing::InvalidRouteException
1157
+ {}
1158
+ end
1159
+ else
1160
+ env
1161
+ end
1162
+ end
960
1163
  end
961
1164
  end
@@ -1,6 +1,6 @@
1
1
  module Lotus
2
2
  class Router
3
3
  # @since 0.1.0
4
- VERSION = '0.4.3'.freeze
4
+ VERSION = '0.5.0'.freeze
5
5
  end
6
6
  end
@@ -1,4 +1,5 @@
1
1
  require 'delegate'
2
+ require 'lotus/routing/error'
2
3
  require 'lotus/utils/class'
3
4
 
4
5
  module Lotus
@@ -7,7 +8,7 @@ module Lotus
7
8
  # This is raised when the router fails to load an endpoint at the runtime.
8
9
  #
9
10
  # @since 0.1.0
10
- class EndpointNotFound < ::StandardError
11
+ class EndpointNotFound < Lotus::Routing::Error
11
12
  end
12
13
 
13
14
  # Routing endpoint
@@ -0,0 +1,7 @@
1
+ module Lotus
2
+ module Routing
3
+ # @since 0.5.0
4
+ class Error < ::StandardError
5
+ end
6
+ end
7
+ end
@@ -81,7 +81,7 @@ module Lotus
81
81
  # @api private
82
82
  IDEMPOTENT_METHODS = ['GET', 'HEAD'].freeze
83
83
 
84
- EMPTY_BODY = ''.freeze
84
+ EMPTY_BODY = [].freeze
85
85
 
86
86
  # Initialize ForceSsl.
87
87
  #
@@ -4,6 +4,7 @@ require 'lotus/routing/endpoint_resolver'
4
4
  require 'lotus/routing/route'
5
5
  require 'lotus/routing/parsers'
6
6
  require 'lotus/routing/force_ssl'
7
+ require 'lotus/routing/error'
7
8
  require 'lotus/utils/path_prefix'
8
9
 
9
10
  Lotus::Utils::IO.silence_warnings do
@@ -17,7 +18,7 @@ module Lotus
17
18
  # given arguments.
18
19
  #
19
20
  # @since 0.1.0
20
- class InvalidRouteException < ::StandardError
21
+ class InvalidRouteException < Lotus::Routing::Error
21
22
  end
22
23
 
23
24
  # HTTP router
@@ -30,6 +31,16 @@ module Lotus
30
31
  # @since 0.1.0
31
32
  # @api private
32
33
  class HttpRouter < ::HttpRouter
34
+ # Script name - rack enviroment variable
35
+ #
36
+ # @since 0.5.0
37
+ # @api private
38
+ SCRIPT_NAME = 'SCRIPT_NAME'.freeze
39
+
40
+ # @since 0.5.0
41
+ # @api private
42
+ attr_reader :namespace
43
+
33
44
  # Initialize the router.
34
45
  #
35
46
  # @see Lotus::Router#initialize
@@ -39,9 +50,10 @@ module Lotus
39
50
  def initialize(options = {}, &blk)
40
51
  super(options, &nil)
41
52
 
42
- @default_scheme = options[:scheme] if options[:scheme]
43
- @default_host = options[:host] if options[:host]
44
- @default_port = options[:port] if options[:port]
53
+ @namespace = options[:namespace] if options[:namespace]
54
+ @default_scheme = options[:scheme] if options[:scheme]
55
+ @default_host = options[:host] if options[:host]
56
+ @default_port = options[:port] if options[:port]
45
57
  @route_class = options[:route] || Routing::Route
46
58
  @resolver = options[:resolver] || Routing::EndpointResolver.new(options)
47
59
  @parsers = Routing::Parsers.new(options[:parsers])
@@ -145,16 +157,24 @@ module Lotus
145
157
  end
146
158
  end
147
159
 
148
- private
149
- def add_with_request_method(path, method, opts = {}, &app)
150
- super.generate(@resolver, opts, &app)
160
+ # @api private
161
+ # @since 0.5.0
162
+ def rewrite_path_info(env, request)
163
+ super
164
+ env[SCRIPT_NAME] = @prefix + env[SCRIPT_NAME]
151
165
  end
152
166
 
167
+ private
168
+
153
169
  def _rescue_url_recognition
154
170
  yield
155
171
  rescue ::HttpRouter::InvalidRouteException,
156
172
  ::HttpRouter::TooManyParametersException => e
157
- raise Routing::InvalidRouteException.new(e.message)
173
+ raise Routing::InvalidRouteException.new("#{ e.message } - please check given arguments")
174
+ end
175
+
176
+ def add_with_request_method(path, method, opts = {}, &app)
177
+ super.generate(@resolver, opts, &app)
158
178
  end
159
179
 
160
180
  def _custom_path(uri_string)
@@ -5,11 +5,22 @@ module Lotus
5
5
  module Parsing
6
6
  class JsonParser < Parser
7
7
  def mime_types
8
- ['application/json']
8
+ ['application/json', 'application/vnd.api+json']
9
9
  end
10
10
 
11
+ # Parse a json string
12
+ #
13
+ # @param body [String] a json string
14
+ #
15
+ # @return [Hash] the parsed json
16
+ #
17
+ # @raise [Lotus::Routing::Parsing::BodyParsingError] when the body can't be parsed.
18
+ #
19
+ # @since 0.2.0
11
20
  def parse(body)
12
21
  JSON.parse(body)
22
+ rescue JSON::ParserError => e
23
+ raise BodyParsingError.new(e.message)
13
24
  end
14
25
  end
15
26
  end
@@ -1,16 +1,27 @@
1
1
  require 'lotus/utils/class'
2
2
  require 'lotus/utils/string'
3
+ require 'lotus/routing/error'
3
4
 
4
5
  module Lotus
5
6
  module Routing
6
7
  module Parsing
7
- class UnknownParserError < ::StandardError
8
+ # Body parsing error
9
+ # This is raised when parser fails to parse the body
10
+ #
11
+ # @since 0.5.0
12
+ class BodyParsingError < Lotus::Routing::Error
13
+ end
14
+
15
+ # @since 0.2.0
16
+ class UnknownParserError < Lotus::Routing::Error
8
17
  def initialize(parser)
9
18
  super("Unknown Parser: `#{ parser }'")
10
19
  end
11
20
  end
12
21
 
22
+ # @since 0.2.0
13
23
  class Parser
24
+ # @since 0.2.0
14
25
  def self.for(parser)
15
26
  case parser
16
27
  when String, Symbol
@@ -20,15 +31,19 @@ module Lotus
20
31
  end
21
32
  end
22
33
 
34
+ # @since 0.2.0
23
35
  def mime_types
24
36
  raise NotImplementedError
25
37
  end
26
38
 
39
+ # @since 0.2.0
27
40
  def parse(body)
28
41
  Hash.new
29
42
  end
30
43
 
31
44
  private
45
+ # @since 0.2.0
46
+ # @api private
32
47
  def self.require_parser(parser)
33
48
  require "lotus/routing/parsing/#{ parser }_parser"
34
49
 
@@ -0,0 +1,153 @@
1
+ require 'lotus/utils/string'
2
+
3
+ module Lotus
4
+ module Routing
5
+ # Represents a result of router path recognition.
6
+ #
7
+ # @since 0.5.0
8
+ #
9
+ # @see Lotus::Router#recognize
10
+ class RecognizedRoute
11
+ # @since 0.5.0
12
+ # @api private
13
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
14
+
15
+ # @since 0.5.0
16
+ # @api private
17
+ NAMESPACE = '%s::'.freeze
18
+
19
+ # @since 0.5.0
20
+ # @api private
21
+ NAMESPACE_REPLACEMENT = ''.freeze
22
+
23
+ # @since 0.5.0
24
+ # @api private
25
+ ACTION_PATH_SEPARATOR = '/'.freeze
26
+
27
+ # @since 0.5.0
28
+ # @api public
29
+ attr_reader :params
30
+
31
+ # Creates a new instance
32
+ #
33
+ # @param response [HttpRouter::Response] raw response of recognition
34
+ # @param env [Hash] Rack env
35
+ # @param router [Lotus::Routing::HttpRouter] low level router
36
+ #
37
+ # @return [Lotus::Routing::RecognizedRoute]
38
+ #
39
+ # @since 0.5.0
40
+ # @api private
41
+ def initialize(response, env, router)
42
+ @env = env
43
+
44
+ unless response.nil?
45
+ @endpoint = response.route.dest
46
+ @params = response.params
47
+ end
48
+
49
+ @namespace = router.namespace
50
+ @action_separator = router.action_separator
51
+ end
52
+
53
+ # Rack protocol compatibility
54
+ #
55
+ # @param env [Hash] Rack env
56
+ #
57
+ # @return [Array] serialized Rack response
58
+ #
59
+ # @raise [Lotus::Router::NotRoutableEndpointError] if not routable
60
+ #
61
+ # @since 0.5.0
62
+ # @api public
63
+ #
64
+ # @see Lotus::Routing::RecognizedRoute#routable?
65
+ # @see Lotus::Router::NotRoutableEndpointError
66
+ def call(env)
67
+ if routable?
68
+ @endpoint.call(env)
69
+ else
70
+ raise Lotus::Router::NotRoutableEndpointError.new(@env)
71
+ end
72
+ end
73
+
74
+ # HTTP verb (aka method)
75
+ #
76
+ # @return [String]
77
+ #
78
+ # @since 0.5.0
79
+ # @api public
80
+ def verb
81
+ @env[REQUEST_METHOD]
82
+ end
83
+
84
+ # Action name
85
+ #
86
+ # @return [String]
87
+ #
88
+ # @since 0.5.0
89
+ # @api public
90
+ #
91
+ # @see Lotus::Router#recognize
92
+ #
93
+ # @example
94
+ # require 'lotus/router'
95
+ #
96
+ # router = Lotus::Router.new do
97
+ # get '/books/:id', to: 'books#show'
98
+ # end
99
+ #
100
+ # puts router.recognize('/books/23').action # => "books#show"
101
+ def action
102
+ namespace = NAMESPACE % @namespace
103
+
104
+ if destination.match(namespace)
105
+ Lotus::Utils::String.new(
106
+ destination.sub(namespace, NAMESPACE_REPLACEMENT)
107
+ ).underscore.rsub(ACTION_PATH_SEPARATOR, @action_separator)
108
+ else
109
+ destination
110
+ end
111
+ end
112
+
113
+ # Check if routable
114
+ #
115
+ # @return [TrueClass,FalseClass]
116
+ #
117
+ # @since 0.5.0
118
+ # @api public
119
+ #
120
+ # @see Lotus::Router#recognize
121
+ #
122
+ # @example
123
+ # require 'lotus/router'
124
+ #
125
+ # router = Lotus::Router.new do
126
+ # get '/', to: 'home#index'
127
+ # end
128
+ #
129
+ # puts router.recognize('/').routable? # => true
130
+ # puts router.recognize('/foo').routable? # => false
131
+ def routable?
132
+ !!@endpoint
133
+ end
134
+
135
+ private
136
+
137
+ # @since 0.5.0
138
+ # @api private
139
+ #
140
+ # @see Lotus::Routing::Endpoint
141
+ def destination
142
+ @destination ||= begin
143
+ case k = @endpoint.__getobj__
144
+ when Class
145
+ k
146
+ else
147
+ k.class
148
+ end.name
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -18,6 +18,29 @@ module Lotus
18
18
  # @api private
19
19
  HTTP_METHODS_SEPARATOR = ', '.freeze
20
20
 
21
+ # Default inspector header hash values
22
+ #
23
+ # @since 0.5.0
24
+ # @api private
25
+ INSPECTOR_HEADER_HASH = Hash[
26
+ name: 'Name',
27
+ methods: 'Method',
28
+ path: 'Path',
29
+ endpoint: 'Action'
30
+ ].freeze
31
+
32
+ # Default inspector header name values
33
+ #
34
+ # @since 0.5.0
35
+ # @api private
36
+ INSPECTOR_HEADER_NAME = 'Name'.freeze
37
+
38
+ # Empty line string
39
+ #
40
+ # @since 0.5.0
41
+ # @api private
42
+ EMPTY_LINE = "\n".freeze
43
+
21
44
  # Instantiate a new inspector
22
45
  #
23
46
  # @return [Lotus::Routing::RoutesInspector] the new instance
@@ -50,8 +73,10 @@ module Lotus
50
73
  # end
51
74
  #
52
75
  # puts router.inspector.to_s
53
- # # => GET, HEAD / Home::Index
54
- # login GET, HEAD /login Sessions::New
76
+ # # => Name Method Path Action
77
+ #
78
+ # GET, HEAD / Home::Index
79
+ # login GET, HEAD /login Sessions::New
55
80
  # POST /login Sessions::Create
56
81
  # logout GET, HEAD /logout Sessions::Destroy
57
82
  #
@@ -68,7 +93,9 @@ module Lotus
68
93
  # formatter = "| %{methods} | %{name} | %{path} | %{endpoint} |\n"
69
94
  #
70
95
  # puts router.inspector.to_s(formatter)
71
- # # => | GET, HEAD | | / | Home::Index |
96
+ # # => | Method | Name | Path | Action |
97
+ #
98
+ # | GET, HEAD | | / | Home::Index |
72
99
  # | GET, HEAD | login | /login | Sessions::New |
73
100
  # | POST | | /login | Sessions::Create |
74
101
  # | GET, HEAD | logout | /logout | Sessions::Destroy |
@@ -100,10 +127,12 @@ module Lotus
100
127
  # formatter = "| %{methods} | %{name} | %{path} | %{endpoint} |\n"
101
128
  #
102
129
  # puts router.inspector.to_s(formatter)
103
- # # => | GET, HEAD | | /fakeroute | Fake::Index |
104
- # | GET, HEAD | | /admin/home | Home::Index |
105
- # | GET, HEAD | | /api/posts | Posts::Index |
106
- # | GET, HEAD | | /api/second_mount/comments | Comments::Index |
130
+ # # => | Method | Name | Path | Action |
131
+ #
132
+ # | GET, HEAD | | /fakeroute | Fake::Index |
133
+ # | GET, HEAD | | /admin/home | Home::Index |
134
+ # | GET, HEAD | | /api/posts | Posts::Index |
135
+ # | GET, HEAD | | /api/second_mount/comments | Comments::Index |
107
136
  def to_s(formatter = FORMATTER, base_path = nil)
108
137
  base_path = Utils::PathPrefix.new(base_path)
109
138
  result = ''
@@ -119,6 +148,10 @@ module Lotus
119
148
  end
120
149
  end
121
150
 
151
+ unless result.include?(INSPECTOR_HEADER_NAME)
152
+ result.insert(0, formatter % INSPECTOR_HEADER_HASH + EMPTY_LINE)
153
+ end
154
+
122
155
  result
123
156
  end
124
157
 
@@ -20,9 +20,10 @@ Gem::Specification.new do |spec|
20
20
  spec.required_ruby_version = '>= 2.0.0'
21
21
 
22
22
  spec.add_dependency 'http_router', '~> 0.11'
23
- spec.add_dependency 'lotus-utils', '~> 0.5', '>= 0.5.1'
23
+ spec.add_dependency 'lotus-utils', '~> 0.6'
24
24
 
25
25
  spec.add_development_dependency 'bundler', '~> 1.5'
26
26
  spec.add_development_dependency 'minitest', '~> 5'
27
27
  spec.add_development_dependency 'rake', '~> 10'
28
+ spec.add_development_dependency 'rack-test', '~> 0.6'
28
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lotus-router
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-09-30 00:00:00.000000000 Z
13
+ date: 2016-01-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: http_router
@@ -32,20 +32,14 @@ dependencies:
32
32
  requirements:
33
33
  - - "~>"
34
34
  - !ruby/object:Gem::Version
35
- version: '0.5'
36
- - - ">="
37
- - !ruby/object:Gem::Version
38
- version: 0.5.1
35
+ version: '0.6'
39
36
  type: :runtime
40
37
  prerelease: false
41
38
  version_requirements: !ruby/object:Gem::Requirement
42
39
  requirements:
43
40
  - - "~>"
44
41
  - !ruby/object:Gem::Version
45
- version: '0.5'
46
- - - ">="
47
- - !ruby/object:Gem::Version
48
- version: 0.5.1
42
+ version: '0.6'
49
43
  - !ruby/object:Gem::Dependency
50
44
  name: bundler
51
45
  requirement: !ruby/object:Gem::Requirement
@@ -88,6 +82,20 @@ dependencies:
88
82
  - - "~>"
89
83
  - !ruby/object:Gem::Version
90
84
  version: '10'
85
+ - !ruby/object:Gem::Dependency
86
+ name: rack-test
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '0.6'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '0.6'
91
99
  description: Rack compatible HTTP router for Ruby
92
100
  email:
93
101
  - me@lucaguidi.com
@@ -105,12 +113,14 @@ files:
105
113
  - lib/lotus/router/version.rb
106
114
  - lib/lotus/routing/endpoint.rb
107
115
  - lib/lotus/routing/endpoint_resolver.rb
116
+ - lib/lotus/routing/error.rb
108
117
  - lib/lotus/routing/force_ssl.rb
109
118
  - lib/lotus/routing/http_router.rb
110
119
  - lib/lotus/routing/namespace.rb
111
120
  - lib/lotus/routing/parsers.rb
112
121
  - lib/lotus/routing/parsing/json_parser.rb
113
122
  - lib/lotus/routing/parsing/parser.rb
123
+ - lib/lotus/routing/recognized_route.rb
114
124
  - lib/lotus/routing/resource.rb
115
125
  - lib/lotus/routing/resource/action.rb
116
126
  - lib/lotus/routing/resource/nested.rb
@@ -140,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
150
  version: '0'
141
151
  requirements: []
142
152
  rubyforge_project:
143
- rubygems_version: 2.4.5.1
153
+ rubygems_version: 2.5.1
144
154
  signing_key:
145
155
  specification_version: 4
146
156
  summary: Rack compatible HTTP router for Ruby and Lotus