grape-swagger 0.11.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -1
  3. data/.rubocop.yml +3 -0
  4. data/.rubocop_todo.yml +14 -22
  5. data/.travis.yml +7 -4
  6. data/CHANGELOG.md +53 -26
  7. data/Gemfile +1 -1
  8. data/README.md +414 -327
  9. data/RELEASING.md +3 -4
  10. data/example/api/endpoints.rb +132 -0
  11. data/example/api/entities.rb +18 -0
  12. data/example/config.ru +36 -2
  13. data/example/example_requests.postman_collection +146 -0
  14. data/example/swagger-example.png +0 -0
  15. data/grape-swagger.gemspec +9 -6
  16. data/lib/grape-swagger.rb +69 -99
  17. data/lib/grape-swagger/doc_methods.rb +69 -544
  18. data/lib/grape-swagger/doc_methods/data_type.rb +77 -0
  19. data/lib/grape-swagger/doc_methods/extensions.rb +75 -0
  20. data/lib/grape-swagger/doc_methods/move_params.rb +153 -0
  21. data/lib/grape-swagger/doc_methods/operation_id.rb +27 -0
  22. data/lib/grape-swagger/doc_methods/optional_object.rb +15 -0
  23. data/lib/grape-swagger/doc_methods/parse_params.rb +113 -0
  24. data/lib/grape-swagger/doc_methods/path_string.rb +29 -0
  25. data/lib/grape-swagger/doc_methods/produces_consumes.rb +12 -0
  26. data/lib/grape-swagger/doc_methods/status_codes.rb +17 -0
  27. data/lib/grape-swagger/doc_methods/tag_name_description.rb +26 -0
  28. data/lib/grape-swagger/endpoint.rb +317 -0
  29. data/lib/grape-swagger/version.rb +1 -1
  30. data/spec/lib/data_type_spec.rb +57 -0
  31. data/spec/lib/endpoint_spec.rb +6 -0
  32. data/spec/lib/extensions_spec.rb +127 -0
  33. data/spec/lib/move_params_spec.rb +298 -0
  34. data/spec/lib/operation_id_spec.rb +24 -0
  35. data/spec/lib/optional_object_spec.rb +40 -0
  36. data/spec/lib/path_string_spec.rb +38 -0
  37. data/spec/lib/produces_consumes_spec.rb +98 -0
  38. data/spec/markdown/kramdown_adapter_spec.rb +2 -9
  39. data/spec/markdown/redcarpet_adapter_spec.rb +2 -16
  40. data/spec/spec_helper.rb +7 -13
  41. data/spec/support/api_swagger_v2_result.rb +204 -0
  42. data/spec/support/namespace_tags.rb +73 -0
  43. data/spec/support/the_api_entities.rb +52 -0
  44. data/spec/support/the_paths_definitions.rb +94 -0
  45. data/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb +32 -0
  46. data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +151 -0
  47. data/spec/swagger_v2/api_swagger_v2_extensions_spec.rb +109 -0
  48. data/spec/swagger_v2/api_swagger_v2_format-content_type_spec.rb +124 -0
  49. data/spec/swagger_v2/api_swagger_v2_global_configuration_spec.rb +51 -0
  50. data/spec/swagger_v2/api_swagger_v2_headers_spec.rb +44 -0
  51. data/spec/swagger_v2/api_swagger_v2_hide_documentation_path_spec.rb +56 -0
  52. data/spec/swagger_v2/api_swagger_v2_mounted_spec.rb +146 -0
  53. data/spec/swagger_v2/api_swagger_v2_param_type_body_nested_spec.rb +197 -0
  54. data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +151 -0
  55. data/spec/swagger_v2/api_swagger_v2_param_type_spec.rb +217 -0
  56. data/spec/swagger_v2/api_swagger_v2_request_params_fix_spec.rb +64 -0
  57. data/spec/swagger_v2/api_swagger_v2_response_spec.rb +184 -0
  58. data/spec/swagger_v2/api_swagger_v2_spec.rb +207 -0
  59. data/spec/swagger_v2/api_swagger_v2_type-format_spec.rb +121 -0
  60. data/spec/{boolean_params_spec.rb → swagger_v2/boolean_params_spec.rb} +2 -2
  61. data/spec/{default_api_spec.rb → swagger_v2/default_api_spec.rb} +40 -36
  62. data/spec/swagger_v2/description_not_initialized.rb +39 -0
  63. data/spec/{float_api_spec.rb → swagger_v2/float_api_spec.rb} +2 -2
  64. data/spec/{form_params_spec.rb → swagger_v2/form_params_spec.rb} +9 -9
  65. data/spec/{grape-swagger_spec.rb → swagger_v2/grape-swagger_spec.rb} +0 -0
  66. data/spec/swagger_v2/hide_api_spec.rb +131 -0
  67. data/spec/swagger_v2/mounted_target_class_spec.rb +76 -0
  68. data/spec/swagger_v2/namespace_tags_prefix_spec.rb +84 -0
  69. data/spec/swagger_v2/namespace_tags_spec.rb +76 -0
  70. data/spec/{namespaced_api_spec.rb → swagger_v2/namespaced_api_spec.rb} +6 -26
  71. data/spec/{param_type_spec.rb → swagger_v2/param_type_spec.rb} +10 -8
  72. data/spec/{param_values_spec.rb → swagger_v2/param_values_spec.rb} +52 -22
  73. data/spec/swagger_v2/params_array_spec.rb +63 -0
  74. data/spec/swagger_v2/params_hash_spec.rb +65 -0
  75. data/spec/swagger_v2/params_nested_spec.rb +63 -0
  76. data/spec/{reference_entity.rb → swagger_v2/reference_entity.rb} +18 -23
  77. data/spec/swagger_v2/response_model_spec.rb +212 -0
  78. data/spec/swagger_v2/simple_mounted_api_spec.rb +264 -0
  79. metadata +175 -90
  80. data/example/api.rb +0 -66
  81. data/lib/grape-swagger/markdown.rb +0 -23
  82. data/spec/api_description_spec.rb +0 -43
  83. data/spec/api_global_models_spec.rb +0 -77
  84. data/spec/api_models_spec.rb +0 -364
  85. data/spec/api_paths_spec.rb +0 -128
  86. data/spec/api_root_spec.rb +0 -30
  87. data/spec/api_with_nil_types.rb +0 -50
  88. data/spec/api_with_path_versioning_spec.rb +0 -33
  89. data/spec/api_with_prefix_and_namespace_spec.rb +0 -32
  90. data/spec/api_with_standalone_namespace_spec.rb +0 -215
  91. data/spec/array_entity_spec.rb +0 -34
  92. data/spec/array_params_spec.rb +0 -85
  93. data/spec/grape-swagger_helper_spec.rb +0 -152
  94. data/spec/group_params_spec.rb +0 -31
  95. data/spec/hash_params_spec.rb +0 -30
  96. data/spec/hide_api_spec.rb +0 -124
  97. data/spec/i18n_spec.rb +0 -364
  98. data/spec/markdown/markdown_spec.rb +0 -27
  99. data/spec/mounted_target_class_spec.rb +0 -63
  100. data/spec/mutually_exclusive_spec.rb +0 -36
  101. data/spec/non_default_api_spec.rb +0 -733
  102. data/spec/response_model_spec.rb +0 -121
  103. data/spec/simple_mounted_api_spec.rb +0 -213
  104. data/spec/support/i18n_helper.rb +0 -8
