grape-security 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|