grape-security 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +45 -0
- data/.rspec +2 -0
- data/.rubocop.yml +70 -0
- data/.travis.yml +18 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +314 -0
- data/CONTRIBUTING.md +118 -0
- data/Gemfile +21 -0
- data/Guardfile +14 -0
- data/LICENSE +20 -0
- data/README.md +1777 -0
- data/RELEASING.md +105 -0
- data/Rakefile +69 -0
- data/UPGRADING.md +124 -0
- data/grape-security.gemspec +39 -0
- data/grape.png +0 -0
- data/lib/grape.rb +99 -0
- data/lib/grape/api.rb +646 -0
- data/lib/grape/cookies.rb +39 -0
- data/lib/grape/endpoint.rb +533 -0
- data/lib/grape/error_formatter/base.rb +31 -0
- data/lib/grape/error_formatter/json.rb +15 -0
- data/lib/grape/error_formatter/txt.rb +16 -0
- data/lib/grape/error_formatter/xml.rb +15 -0
- data/lib/grape/exceptions/base.rb +66 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +10 -0
- data/lib/grape/exceptions/invalid_formatter.rb +10 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +10 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +10 -0
- data/lib/grape/exceptions/missing_mime_type.rb +10 -0
- data/lib/grape/exceptions/missing_option.rb +10 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +10 -0
- data/lib/grape/exceptions/unknown_options.rb +10 -0
- data/lib/grape/exceptions/unknown_validator.rb +10 -0
- data/lib/grape/exceptions/validation.rb +26 -0
- data/lib/grape/exceptions/validation_errors.rb +43 -0
- data/lib/grape/formatter/base.rb +31 -0
- data/lib/grape/formatter/json.rb +12 -0
- data/lib/grape/formatter/serializable_hash.rb +35 -0
- data/lib/grape/formatter/txt.rb +11 -0
- data/lib/grape/formatter/xml.rb +12 -0
- data/lib/grape/http/request.rb +26 -0
- data/lib/grape/locale/en.yml +32 -0
- data/lib/grape/middleware/auth/base.rb +30 -0
- data/lib/grape/middleware/auth/basic.rb +13 -0
- data/lib/grape/middleware/auth/digest.rb +13 -0
- data/lib/grape/middleware/auth/oauth2.rb +83 -0
- data/lib/grape/middleware/base.rb +62 -0
- data/lib/grape/middleware/error.rb +89 -0
- data/lib/grape/middleware/filter.rb +17 -0
- data/lib/grape/middleware/formatter.rb +150 -0
- data/lib/grape/middleware/globals.rb +13 -0
- data/lib/grape/middleware/versioner.rb +32 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
- data/lib/grape/middleware/versioner/header.rb +132 -0
- data/lib/grape/middleware/versioner/param.rb +42 -0
- data/lib/grape/middleware/versioner/path.rb +52 -0
- data/lib/grape/namespace.rb +23 -0
- data/lib/grape/parser/base.rb +29 -0
- data/lib/grape/parser/json.rb +11 -0
- data/lib/grape/parser/xml.rb +11 -0
- data/lib/grape/path.rb +70 -0
- data/lib/grape/route.rb +27 -0
- data/lib/grape/util/content_types.rb +18 -0
- data/lib/grape/util/deep_merge.rb +23 -0
- data/lib/grape/util/hash_stack.rb +120 -0
- data/lib/grape/validations.rb +322 -0
- data/lib/grape/validations/coerce.rb +63 -0
- data/lib/grape/validations/default.rb +25 -0
- data/lib/grape/validations/exactly_one_of.rb +26 -0
- data/lib/grape/validations/mutual_exclusion.rb +25 -0
- data/lib/grape/validations/presence.rb +16 -0
- data/lib/grape/validations/regexp.rb +12 -0
- data/lib/grape/validations/values.rb +23 -0
- data/lib/grape/version.rb +3 -0
- data/spec/grape/api_spec.rb +2571 -0
- data/spec/grape/endpoint_spec.rb +784 -0
- data/spec/grape/entity_spec.rb +324 -0
- data/spec/grape/exceptions/invalid_formatter_spec.rb +18 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +18 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +18 -0
- data/spec/grape/exceptions/missing_option_spec.rb +18 -0
- data/spec/grape/exceptions/unknown_options_spec.rb +18 -0
- data/spec/grape/exceptions/unknown_validator_spec.rb +18 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
- data/spec/grape/middleware/auth/basic_spec.rb +31 -0
- data/spec/grape/middleware/auth/digest_spec.rb +47 -0
- data/spec/grape/middleware/auth/oauth2_spec.rb +135 -0
- data/spec/grape/middleware/base_spec.rb +58 -0
- data/spec/grape/middleware/error_spec.rb +45 -0
- data/spec/grape/middleware/exception_spec.rb +184 -0
- data/spec/grape/middleware/formatter_spec.rb +258 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
- data/spec/grape/middleware/versioner/header_spec.rb +302 -0
- data/spec/grape/middleware/versioner/param_spec.rb +58 -0
- data/spec/grape/middleware/versioner/path_spec.rb +44 -0
- data/spec/grape/middleware/versioner_spec.rb +22 -0
- data/spec/grape/path_spec.rb +229 -0
- data/spec/grape/util/hash_stack_spec.rb +132 -0
- data/spec/grape/validations/coerce_spec.rb +208 -0
- data/spec/grape/validations/default_spec.rb +123 -0
- data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
- data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
- data/spec/grape/validations/presence_spec.rb +142 -0
- data/spec/grape/validations/regexp_spec.rb +40 -0
- data/spec/grape/validations/values_spec.rb +152 -0
- data/spec/grape/validations/zh-CN.yml +10 -0
- data/spec/grape/validations_spec.rb +994 -0
- data/spec/shared/versioning_examples.rb +121 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/basic_auth_encode_helpers.rb +3 -0
- data/spec/support/content_type_helpers.rb +11 -0
- data/spec/support/versioned_helpers.rb +50 -0
- metadata +421 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'grape/middleware/base'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
class Globals < Base
|
6
|
+
def before
|
7
|
+
@env['grape.request'] = Grape::Request.new(@env)
|
8
|
+
@env['grape.request.headers'] = request.headers
|
9
|
+
@env['grape.request.params'] = request.params if @env['rack.input']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Versioners set env['api.version'] when a version is defined on an API and
|
2
|
+
# on the requests. The current methods for determining version are:
|
3
|
+
#
|
4
|
+
# :header - version from HTTP Accept header.
|
5
|
+
# :path - version from uri. e.g. /v1/resource
|
6
|
+
# :param - version from uri query string, e.g. /v1/resource?apiver=v1
|
7
|
+
#
|
8
|
+
# See individual classes for details.
|
9
|
+
module Grape
|
10
|
+
module Middleware
|
11
|
+
module Versioner
|
12
|
+
module_function
|
13
|
+
|
14
|
+
# @param strategy [Symbol] :path, :header or :param
|
15
|
+
# @return a middleware class based on strategy
|
16
|
+
def using(strategy)
|
17
|
+
case strategy
|
18
|
+
when :path
|
19
|
+
Path
|
20
|
+
when :header
|
21
|
+
Header
|
22
|
+
when :param
|
23
|
+
Param
|
24
|
+
when :accept_version_header
|
25
|
+
AcceptVersionHeader
|
26
|
+
else
|
27
|
+
raise Grape::Exceptions::InvalidVersionerOption.new(strategy)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'grape/middleware/base'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
module Versioner
|
6
|
+
# This middleware sets various version related rack environment variables
|
7
|
+
# based on the HTTP Accept-Version header
|
8
|
+
#
|
9
|
+
# Example: For request header
|
10
|
+
# Accept-Version: v1
|
11
|
+
#
|
12
|
+
# The following rack env variables are set:
|
13
|
+
#
|
14
|
+
# env['api.version'] => 'v1'
|
15
|
+
#
|
16
|
+
# If version does not match this route, then a 406 is raised with
|
17
|
+
# X-Cascade header to alert Rack::Mount to attempt the next matched
|
18
|
+
# route.
|
19
|
+
class AcceptVersionHeader < Base
|
20
|
+
def before
|
21
|
+
potential_version = (env['HTTP_ACCEPT_VERSION'] || '').strip
|
22
|
+
|
23
|
+
if strict?
|
24
|
+
# If no Accept-Version header:
|
25
|
+
if potential_version.empty?
|
26
|
+
throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
unless potential_version.empty?
|
31
|
+
# If the requested version is not supported:
|
32
|
+
unless versions.any? { |v| v.to_s == potential_version }
|
33
|
+
throw :error, status: 406, headers: error_headers, message: 'The requested version is not supported.'
|
34
|
+
end
|
35
|
+
|
36
|
+
env['api.version'] = potential_version
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def versions
|
43
|
+
options[:versions] || []
|
44
|
+
end
|
45
|
+
|
46
|
+
def strict?
|
47
|
+
options[:version_options] && options[:version_options][:strict]
|
48
|
+
end
|
49
|
+
|
50
|
+
# By default those errors contain an `X-Cascade` header set to `pass`, which allows nesting and stacking
|
51
|
+
# of routes (see [Rack::Mount](https://github.com/josh/rack-mount) for more information). To prevent
|
52
|
+
# this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
|
53
|
+
def cascade?
|
54
|
+
if options[:version_options] && options[:version_options].key?(:cascade)
|
55
|
+
!!options[:version_options][:cascade]
|
56
|
+
else
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def error_headers
|
62
|
+
cascade? ? { 'X-Cascade' => 'pass' } : {}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'grape/middleware/base'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
module Versioner
|
6
|
+
# This middleware sets various version related rack environment variables
|
7
|
+
# based on the HTTP Accept header with the pattern:
|
8
|
+
# application/vnd.:vendor-:version+:format
|
9
|
+
#
|
10
|
+
# Example: For request header
|
11
|
+
# Accept: application/vnd.mycompany-v1+json
|
12
|
+
#
|
13
|
+
# The following rack env variables are set:
|
14
|
+
#
|
15
|
+
# env['api.type'] => 'application'
|
16
|
+
# env['api.subtype'] => 'vnd.mycompany-v1+json'
|
17
|
+
# env['api.vendor] => 'mycompany'
|
18
|
+
# env['api.version] => 'v1'
|
19
|
+
# env['api.format] => 'format'
|
20
|
+
#
|
21
|
+
# If version does not match this route, then a 406 is raised with
|
22
|
+
# X-Cascade header to alert Rack::Mount to attempt the next matched
|
23
|
+
# route.
|
24
|
+
class Header < Base
|
25
|
+
def before
|
26
|
+
begin
|
27
|
+
header = Rack::Accept::MediaType.new env['HTTP_ACCEPT']
|
28
|
+
rescue RuntimeError => e
|
29
|
+
throw :error, status: 406, headers: error_headers, message: e.message
|
30
|
+
end
|
31
|
+
|
32
|
+
if strict?
|
33
|
+
# If no Accept header:
|
34
|
+
if header.qvalues.empty?
|
35
|
+
throw :error, status: 406, headers: error_headers, message: 'Accept header must be set.'
|
36
|
+
end
|
37
|
+
# Remove any acceptable content types with ranges.
|
38
|
+
header.qvalues.reject! do |media_type, _|
|
39
|
+
Rack::Accept::Header.parse_media_type(media_type).find { |s| s == '*' }
|
40
|
+
end
|
41
|
+
# If all Accept headers included a range:
|
42
|
+
if header.qvalues.empty?
|
43
|
+
throw :error, status: 406, headers: error_headers, message: 'Accept header must not contain ranges ("*").'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
media_type = header.best_of available_media_types
|
48
|
+
|
49
|
+
if media_type
|
50
|
+
type, subtype = Rack::Accept::Header.parse_media_type media_type
|
51
|
+
env['api.type'] = type
|
52
|
+
env['api.subtype'] = subtype
|
53
|
+
|
54
|
+
if /\Avnd\.([a-z0-9*.]+)(?:-([a-z0-9*\-.]+))?(?:\+([a-z0-9*\-.+]+))?\z/ =~ subtype
|
55
|
+
env['api.vendor'] = $1
|
56
|
+
env['api.version'] = $2
|
57
|
+
env['api.format'] = $3 # weird that Grape::Middleware::Formatter also does this
|
58
|
+
end
|
59
|
+
# If none of the available content types are acceptable:
|
60
|
+
elsif strict?
|
61
|
+
throw :error, status: 406, headers: error_headers, message: '406 Not Acceptable'
|
62
|
+
# If all acceptable content types specify a vendor or version that doesn't exist:
|
63
|
+
elsif header.values.all? { |header_value| has_vendor?(header_value) || version?(header_value) }
|
64
|
+
throw :error, status: 406, headers: error_headers, message: 'API vendor or version not found.'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def available_media_types
|
71
|
+
available_media_types = []
|
72
|
+
|
73
|
+
content_types.each do |extension, media_type|
|
74
|
+
versions.reverse.each do |version|
|
75
|
+
available_media_types += ["application/vnd.#{vendor}-#{version}+#{extension}", "application/vnd.#{vendor}-#{version}"]
|
76
|
+
end
|
77
|
+
available_media_types << "application/vnd.#{vendor}+#{extension}"
|
78
|
+
end
|
79
|
+
|
80
|
+
available_media_types << "application/vnd.#{vendor}"
|
81
|
+
|
82
|
+
content_types.each do |_, media_type|
|
83
|
+
available_media_types << media_type
|
84
|
+
end
|
85
|
+
|
86
|
+
available_media_types = available_media_types.flatten
|
87
|
+
end
|
88
|
+
|
89
|
+
def versions
|
90
|
+
options[:versions] || []
|
91
|
+
end
|
92
|
+
|
93
|
+
def vendor
|
94
|
+
options[:version_options] && options[:version_options][:vendor]
|
95
|
+
end
|
96
|
+
|
97
|
+
def strict?
|
98
|
+
options[:version_options] && options[:version_options][:strict]
|
99
|
+
end
|
100
|
+
|
101
|
+
# By default those errors contain an `X-Cascade` header set to `pass`, which allows nesting and stacking
|
102
|
+
# of routes (see [Rack::Mount](https://github.com/josh/rack-mount) for more information). To prevent
|
103
|
+
# this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
|
104
|
+
def cascade?
|
105
|
+
if options[:version_options] && options[:version_options].key?(:cascade)
|
106
|
+
!!options[:version_options][:cascade]
|
107
|
+
else
|
108
|
+
true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def error_headers
|
113
|
+
cascade? ? { 'X-Cascade' => 'pass' } : {}
|
114
|
+
end
|
115
|
+
|
116
|
+
# @param [String] media_type a content type
|
117
|
+
# @return [Boolean] whether the content type sets a vendor
|
118
|
+
def has_vendor?(media_type)
|
119
|
+
_, subtype = Rack::Accept::Header.parse_media_type media_type
|
120
|
+
subtype[/\Avnd\.[a-z0-9*.]+/]
|
121
|
+
end
|
122
|
+
|
123
|
+
# @param [String] media_type a content type
|
124
|
+
# @return [Boolean] whether the content type sets an API version
|
125
|
+
def version?(media_type)
|
126
|
+
_, subtype = Rack::Accept::Header.parse_media_type media_type
|
127
|
+
subtype[/\Avnd\.[a-z0-9*.]+-[a-z0-9*\-.]+/]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'grape/middleware/base'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
module Versioner
|
6
|
+
# This middleware sets various version related rack environment variables
|
7
|
+
# based on the request parameters and removes that parameter from the
|
8
|
+
# request parameters for subsequent middleware and API.
|
9
|
+
# If the version substring does not match any potential initialized
|
10
|
+
# versions, a 404 error is thrown.
|
11
|
+
# If the version substring is not passed the version (highest mounted)
|
12
|
+
# version will be used.
|
13
|
+
#
|
14
|
+
# Example: For a uri path
|
15
|
+
# /resource?apiver=v1
|
16
|
+
#
|
17
|
+
# The following rack env variables are set and path is rewritten to
|
18
|
+
# '/resource':
|
19
|
+
#
|
20
|
+
# env['api.version'] => 'v1'
|
21
|
+
class Param < Base
|
22
|
+
def default_options
|
23
|
+
{
|
24
|
+
parameter: "apiver"
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def before
|
29
|
+
paramkey = options[:parameter]
|
30
|
+
potential_version = Rack::Utils.parse_nested_query(env['QUERY_STRING'])[paramkey]
|
31
|
+
unless potential_version.nil?
|
32
|
+
if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
33
|
+
throw :error, status: 404, message: "404 API Version Not Found", headers: { 'X-Cascade' => 'pass' }
|
34
|
+
end
|
35
|
+
env['api.version'] = potential_version
|
36
|
+
env['rack.request.query_hash'].delete(paramkey) if env.key? 'rack.request.query_hash'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'grape/middleware/base'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
module Versioner
|
6
|
+
# This middleware sets various version related rack environment variables
|
7
|
+
# based on the uri path and removes the version substring from the uri
|
8
|
+
# path. If the version substring does not match any potential initialized
|
9
|
+
# versions, a 404 error is thrown.
|
10
|
+
#
|
11
|
+
# Example: For a uri path
|
12
|
+
# /v1/resource
|
13
|
+
#
|
14
|
+
# The following rack env variables are set and path is rewritten to
|
15
|
+
# '/resource':
|
16
|
+
#
|
17
|
+
# env['api.version'] => 'v1'
|
18
|
+
#
|
19
|
+
class Path < Base
|
20
|
+
def default_options
|
21
|
+
{
|
22
|
+
pattern: /.*/i
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def before
|
27
|
+
path = env['PATH_INFO'].dup
|
28
|
+
|
29
|
+
if prefix && path.index(prefix) == 0
|
30
|
+
path.sub!(prefix, '')
|
31
|
+
path = Rack::Mount::Utils.normalize_path(path)
|
32
|
+
end
|
33
|
+
|
34
|
+
pieces = path.split('/')
|
35
|
+
potential_version = pieces[1]
|
36
|
+
if potential_version =~ options[:pattern]
|
37
|
+
if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
38
|
+
throw :error, status: 404, message: "404 API Version Not Found"
|
39
|
+
end
|
40
|
+
env['api.version'] = potential_version
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def prefix
|
47
|
+
Rack::Mount::Utils.normalize_path(options[:prefix].to_s) if options[:prefix]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Grape
|
2
|
+
class Namespace
|
3
|
+
attr_reader :space, :options
|
4
|
+
|
5
|
+
# options:
|
6
|
+
# requirements: a hash
|
7
|
+
def initialize(space, options = {})
|
8
|
+
@space, @options = space.to_s, options
|
9
|
+
end
|
10
|
+
|
11
|
+
def requirements
|
12
|
+
options[:requirements] || {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.joined_space(settings)
|
16
|
+
settings.gather(:namespace).map(&:space).join("/")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.joined_space_path(settings)
|
20
|
+
Rack::Mount::Utils.normalize_path(joined_space(settings))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Grape
|
2
|
+
module Parser
|
3
|
+
module Base
|
4
|
+
class << self
|
5
|
+
PARSERS = {
|
6
|
+
json: Grape::Parser::Json,
|
7
|
+
jsonapi: Grape::Parser::Json,
|
8
|
+
xml: Grape::Parser::Xml
|
9
|
+
}
|
10
|
+
|
11
|
+
def parsers(options)
|
12
|
+
PARSERS.merge(options[:parsers] || {})
|
13
|
+
end
|
14
|
+
|
15
|
+
def parser_for(api_format, options = {})
|
16
|
+
spec = parsers(options)[api_format]
|
17
|
+
case spec
|
18
|
+
when nil
|
19
|
+
nil
|
20
|
+
when Symbol
|
21
|
+
method(spec)
|
22
|
+
else
|
23
|
+
spec
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/grape/path.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module Grape
|
2
|
+
class Path
|
3
|
+
def self.prepare(raw_path, namespace, settings)
|
4
|
+
Path.new(raw_path, namespace, settings).path_with_suffix
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :raw_path, :namespace, :settings
|
8
|
+
|
9
|
+
def initialize(raw_path, namespace, settings)
|
10
|
+
@raw_path = raw_path
|
11
|
+
@namespace = namespace
|
12
|
+
@settings = settings
|
13
|
+
end
|
14
|
+
|
15
|
+
def mount_path
|
16
|
+
split_setting(:mount_path, '/')
|
17
|
+
end
|
18
|
+
|
19
|
+
def root_prefix
|
20
|
+
split_setting(:root_prefix, '/')
|
21
|
+
end
|
22
|
+
|
23
|
+
def uses_path_versioning?
|
24
|
+
!!(settings[:version] && settings[:version_options][:using] == :path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def has_namespace?
|
28
|
+
namespace && namespace.to_s =~ /^\S/ && namespace != '/'
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_path?
|
32
|
+
raw_path && raw_path.to_s =~ /^\S/ && raw_path != '/'
|
33
|
+
end
|
34
|
+
|
35
|
+
def suffix
|
36
|
+
if !uses_path_versioning? || (has_namespace? || has_path?)
|
37
|
+
'(.:format)'
|
38
|
+
else
|
39
|
+
'(/.:format)'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def path
|
44
|
+
Rack::Mount::Utils.normalize_path(parts.join('/'))
|
45
|
+
end
|
46
|
+
|
47
|
+
def path_with_suffix
|
48
|
+
"#{path}#{suffix}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
path_with_suffix
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def parts
|
58
|
+
parts = [mount_path, root_prefix].compact
|
59
|
+
parts << ':version' if uses_path_versioning?
|
60
|
+
parts << namespace.to_s
|
61
|
+
parts << raw_path.to_s
|
62
|
+
parts.flatten.reject { |part| part == '/' }
|
63
|
+
end
|
64
|
+
|
65
|
+
def split_setting(key, delimiter)
|
66
|
+
return if settings[key].nil?
|
67
|
+
settings[key].to_s.split("/")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|