data/RELEASING.md CHANGED
@@ -1,17 +1,16 @@
1
1
  # Releasing Grape-Swagger
2
2
 
3
- There're no particular rules about when to release grape-swagger. Release bug fixes frequently, features not so frequently and breaking API changes rarely.
3
+ There're no particular rules about when to release grape-swagger. Release bug fixes frequenty, features not so frequently and breaking API changes rarely.
4
4
 
5
5
  ### Release
6
6
 
7
- Run tests and rubocop, check that all tests succeed locally.
7
+ Run tests, check that all tests succeed locally.
8
8
 
9
9
  ```
10
10
  bundle install
11
- bundle exec rake
11
+ rake
12
12
  ```
13
13
 
14
-
15
14
  Check that the last build succeeded in [Travis CI](https://travis-ci.org/ruby-grape/grape-swagger) for all supported platforms.
16
15
 
17
16
  Increment the version, modify [lib/grape-swagger/version.rb](lib/grape-swagger/version.rb).
@@ -0,0 +1,132 @@
1
+ require 'grape-entity'
2
+ require './api/entities'
3
+
4
+ module Api
5
+ class Spline
6
+ attr_accessor :id, :x, :y, :reticulated
7
+ end
8
+
9
+ module Endpoints
10
+ class Root < Grape::API
11
+ desc 'API Root'
12
+ get do
13
+ {
14
+ splines_url: '/splines',
15
+ file_url: '/file'
16
+ }
17
+ end
18
+ end
19
+
20
+ class Splines < Grape::API
21
+
22
+ @@splines = []
23
+
24
+ namespace :splines do
25
+ #
26
+ desc 'Get all splines',
27
+ is_array: true,
28
+ http_codes: [
29
+ { code: 200, message: 'get Splines', model: Api::Entities::Splines },
30
+ { code: 422, message: 'SplinesOutError' }
31
+ ]
32
+ get do
33
+ present :items, @@splines, with: Entities::Splines
34
+ end
35
+
36
+ #
37
+ desc 'Return a spline.',
38
+ http_codes: [
39
+ { code: 200, message: 'get Splines' },
40
+ { code: 422, message: 'SplinesOutError' }
41
+ ]
42
+ params do
43
+ requires :id, type: Integer, desc: 'Spline id.'
44
+ end
45
+ get ':id' do
46
+ error!({ code: 422, message: 'SplinesOutError' }) unless @@splines[params[:id] - 1]
47
+
48
+ present @@splines[params[:id] - 1], with: Entities::Splines
49
+ end
50
+
51
+ #
52
+ desc 'Create a spline.',
53
+ http_codes: [
54
+ { code: 201, message: 'Spline created', model: Api::Entities::Splines }
55
+ ]
56
+ params do
57
+ requires :spline, type: Hash do
58
+ requires :x, type: Numeric
59
+ requires :y, type: Numeric
60
+ end
61
+ optional :reticulated, type: Boolean, default: true, desc: 'True if the spline is reticulated.'
62
+ end
63
+ post do
64
+ spline = Spline.new
65
+ spline.id = @@splines.size + 1
66
+ spline.x = (params[:spline][:x]/params[:spline][:y] || 0.0)
67
+ spline.y = (params[:spline][:y]/params[:spline][:x] || 0.0)
68
+ spline.reticulated = params[:reticulated]
69
+
70
+ @@splines << spline
71
+
72
+ present spline, with: Entities::Splines
73
+ end
74
+
75
+ #
76
+ desc 'Update a spline.',
77
+ http_codes: [
78
+ { code: 200, message: 'update Splines', model: Api::Entities::Splines },
79
+ { code: 422, message: 'SplinesOutError' }
80
+ ]
81
+ params do
82
+ requires :id, type: Integer, desc: 'Spline id.'
83
+ optional :spline, type: Hash do
84
+ optional :x, type: Numeric
85
+ optional :y, type: Numeric
86
+ end
87
+ optional :reticulated, type: Boolean, default: true, desc: 'True if the spline is reticulated.'
88
+ end
89
+ put ':id' do
90
+ error!({ code: 422, message: 'SplinesOutError' }) unless @@splines[params[:id] - 1]
91
+
92
+ update_data = params[:spline]
93
+ spline = @@splines[params[:id] - 1]
94
+
95
+ spline.reticulated = !!update_data[:reticulated]
96
+ spline.x = update_data[:x]/update_data[:y] || 0.0
97
+ spline.y = update_data[:y]/update_data[:x] || 0.0
98
+
99
+ present spline, with: Entities::Splines
100
+ end
101
+
102
+ #
103
+ desc 'Delete a spline.'
104
+ params do
105
+ requires :id, type: Integer, desc: 'Spline id.'
106
+ end
107
+ delete ':id' do
108
+ error!({ code: 422, message: 'SplinesOutError' }) unless @@splines[params[:id] - 1]
109
+
110
+ @@splines.delete_at(params[:id] - 1)
111
+ { "deleted": params[:id] }
112
+ end
113
+ end
114
+ end
115
+
116
+ class FileAccessor < Grape::API
117
+ namespace :file do
118
+ desc 'Update image',
119
+ details: "# TEST api for testing uploading\n
120
+ # curl --form file=@splines.png http://localhost:9292/file/upload",
121
+ content_type: 'application/octet-stream'
122
+ post 'upload' do
123
+ filename = params[:file][:filename]
124
+ content_type 'binary', 'application/octet-stream'
125
+ # env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
126
+ header 'Content-Disposition', "attachment; filename*=UTF-8''#{URI.escape(filename)}"
127
+ params[:file][:tempfile].read
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,18 @@
1
+ require 'grape-entity'
2
+
3
+ module Api
4
+ module Entities
5
+ class Splines < Grape::Entity
6
+ expose :id, documentation: { type: Integer, desc: 'identity of a resource' }
7
+ expose :x, documentation: { type: Float, desc: 'x-value' }
8
+ expose :y, documentation: { type: Float, desc: 'y-value' }
9
+ expose :path, documentation: { type: String, desc: 'the requested resource'}
10
+
11
+ private
12
+
13
+ def path
14
+ "/#{object.class.name.demodulize.to_s.underscore}/#{object.id}"
15
+ end
16
+ end
17
+ end
18
+ end
data/example/config.ru CHANGED
@@ -6,5 +6,39 @@ use Rack::Cors do
6
6
  end
7
7
  end
8
8
 
9
- require './api'
10
- run Api.new
9
+ require 'grape'
10
+
11
+ require './api/endpoints'
12
+ require './api/entities'
13
+
14
+ class Base < Grape::API
15
+ require 'grape-entity'
16
+ require '../lib/grape-swagger'
17
+ format :json
18
+
19
+ mount Api::Endpoints::Root
20
+ mount Api::Endpoints::Splines
21
+ mount Api::Endpoints::FileAccessor
22
+
23
+ before do
24
+ header['Access-Control-Allow-Origin'] = '*'
25
+ header['Access-Control-Request-Method'] = '*'
26
+ end
27
+
28
+ # global exception handler, used for error notifications
29
+ rescue_from :all do |e|
30
+ raise e
31
+ error_response(message: "Internal server error: #{e}", status: 500)
32
+ end
33
+
34
+ add_swagger_documentation :hide_documentation_path => true,
35
+ :api_version => 'v1',
36
+ :info => {
37
+ title: "Horses and Hussars",
38
+ description: "Demo app for dev of grape swagger 2.0"
39
+ }
40
+
41
+ end
42
+
43
+
44
+ run Base.new
@@ -0,0 +1,146 @@
1
+ {
2
+ "id": "1a2c4a65-9c99-3435-17db-307763b28fd1",
3
+ "name": "grape-rackup",
4
+ "description": "",
5
+ "order": [
6
+ "cf633582-c2ea-cb44-24d7-d64e73a65049",
7
+ "60bd637b-7b77-b280-d4f7-3d3d38cd86ef",
8
+ "4780e2ef-f05d-f464-9d18-538c5960be3c",
9
+ "f83c2241-c239-13ea-bffb-255436e95863",
10
+ "0648649b-9e54-1bc5-f835-2c690f67d47a"
11
+ ],
12
+ "folders": [],
13
+ "timestamp": 1453407636651,
14
+ "owner": "",
15
+ "remoteLink": "",
16
+ "public": false,
17
+ "requests": [
18
+ {
19
+ "id": "0648649b-9e54-1bc5-f835-2c690f67d47a",
20
+ "headers": "Content-Type: application/json\n",
21
+ "url": "http://localhost:9292/splines/1",
22
+ "preRequestScript": "",
23
+ "pathVariables": {},
24
+ "method": "DELETE",
25
+ "data": [],
26
+ "dataMode": "params",
27
+ "version": 2,
28
+ "tests": "",
29
+ "currentHelper": "normal",
30
+ "helperAttributes": {},
31
+ "time": 1453408596777,
32
+ "name": "http://localhost:9292/splines",
33
+ "description": "",
34
+ "collectionId": "1a2c4a65-9c99-3435-17db-307763b28fd1",
35
+ "responses": []
36
+ },
37
+ {
38
+ "id": "4780e2ef-f05d-f464-9d18-538c5960be3c",
39
+ "headers": "Content-Type: application/json\n",
40
+ "url": "http://localhost:9292/splines/1",
41
+ "pathVariables": {},
42
+ "preRequestScript": "",
43
+ "method": "GET",
44
+ "collectionId": "1a2c4a65-9c99-3435-17db-307763b28fd1",
45
+ "data": [
46
+ {
47
+ "key": "required_group[required_param_1]",
48
+ "value": "sadf",
49
+ "type": "text",
50
+ "enabled": true
51
+ },
52
+ {
53
+ "key": "required_group[required_param_2]",
54
+ "value": "fgsd",
55
+ "type": "text",
56
+ "enabled": true
57
+ }
58
+ ],
59
+ "dataMode": "params",
60
+ "name": "http://localhost:9292/splines",
61
+ "description": "",
62
+ "descriptionFormat": "html",
63
+ "time": 1453408415061,
64
+ "version": 2,
65
+ "responses": [],
66
+ "tests": "",
67
+ "currentHelper": "normal",
68
+ "helperAttributes": {}
69
+ },
70
+ {
71
+ "id": "60bd637b-7b77-b280-d4f7-3d3d38cd86ef",
72
+ "headers": "Content-Type: application/json\n",
73
+ "url": "http://localhost:9292/splines",
74
+ "pathVariables": {},
75
+ "preRequestScript": "",
76
+ "method": "GET",
77
+ "collectionId": "1a2c4a65-9c99-3435-17db-307763b28fd1",
78
+ "data": [
79
+ {
80
+ "key": "required_group[required_param_1]",
81
+ "value": "sadf",
82
+ "type": "text",
83
+ "enabled": true
84
+ },
85
+ {
86
+ "key": "required_group[required_param_2]",
87
+ "value": "fgsd",
88
+ "type": "text",
89
+ "enabled": true
90
+ }
91
+ ],
92
+ "dataMode": "params",
93
+ "name": "http://localhost:9292/splines",
94
+ "description": "",
95
+ "descriptionFormat": "html",
96
+ "time": 1453408387712,
97
+ "version": 2,
98
+ "responses": [],
99
+ "tests": "",
100
+ "currentHelper": "normal",
101
+ "helperAttributes": {}
102
+ },
103
+ {
104
+ "id": "cf633582-c2ea-cb44-24d7-d64e73a65049",
105
+ "headers": "Content-Type: application/json\n",
106
+ "url": "http://localhost:9292/splines",
107
+ "pathVariables": {},
108
+ "preRequestScript": "",
109
+ "method": "POST",
110
+ "collectionId": "1a2c4a65-9c99-3435-17db-307763b28fd1",
111
+ "data": [],
112
+ "dataMode": "raw",
113
+ "name": "http://localhost:9292/splines",
114
+ "description": "",
115
+ "descriptionFormat": "html",
116
+ "time": 1453407769825,
117
+ "version": 2,
118
+ "responses": [],
119
+ "tests": "",
120
+ "currentHelper": "normal",
121
+ "helperAttributes": {},
122
+ "rawModeData": "{\n \"spline\":{\n \"x\":1.23,\n \"y\":3.14\n }\n}"
123
+ },
124
+ {
125
+ "id": "f83c2241-c239-13ea-bffb-255436e95863",
126
+ "headers": "Content-Type: application/json\n",
127
+ "url": "http://localhost:9292/splines/1",
128
+ "pathVariables": {},
129
+ "preRequestScript": "",
130
+ "method": "PUT",
131
+ "collectionId": "1a2c4a65-9c99-3435-17db-307763b28fd1",
132
+ "data": [],
133
+ "dataMode": "raw",
134
+ "name": "http://localhost:9292/splines",
135
+ "description": "",
136
+ "descriptionFormat": "html",
137
+ "time": 1453408494448,
138
+ "version": 2,
139
+ "responses": [],
140
+ "tests": "",
141
+ "currentHelper": "normal",
142
+ "helperAttributes": {},
143
+ "rawModeData": "{\n \"spline\":{\n \"x\":131.23,\n \"y\":93.14\n }\n}"
144
+ }
145
+ ]
146
+ }
Binary file
@@ -11,8 +11,9 @@ Gem::Specification.new do |s|
11
11
  s.summary = 'A simple way to add auto generated documentation to your Grape API that can be displayed with Swagger.'
12
12
  s.license = 'MIT'
13
13
 
14
- s.add_runtime_dependency 'grape', '>= 0.16.2'
15
- s.add_runtime_dependency 'grape-entity', '< 0.5.0'
14
+ s.add_runtime_dependency 'grape'
15
+ s.add_runtime_dependency 'grape-entity'
16
+ s.add_runtime_dependency 'awesome_print'
16
17
 
17
18
  s.add_development_dependency 'rake'
18
19
  s.add_development_dependency 'shoulda'
@@ -21,10 +22,12 @@ Gem::Specification.new do |s|
21
22
  s.add_development_dependency 'bundler'
22
23
  s.add_development_dependency 'rack-test'
23
24
  s.add_development_dependency 'rack-cors'
24
- s.add_development_dependency 'rubocop', '0.33.0'
25
- s.add_development_dependency 'kramdown', '~> 1.4.1'
26
- s.add_development_dependency 'redcarpet', '~> 3.1.2' unless RUBY_PLATFORM.eql? 'java'
27
- s.add_development_dependency 'rouge', '~> 1.6.1'
25
+ s.add_development_dependency 'rubocop'
26
+ s.add_development_dependency 'kramdown'
27
+ s.add_development_dependency 'redcarpet' unless RUBY_PLATFORM.eql?('java') || RUBY_ENGINE.eql?('rbx')
28
+ s.add_development_dependency 'rouge' unless RUBY_PLATFORM.eql?('java') || RUBY_ENGINE.eql?('rbx')
29
+ s.add_development_dependency 'pry' unless RUBY_PLATFORM.eql?('java') || RUBY_ENGINE.eql?('rbx')
30
+ s.add_development_dependency 'pry-byebug' unless RUBY_PLATFORM.eql?('java') || RUBY_ENGINE.eql?('rbx')
28
31
 
29
32
  s.files = `git ls-files`.split("\n")
30
33
  s.test_files = `git ls-files -- {test,spec}/*`.split("\n")
data/lib/grape-swagger.rb CHANGED
@@ -1,20 +1,24 @@
1
1
  require 'grape'
2
2
  require 'grape-swagger/version'
3
+ require 'grape-swagger/endpoint'
3
4
  require 'grape-swagger/errors'
5
+
4
6
  require 'grape-swagger/doc_methods'
5
- require 'grape-swagger/markdown'
7
+
6
8
  require 'grape-swagger/markdown/kramdown_adapter'
7
9
  require 'grape-swagger/markdown/redcarpet_adapter'
8
10
 
11
+ require 'awesome_print'
12
+
9
13
  module Grape
10
14
  class API
11
15
  class << self
12
16
  attr_accessor :combined_routes, :combined_namespaces, :combined_namespace_routes, :combined_namespace_identifiers
13
- attr_accessor :endpoint_mapping
14
17
 
15
18
  def add_swagger_documentation(options = {})
16
19
  documentation_class = create_documentation_class
17
20
 
21
+ version_for(options)
18
22
  options = { target_class: self }.merge(options)
19
23
  @target_class = options[:target_class]
20
24
 
@@ -22,20 +26,8 @@ module Grape
22
26
  mount(documentation_class)
23
27
 
24
28
  @target_class.combined_routes = {}
25
- @target_class.routes.each do |route|
26
- route_path = route.path
27
- route_match = route_path.split(/^.*?#{route.prefix.to_s}/).last
28
- next unless route_match
29
- route_match = route_match.match('\/([\w|-]*?)[\.\/\(]') || route_match.match('\/([\w|-]*)$')
30
- next unless route_match
31
- resource = route_match.captures.first
32
- next if resource.empty?
33
- @target_class.combined_routes[resource] ||= []
34
- next if documentation_class.hide_documentation_path && route.path.match(/#{documentation_class.mount_path}($|\/|\(\.)/)
35
- @target_class.combined_routes[resource] << route
36
- end
29
+ combine_routes(@target_class, documentation_class)
37
30
 
38
- @target_class.endpoint_mapping = {}
39
31
  @target_class.combined_namespaces = {}
40
32
  combine_namespaces(@target_class)
41
33
 
@@ -50,6 +42,26 @@ module Grape
50
42
 
51
43
  private
52
44
 
45
+ def version_for(options)
46
+ options[:version] = version if version
47
+ end
48
+
49
+ def combine_routes(app, doc_klass)
50
+ app.routes.each do |route|
51
+ route_path = route.route_path
52
+ route_match = route_path.split(/^.*?#{route.route_prefix.to_s}/).last
53
+ next unless route_match
54
+ route_match = route_match.match('\/([\w|-]*?)[\.\/\(]') || route_match.match('\/([\w|-]*)$')
55
+ next unless route_match
56
+ resource = route_match.captures.first
57
+ next if resource.empty?
58
+ resource.downcase!
59
+ @target_class.combined_routes[resource] ||= []
60
+ next if doc_klass.hide_documentation_path && route.route_path.match(/#{doc_klass.mount_path}($|\/|\(\.)/)
61
+ @target_class.combined_routes[resource] << route
62
+ end
63
+ end
64
+
53
65
  def combine_namespaces(app)
54
66
  app.endpoints.each do |endpoint|
55
67
  ns = if endpoint.respond_to?(:namespace_stackable)
@@ -61,10 +73,6 @@ module Grape
61
73
  # and strip leading slash
62
74
  @target_class.combined_namespaces[endpoint.namespace.sub(/^\//, '')] = ns if ns
63
75
 
64
- endpoint.routes.each do |route|
65
- @target_class.endpoint_mapping[route.to_s.sub('(.:format)', '')] = endpoint
66
- end
67
-
68
76
  combine_namespaces(endpoint.options[:app]) if endpoint.options[:app]
69
77
  end
70
78
  end
@@ -76,35 +84,32 @@ module Grape
76
84
  parent_route_name = name.match(%r{^/?([^/]*).*$})[1]
77
85
  parent_route = @target_class.combined_routes[parent_route_name]
78
86
  # fetch all routes that are within the current namespace
79
- namespace_routes = parent_route.collect do |route|
80
- route if (route.path.start_with?(route.prefix ? "/#{route.prefix}/#{name}" : "/#{name}") || route.path.start_with?((route.prefix ? "/#{route.prefix}/:version/#{name}" : "/:version/#{name}"))) &&
81
- (route.instance_variable_get(:@options)[:namespace] == "/#{name}" || route.instance_variable_get(:@options)[:namespace] == "/:version/#{name}")
82
- end.compact
87
+ namespace_routes = parent_route.reject do |route|
88
+ !route_path_start_with?(route, name) || !route_instance_variable_equals?(route, name)
89
+ end
83
90
 
84
91
  if namespace.options.key?(:swagger) && namespace.options[:swagger][:nested] == false
85
92
  # Namespace shall appear as standalone resource, use specified name or use normalized path as name
86
- identifier = if namespace.options[:swagger].key?(:name)
87
- namespace.options[:swagger][:name].tr(' ', '-')
88
- else
89
- name.tr('_', '-').gsub(/\//, '_')
90
- end
93
+ identifier = if namespace.options[:swagger].key?(:name)
94
+ name.tr(' ', '-')
95
+ else
96
+ name.tr('_', '-').gsub(/\//, '_')
97
+ end
91
98
  @target_class.combined_namespace_identifiers[identifier] = name
92
99
  @target_class.combined_namespace_routes[identifier] = namespace_routes
93
100
 
94
- # get all nested namespaces below the current namespace
101
+ # # get all nested namespaces below the current namespace
95
102
  sub_namespaces = standalone_sub_namespaces(name, namespaces)
96
- # convert namespace to route names
97
- sub_ns_paths = sub_namespaces.collect { |ns_name, _| "/#{ns_name}" }
98
- sub_ns_paths_versioned = sub_namespaces.collect { |ns_name, _| "/:version/#{ns_name}" }
99
- # get the actual route definitions for the namespace path names
100
- sub_routes = parent_route.collect do |route|
101
- route if sub_ns_paths.include?(route.instance_variable_get(:@options)[:namespace]) || sub_ns_paths_versioned.include?(route.instance_variable_get(:@options)[:namespace])
102
- end.compact
103
- # add all determined routes of the sub namespaces to standalone resource
103
+ sub_routes = sub_routes_from(parent_route, sub_namespaces)
104
104
  @target_class.combined_namespace_routes[identifier].push(*sub_routes)
105
105
  else
106
106
  # default case when not explicitly specified or nested == true
107
- standalone_namespaces = namespaces.reject { |_, ns| !ns.options.key?(:swagger) || !ns.options[:swagger].key?(:nested) || ns.options[:swagger][:nested] != false }
107
+ standalone_namespaces = namespaces.reject do |_, ns|
108
+ !ns.options.key?(:swagger) ||
109
+ !ns.options[:swagger].key?(:nested) ||
110
+ ns.options[:swagger][:nested] != false
111
+ end
112
+
108
113
  parent_standalone_namespaces = standalone_namespaces.reject { |ns_name, _| !name.start_with?(ns_name) }
109
114
  # add only to the main route if the namespace is not within any other namespace appearing as standalone resource
110
115
  if parent_standalone_namespaces.empty?
@@ -116,6 +121,32 @@ module Grape
116
121
  end
117
122
  end
118
123
 
124
+ def sub_routes_from(parent_route, sub_namespaces)
125
+ sub_ns_paths = sub_namespaces.collect { |ns_name, _| ["/#{ns_name}", "/:version/#{ns_name}"] }
126
+ sub_routes = parent_route.reject do |route|
127
+ parent_namespace = route_instance_variable(route)
128
+ !sub_ns_paths.assoc(parent_namespace) && !sub_ns_paths.rassoc(parent_namespace)
129
+ end
130
+
131
+ sub_routes
132
+ end
133
+
134
+ def route_instance_variable(route)
135
+ route.instance_variable_get(:@options)[:namespace]
136
+ end
137
+
138
+ def route_instance_variable_equals?(route, name)
139
+ route_instance_variable(route) == "/#{name}" ||
140
+ route_instance_variable(route) == "/:version/#{name}"
141
+ end
142
+
143
+ def route_path_start_with?(route, name)
144
+ route_prefix = route.route_prefix ? "/#{route.route_prefix}/#{name}" : "/#{name}"
145
+ route_versioned_prefix = route.route_prefix ? "/#{route.route_prefix}/:version/#{name}" : "/:version/#{name}"
146
+
147
+ route.route_path.start_with?(route_prefix, route_versioned_prefix)
148
+ end
149
+
119
150
  def standalone_sub_namespaces(name, namespaces)
120
151
  # assign all nested namespace routes to this resource, too
121
152
  # (unless they are assigned to another standalone namespace themselves)
@@ -132,67 +163,6 @@ module Grape
132
163
  sub_namespaces
133
164
  end
134
165
 
135
- def get_non_nested_params(params)
136
- # Duplicate the params as we are going to modify them
137
- dup_params = params.each_with_object({}) do |(param, value), dparams|
138
- dparams[param] = value.dup
139
- end
140
-
141
- dup_params.reject do |param, value|
142
- is_nested_param = /^#{ Regexp.quote param }\[.+\]$/
143
- 0 < dup_params.count do |p, _|
144
- match = p.match(is_nested_param)
145
- dup_params[p][:required] = false if match && !value[:required]
146
- match
147
- end
148
- end
149
- end
150
-
151
- def parse_array_params(params)
152
- modified_params = {}
153
- array_keys = []
154
- params.each_key do |k|
155
- new_key, modified_params = parse_array_key(array_keys, k, modified_params)
156
- if params[k].is_a?(Hash) && params[k][:type] == 'Array'
157
- array_keys.push(new_key)
158
- end
159
- modified_params[new_key] = params[k]
160
- end
161
- modified_params
162
- end
163
-
164
- def parse_array_key(array_keys, key, modified_params)
165
- unless array_keys.blank?
166
- array_keys.each do |array_key|
167
- if key.to_s.start_with?(array_key.to_s + '[')
168
- key = array_key.to_s + '[]' + key.to_s.split(array_key)[1]
169
- modified_params.delete array_key
170
- end
171
- end
172
- end
173
- [key, modified_params]
174
- end
175
-
176
- def parse_enum_or_range_values(values)
177
- case values
178
- when Range
179
- parse_range_values(values) if values.first.is_a?(Integer)
180
- when Proc
181
- values_result = values.call
182
- if values_result.is_a?(Range) && values_result.first.is_a?(Integer)
183
- parse_range_values(values_result)
184
- else
185
- { enum: values_result }
186
- end
187
- else
188
- { enum: values } if values
189
- end
190
- end
191
-
192
- def parse_range_values(values)
193
- { minimum: values.first, maximum: values.last }
194
- end
195
-
196
166
  def create_documentation_class
197
167
  Class.new(Grape::API) do
198
168
  extend GrapeSwagger::DocMethods