grape-swagger 2.1.2 → 2.1.4

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.
data/CLAUDE.md ADDED
@@ -0,0 +1,88 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ grape-swagger is a Ruby gem that auto-generates Swagger 2.0 documentation for Grape APIs. It extends Grape APIs with `add_swagger_documentation` to register documentation endpoints that output OpenAPI/Swagger-compliant JSON.
8
+
9
+ **Key dependencies:** Grape >= 1.7 (supports up to 3.x), Ruby >= 3.1
10
+
11
+ ## Common Commands
12
+
13
+ ```bash
14
+ # Install dependencies
15
+ bundle install
16
+
17
+ # Run all tests + RuboCop
18
+ bundle exec rake
19
+
20
+ # Run tests only
21
+ bundle exec rspec
22
+
23
+ # Run a single test file
24
+ bundle exec rspec spec/swagger_v2/api_swagger_v2_spec.rb
25
+
26
+ # Run a specific test by line number
27
+ bundle exec rspec spec/swagger_v2/api_swagger_v2_spec.rb:42
28
+
29
+ # Run RuboCop linting
30
+ bundle exec rubocop
31
+
32
+ # Test with different model parsers
33
+ MODEL_PARSER=grape-swagger-entity bundle exec rspec
34
+ MODEL_PARSER=grape-swagger-representable bundle exec rspec
35
+
36
+ # Test with a specific Grape version
37
+ GRAPE_VERSION=2.2.0 bundle update && bundle exec rspec
38
+ ```
39
+
40
+ ## Architecture
41
+
42
+ ### Core Extension Flow
43
+
44
+ 1. **Entry point:** `lib/grape-swagger.rb` - Extends `GrapeInstance` with `SwaggerDocumentationAdder` mixin
45
+ 2. **Endpoint generation:** `lib/grape-swagger/endpoint.rb` - Extends `Grape::Endpoint` to build Swagger objects from route definitions
46
+ 3. **Documentation helpers:** `lib/grape-swagger/doc_methods.rb` - Central module for generating paths, definitions, tags
47
+
48
+ ### Pluggable Systems
49
+
50
+ **Model Parsers** (`lib/grape-swagger/model_parsers.rb`):
51
+ - Registry for handling different entity types (Grape::Entity, representable, custom)
52
+ - Access via `GrapeSwagger.model_parsers`
53
+ - Supports `insert_before` and `insert_after` for ordering
54
+
55
+ **Request Param Parsers** (`lib/grape-swagger/request_param_parser_registry.rb`):
56
+ - Three default parsers: Headers, Route, Body (in `request_param_parsers/`)
57
+ - Access via `GrapeSwagger.request_param_parsers`
58
+
59
+ ### Key Modules
60
+
61
+ - `SwaggerDocumentationAdder` - Adds `add_swagger_documentation` method to Grape APIs
62
+ - `SwaggerRouting` - Combines routes by resource path, handles namespace routing
63
+ - `GrapeSwagger::DocMethods` - Helpers in `doc_methods/` subdirectory for specific documentation tasks
64
+
65
+ ## Testing Patterns
66
+
67
+ - Tests use `Rack::Test::Methods` for HTTP testing
68
+ - Define an `app` method returning a `Grape::API` subclass in specs
69
+ - Use shared contexts like `include_context "#{MODEL_PARSER} swagger example"` for model parser testing
70
+ - Tests run with random order (seed: 40834)
71
+ - `MODEL_PARSER` env var controls which parser to test (mock, entity, representable)
72
+
73
+ ## Code Style
74
+
75
+ - Always include `# frozen_string_literal: true` at file start
76
+ - Max line length: 120 characters
77
+ - RuboCop enforced with some rules relaxed in `.rubocop_todo.yml`
78
+ - Naming cops disabled; Style cops mostly disabled
79
+ - Spec files excluded from most length/complexity checks
80
+
81
+ ## Contributing Workflow
82
+
83
+ 1. Create feature branch from master
84
+ 2. Write tests first (add to `spec/`)
85
+ 3. Implement feature
86
+ 4. Run `bundle exec rake` (must pass)
87
+ 5. Add entry to CHANGELOG.md under *Next Release*
88
+ 6. Submit PR
data/README.md CHANGED
@@ -3,22 +3,92 @@
3
3
  [![Coverage Status](https://coveralls.io/repos/github/ruby-grape/grape-swagger/badge.svg?branch=master)](https://coveralls.io/github/ruby-grape/grape-swagger?branch=master)
4
4
  [![Code Climate](https://codeclimate.com/github/ruby-grape/grape-swagger.svg)](https://codeclimate.com/github/ruby-grape/grape-swagger)
5
5
 
6
- ##### Table of Contents
7
-
8
- * [What is grape-swagger?](#what)
9
- * [Related Projects](#related)
10
- * [Compatibility](#version)
11
- * [Swagger-Spec](#swagger-spec)
12
- * [Installation](#install)
13
- * [Usage](#usage)
14
- * [Model Parsers](#model_parsers)
15
- * [Configure](#configure)
16
- * [Routes Configuration](#routes)
17
- * [Using Grape Entities](#grape-entity)
18
- * [Securing the Swagger UI](#oauth)
19
- * [Example](#example)
20
- * [Rake Tasks](#rake)
21
-
6
+ # Table of Contents
7
+
8
+ - [What is grape-swagger? ](#what-is-grape-swagger-)
9
+ - [Related Projects ](#related-projects-)
10
+ - [Compatibility ](#compatibility-)
11
+ - [Swagger-Spec ](#swagger-spec-)
12
+ - [Installation ](#installation-)
13
+ - [Upgrade](#upgrade)
14
+ - [Usage ](#usage-)
15
+ - [Model Parsers ](#model-parsers-)
16
+ - [Custom Model Parsers](#custom-model-parsers)
17
+ - [insert_before](#insert_before)
18
+ - [insert_after](#insert_after)
19
+ - [CORS](#cors)
20
+ - [Configure ](#configure-)
21
+ - [host: ](#host-)
22
+ - [base_path: ](#base_path-)
23
+ - [mount_path: ](#mount_path-)
24
+ - [add_base_path: ](#add_base_path-)
25
+ - [add_root: ](#add_root-)
26
+ - [add_version: ](#add_version-)
27
+ - [doc_version: ](#doc_version-)
28
+ - [endpoint_auth_wrapper: ](#endpoint_auth_wrapper-)
29
+ - [swagger_endpoint_guard: ](#swagger_endpoint_guard-)
30
+ - [token_owner: ](#token_owner-)
31
+ - [security_definitions: ](#security_definitions-)
32
+ - [security: ](#security-)
33
+ - [models: ](#models-)
34
+ - [tags: ](#tags-)
35
+ - [hide_documentation_path: (default: true) ](#hide_documentation_path-default-true-)
36
+ - [info: ](#info-)
37
+ - [array_use_braces: ](#array_use_braces-)
38
+ - [api_documentation](#api_documentation)
39
+ - [specific_api_documentation](#specific_api_documentation)
40
+ - [consumes](#consumes)
41
+ - [produces](#produces)
42
+ - [Routes Configuration ](#routes-configuration-)
43
+ - [Swagger Header Parameters ](#swagger-header-parameters--)
44
+ - [Hiding an Endpoint ](#hiding-an-endpoint-)
45
+ - [Overriding Auto-Generated Nicknames ](#overriding-auto-generated-nicknames-)
46
+ - [Specify endpoint details ](#specify-endpoint-details-)
47
+ - [Overriding the route summary ](#overriding-the-route-summary-)
48
+ - [Overriding the tags ](#overriding-the-tags-)
49
+ - [Deprecating routes ](#deprecating-routes-)
50
+ - [Overriding the name of the body parameter ](#overriding-the-name-of-the-body-parameter-)
51
+ - [Defining an endpoint as an array ](#defining-an-endpoint-as-an-array-)
52
+ - [Using an options hash ](#using-an-options-hash-)
53
+ - [Overriding parameter type ](#overriding-parameter-type-)
54
+ - [Overriding data type of the parameter ](#overriding-data-type-of-the-parameter-)
55
+ - [Multiple types ](#multiple-types-)
56
+ - [Array of data type ](#array-of-data-type-)
57
+ - [Collection format of arrays ](#collection-format-of-arrays-)
58
+ - [Hiding parameters ](#hiding-parameters-)
59
+ - [Setting a Swagger default value ](#setting-a-swagger-default-value-)
60
+ - [Setting additionalProperties for object-type parameters ](#setting-additionalproperties-for-object-type-parameters-)
61
+ - [Allow any additional properties](#allow-any-additional-properties)
62
+ - [Allow any additional properties of a particular type](#allow-any-additional-properties-of-a-particular-type)
63
+ - [Allow any additional properties matching a defined schema](#allow-any-additional-properties-matching-a-defined-schema)
64
+ - [Example parameter value ](#example-parameter-value-)
65
+ - [Expose nested namespace as standalone route](#expose-nested-namespace-as-standalone-route)
66
+ - [With a custom name](#with-a-custom-name)
67
+ - [Response documentation ](#response-documentation-)
68
+ - [Changing default status codes ](#changing-default-status-codes-)
69
+ - [Multiple status codes for response ](#multiple-status-codes-for-response-)
70
+ - [File response ](#file-response-)
71
+ - [Default response ](#default-response-)
72
+ - [Extensions ](#extensions-)
73
+ - [Response examples documentation ](#response-examples-documentation-)
74
+ - [Response headers documentation ](#response-headers-documentation-)
75
+ - [Adding root element to responses ](#adding-root-element-to-responses-)
76
+ - [Multiple present Response ](#multiple-present-response-)
77
+ - [Using Grape Entities ](#using-grape-entities-)
78
+ - [Documented class/definition](#documented-classdefinition)
79
+ - [Relationships](#relationships)
80
+ - [1xN](#1xn)
81
+ - [1x1](#1x1)
82
+ - [Inheritance with allOf and discriminator](#inheritance-with-allof-and-discriminator)
83
+ - [Securing the Swagger UI ](#securing-the-swagger-ui-)
84
+ - [Example ](#example-)
85
+ - [Grouping the API list using Namespace](#grouping-the-api-list-using-namespace)
86
+ - [Example Code](#example-code)
87
+ - [Rake Tasks ](#rake-tasks-)
88
+ - [OpenApi/Swagger Documentation](#openapiswagger-documentation)
89
+ - [OpenApi/Swagger Validation](#openapiswagger-validation)
90
+ - [Contributing to grape-swagger](#contributing-to-grape-swagger)
91
+ - [Copyright and License](#copyright-and-license)
22
92
 
23
93
  ## What is grape-swagger? <a name="what"></a>
24
94
 
@@ -43,18 +113,19 @@ This screenshot is based on the [Hussars](https://github.com/LeFnord/hussars) sa
43
113
 
44
114
  The following versions of grape, grape-entity and grape-swagger can currently be used together.
45
115
 
46
- | grape-swagger | swagger spec | grape | grape-entity | representable |
47
- | ------------------ | ------------ | ----------------------- | ------------ | ------------- |
48
- | 0.10.5 | 1.2 | >= 0.10.0 ... <= 0.14.0 | < 0.5.0 | n/a |
49
- | 0.11.0 | 1.2 | >= 0.16.2 | < 0.5.0 | n/a |
50
- | 0.25.2 | 2.0 | >= 0.14.0 ... <= 0.18.0 | <= 0.6.0 | >= 2.4.1 |
51
- | 0.26.0 | 2.0 | >= 0.16.2 ... <= 1.1.0 | <= 0.6.1 | >= 2.4.1 |
52
- | 0.27.0 | 2.0 | >= 0.16.2 ... <= 1.1.0 | >= 0.5.0 | >= 2.4.1 |
53
- | 0.32.0 | 2.0 | >= 0.16.2 | >= 0.5.0 | >= 2.4.1 |
54
- | 0.34.0 | 2.0 | >= 0.16.2 ... < 1.3.0 | >= 0.5.0 | >= 2.4.1 |
55
- | >= 1.0.0 | 2.0 | >= 1.3.0 | >= 0.5.0 | >= 2.4.1 |
56
- | >= 2.0.0 | 2.0 | >= 1.7.0 | >= 0.5.0 | >= 2.4.1 |
57
- | >= 2.0.0 ... < 2.2 | 2.0 | >= 1.8.0 ... < 2.3.0 | >= 0.5.0 | >= 2.4.1 |
116
+ | grape-swagger | swagger spec | grape | grape-entity | representable |
117
+ | --------------------- | ------------ | ----------------------- | ------------ | ------------- |
118
+ | 0.10.5 | 1.2 | >= 0.10.0 ... <= 0.14.0 | < 0.5.0 | n/a |
119
+ | 0.11.0 | 1.2 | >= 0.16.2 | < 0.5.0 | n/a |
120
+ | 0.25.2 | 2.0 | >= 0.14.0 ... <= 0.18.0 | <= 0.6.0 | >= 2.4.1 |
121
+ | 0.26.0 | 2.0 | >= 0.16.2 ... <= 1.1.0 | <= 0.6.1 | >= 2.4.1 |
122
+ | 0.27.0 | 2.0 | >= 0.16.2 ... <= 1.1.0 | >= 0.5.0 | >= 2.4.1 |
123
+ | 0.32.0 | 2.0 | >= 0.16.2 | >= 0.5.0 | >= 2.4.1 |
124
+ | 0.34.0 | 2.0 | >= 0.16.2 ... < 1.3.0 | >= 0.5.0 | >= 2.4.1 |
125
+ | >= 1.0.0 | 2.0 | >= 1.3.0 | >= 0.5.0 | >= 2.4.1 |
126
+ | >= 2.0.0 | 2.0 | >= 1.7.0 | >= 0.5.0 | >= 2.4.1 |
127
+ | >= 2.0.0 ... <= 2.1.2 | 2.0 | >= 1.8.0 ... < 2.3.0 | >= 0.5.0 | >= 2.4.1 |
128
+ | > 2.1.2 | 2.0 | >= 1.8.0 ... < 4.0 | >= 0.5.0 | >= 2.4.1 |
58
129
 
59
130
 
60
131
  ## Swagger-Spec <a name="swagger-spec"></a>
@@ -1446,7 +1517,6 @@ The result will look like following:
1446
1517
  Add the [grape-entity](https://github.com/ruby-grape/grape-entity) and [grape-swagger-entity](https://github.com/ruby-grape/grape-swagger-entity) gem to your Gemfile.
1447
1518
 
1448
1519
  The following example exposes statuses. And exposes statuses documentation adding :type, :desc and :required.
1449
- The documented class/definition name could be set via `#entity_name`.
1450
1520
 
1451
1521
  ```ruby
1452
1522
  module API
@@ -1489,6 +1559,57 @@ module API
1489
1559
  end
1490
1560
  ```
1491
1561
 
1562
+ ### Documented class/definition
1563
+
1564
+ You can set the name of the Entity when being documented via `#entity_name`:
1565
+
1566
+ ```ruby
1567
+ module API
1568
+ module Entities
1569
+ class Status < Grape::Entity
1570
+ expose :text, documentation: { type: 'string', desc: 'Status update text.', required: true }
1571
+ end
1572
+
1573
+ class Link < Grape::Entity
1574
+ expose :href, documentation: { type: 'url' }
1575
+
1576
+ def self.entity_name
1577
+ 'LinkedStatus'
1578
+ end
1579
+
1580
+ end
1581
+ end
1582
+ end
1583
+ ```
1584
+ Should generate the following definitions in your swagger json:
1585
+
1586
+ ```json
1587
+ {
1588
+ "definitions": {
1589
+ "API_Entities_Status": {
1590
+ "type": "object",
1591
+ "properties": {
1592
+ "text": {
1593
+ "type": "string",
1594
+ "description": "Status update text.",
1595
+ },
1596
+ },
1597
+ "required": [
1598
+ "text",
1599
+ ],
1600
+ "description": "API_Entities_Pet model"
1601
+ },
1602
+ "LinkedStatus": {
1603
+ "type": "object",
1604
+ "properties": {
1605
+ "href": {
1606
+ "type": "url",
1607
+ },
1608
+ "description": "LinkedStatus model"
1609
+ }
1610
+ }
1611
+ }
1612
+ ```
1492
1613
 
1493
1614
  ### Relationships
1494
1615
 
data/RELEASING.md CHANGED
@@ -16,7 +16,7 @@ Check that the last build succeeded in [Travis CI](https://travis-ci.org/ruby-gr
16
16
  Change "Next" in [CHANGELOG.md](CHANGELOG.md) to the current date.
17
17
 
18
18
  ```
19
- ### 0.7.2 (February 6, 2014)
19
+ ### 2.2.0 (2025-11-21)
20
20
  ```
21
21
 
22
22
  Remove the lines with "Your contribution here.", since there will be no more contributions to this release.
@@ -25,7 +25,7 @@ Commit your changes.
25
25
 
26
26
  ```
27
27
  git add CHANGELOG.md lib/grape-swagger/version.rb
28
- git commit -m "Preparing for release, 0.7.2."
28
+ git commit -m "Preparing for release, 2.2.0."
29
29
  git push origin master
30
30
  ```
31
31
 
@@ -34,10 +34,10 @@ Release.
34
34
  ```
35
35
  $ rake release
36
36
 
37
- grape-swagger 0.7.2 built to pkg/grape-swagger-0.7.2.gem.
38
- Tagged v0.7.2.
37
+ grape-swagger 2.2.0 built to pkg/grape-swagger-2.2.0.gem.
38
+ Tagged v2.2.0.
39
39
  Pushed git commits and tags.
40
- Pushed grape-swagger 0.7.2 to rubygems.org.
40
+ Pushed grape-swagger 2.2.0 to rubygems.org.
41
41
  ```
42
42
 
43
43
  ### Prepare for the Next Version
@@ -47,7 +47,7 @@ Increment the minor version, the third number, modify [lib/grape-swagger/version
47
47
  Add the next release to [CHANGELOG.md](CHANGELOG.md).
48
48
 
49
49
  ```
50
- ### 0.7.3 (Next)
50
+ ### 2.2.1 (Next)
51
51
 
52
52
  #### Features
53
53
 
@@ -62,7 +62,7 @@ Commit your changes.
62
62
 
63
63
  ```
64
64
  git add CHANGELOG.md lib/grape-swagger/version.rb
65
- git commit -m "Preparing for next developer iteration, 0.7.3."
65
+ git commit -m "Preparing for next developer iteration, 2.2.1."
66
66
  git push origin master
67
67
  ```
68
68
 
@@ -71,7 +71,7 @@ git push origin master
71
71
  Make an announcement on the [ruby-grape@googlegroups.com](mailto:ruby-grape@googlegroups.com) mailing list. The general format is as follows.
72
72
 
73
73
  ```
74
- Grape-Swagger 0.7.2 has been released.
74
+ Grape-Swagger 2.2.0 has been released.
75
75
 
76
76
  There were 8 contributors to this release, not counting documentation.
77
77
 
@@ -15,8 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.metadata['rubygems_mfa_required'] = 'true'
16
16
 
17
17
  s.required_ruby_version = '>= 3.1'
18
- s.add_dependency 'grape', '>= 1.7', '< 3.0'
19
- s.add_dependency 'rack-test', '~> 2'
18
+ s.add_dependency 'grape', '>= 1.7', '< 4.0'
20
19
 
21
20
  s.files = Dir['lib/**/*', '*.md', 'LICENSE.txt', 'grape-swagger.gemspec']
22
21
  s.require_paths = ['lib']
@@ -22,7 +22,7 @@ module GrapeSwagger
22
22
  }
23
23
  else
24
24
  properties, required = parsed_response
25
- unless properties&.any?
25
+ if properties.nil?
26
26
  raise GrapeSwagger::Errors::SwaggerSpec,
27
27
  "Empty model #{model_name}, swagger 2.0 doesn't support empty definitions."
28
28
  end
@@ -7,7 +7,7 @@ module GrapeSwagger
7
7
  def to_format(parameters)
8
8
  parameters.reject { |parameter| parameter[:in] == 'body' }.each do |b|
9
9
  related_parameters = parameters.select do |p|
10
- p[:name] != b[:name] && p[:name].to_s.start_with?("#{b[:name].to_s.gsub(/\[\]\z/, '')}[")
10
+ p[:name] != b[:name] && p[:name].start_with?("#{b[:name].to_s.gsub(/\[\]\z/, '')}[")
11
11
  end
12
12
  parameters.reject! { |p| p[:name] == b[:name] } if move_down(b, related_parameters)
13
13
  end
@@ -163,7 +163,7 @@ module GrapeSwagger
163
163
  end
164
164
 
165
165
  def prepare_nested_names(property, params)
166
- params.each { |x| x[:name] = x[:name].sub(property, '').sub('[', '').sub(']', '') }
166
+ params.each { |x| x[:name] = x[:name].sub(property.to_s, '').sub('[', '').sub(']', '') }
167
167
  end
168
168
 
169
169
  def unify!(params)
@@ -141,8 +141,10 @@ module GrapeSwagger
141
141
  end
142
142
 
143
143
  def document_example(settings)
144
- example = settings[:example]
145
- @parsed_param[:example] = example if example
144
+ return unless settings.key?(:example)
145
+
146
+ key = @parsed_param[:in] == 'body' ? :example : :'x-example'
147
+ @parsed_param[key] = settings[:example]
146
148
  end
147
149
 
148
150
  def param_type(value_type, consumes)
@@ -11,7 +11,6 @@ require 'grape-swagger/doc_methods/path_string'
11
11
  require 'grape-swagger/doc_methods/tag_name_description'
12
12
  require 'grape-swagger/doc_methods/parse_params'
13
13
  require 'grape-swagger/doc_methods/move_params'
14
- require 'grape-swagger/doc_methods/headers'
15
14
  require 'grape-swagger/doc_methods/build_model_definition'
16
15
  require 'grape-swagger/doc_methods/version'
17
16
 
@@ -2,7 +2,10 @@
2
2
 
3
3
  require 'active_support'
4
4
  require 'active_support/core_ext/string/inflections'
5
- require 'grape-swagger/endpoint/params_parser'
5
+ require_relative 'request_param_parsers/headers'
6
+ require_relative 'request_param_parsers/route'
7
+ require_relative 'request_param_parsers/body'
8
+ require_relative 'token_owner_resolver'
6
9
 
7
10
  module Grape
8
11
  class Endpoint # rubocop:disable Metrics/ClassLength
@@ -12,9 +15,7 @@ module Grape
12
15
  if content_types.empty?
13
16
  formats = [target_class.format, target_class.default_format].compact.uniq
14
17
  formats = GrapeSwagger::FORMATTER_DEFAULTS.keys if formats.empty?
15
- content_types = GrapeSwagger::CONTENT_TYPE_DEFAULTS.select do |content_type, _mime_type|
16
- formats.include? content_type
17
- end.values
18
+ content_types = formats.filter_map { |f| GrapeSwagger::CONTENT_TYPE_DEFAULTS[f] }
18
19
  end
19
20
 
20
21
  content_types.uniq
@@ -154,8 +155,8 @@ module Grape
154
155
  end
155
156
 
156
157
  def produces_object(route, format)
157
- return ['application/octet-stream'] if file_response?(route.attributes.success) &&
158
- !route.attributes.produces.present?
158
+ return ['application/octet-stream'] if file_response?(route.options[:success]) &&
159
+ !route.options[:produces].present?
159
160
 
160
161
  mime_types = GrapeSwagger::DocMethods::ProducesConsumes.call(format)
161
162
 
@@ -383,45 +384,15 @@ module Grape
383
384
  end
384
385
 
385
386
  def build_request_params(route, settings)
386
- required = merge_params(route)
387
- required = GrapeSwagger::DocMethods::Headers.parse(route) + required unless route.headers.nil?
388
-
389
- default_type(required)
390
-
391
- request_params = GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings, self)
392
-
393
- request_params.empty? ? required : request_params
394
- end
395
-
396
- def merge_params(route)
397
- path_params = get_path_params(route.app&.inheritable_setting&.namespace_stackable)
398
- param_keys = route.params.keys
399
-
400
- # Merge path params options into route params
401
- route_params = route.params
402
- route_params.each_key do |key|
403
- path = path_params[key] || {}
404
- params = route_params[key]
405
- params = {} unless params.is_a? Hash
406
- route_params[key] = path.merge(params)
387
+ GrapeSwagger.request_param_parsers.each_with_object({}) do |parser_klass, accum|
388
+ params = parser_klass.parse(
389
+ route,
390
+ accum,
391
+ settings,
392
+ self
393
+ )
394
+ accum.merge!(params.stringify_keys)
407
395
  end
408
-
409
- route_params.delete_if { |key| key.is_a?(String) && param_keys.include?(key.to_sym) }.to_a
410
- end
411
-
412
- # Iterates over namespaces recursively
413
- # to build a hash of path params with options, including type
414
- def get_path_params(stackable_values)
415
- params = {}
416
- return param unless stackable_values
417
- return params unless stackable_values.is_a? Grape::Util::StackableValues
418
-
419
- stackable_values&.new_values&.dig(:namespace)&.each do |namespace| # rubocop:disable Style/SafeNavigationChainLength
420
- space = namespace.space.to_s.gsub(':', '')
421
- params[space] = namespace.options || {}
422
- end
423
- inherited_params = get_path_params(stackable_values.inherited_values)
424
- inherited_params.merge(params)
425
396
  end
426
397
 
427
398
  def default_type(params)
@@ -469,7 +440,10 @@ module Grape
469
440
  route_hidden = route.options[:hidden] if route.options.key?(:hidden)
470
441
  return route_hidden unless route_hidden.is_a?(Proc)
471
442
 
472
- options[:token_owner] ? route_hidden.call(send(options[:token_owner].to_sym)) : route_hidden.call
443
+ return route_hidden.call unless options[:token_owner]
444
+
445
+ token_owner = GrapeSwagger::TokenOwnerResolver.resolve(self, options[:token_owner])
446
+ GrapeSwagger::TokenOwnerResolver.evaluate_proc(route_hidden, token_owner)
473
447
  end
474
448
 
475
449
  def hidden_parameter?(value)
@@ -13,5 +13,7 @@ module GrapeSwagger
13
13
  end
14
14
  end
15
15
  end
16
+
17
+ class TokenOwnerNotFound < NoMethodError; end
16
18
  end
17
19
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'request_param_parsers/headers'
4
+ require_relative 'request_param_parsers/route'
5
+ require_relative 'request_param_parsers/body'
6
+
7
+ module GrapeSwagger
8
+ class RequestParamParserRegistry
9
+ DEFAULT_PARSERS = [
10
+ GrapeSwagger::RequestParamParsers::Headers,
11
+ GrapeSwagger::RequestParamParsers::Route,
12
+ GrapeSwagger::RequestParamParsers::Body
13
+ ].freeze
14
+
15
+ include Enumerable
16
+
17
+ def initialize
18
+ @parsers = DEFAULT_PARSERS.dup
19
+ end
20
+
21
+ def register(klass)
22
+ remove_parser(klass)
23
+ @parsers << klass
24
+ end
25
+
26
+ def insert_before(before_klass, klass)
27
+ remove_parser(klass)
28
+ insert_at = @parsers.index(before_klass) || @parsers.size
29
+ @parsers.insert(insert_at, klass)
30
+ end
31
+
32
+ def insert_after(after_klass, klass)
33
+ remove_parser(klass)
34
+ insert_at = @parsers.index(after_klass)
35
+ @parsers.insert(insert_at ? insert_at + 1 : @parsers.size, klass)
36
+ end
37
+
38
+ def each(&)
39
+ @parsers.each(&)
40
+ end
41
+
42
+ private
43
+
44
+ def remove_parser(klass)
45
+ @parsers.reject! { |k| k == klass }
46
+ end
47
+ end
48
+ end
@@ -1,27 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrapeSwagger
4
- module Endpoint
5
- class ParamsParser
6
- attr_reader :params, :settings, :endpoint
4
+ module RequestParamParsers
5
+ class Body
6
+ attr_reader :route, :params, :settings, :endpoint
7
7
 
8
- def self.parse_request_params(params, settings, endpoint)
9
- new(params, settings, endpoint).parse_request_params
8
+ def self.parse(route, params, settings, endpoint)
9
+ new(route, params, settings, endpoint).parse
10
10
  end
11
11
 
12
- def initialize(params, settings, endpoint)
12
+ def initialize(_route, params, settings, endpoint)
13
13
  @params = params
14
14
  @settings = settings
15
15
  @endpoint = endpoint
16
16
  end
17
17
 
18
- def parse_request_params
18
+ def parse
19
19
  public_params.each_with_object({}) do |(name, options), memo|
20
20
  name = name.to_s
21
- param_type = options[:type]
22
- param_type = param_type.to_s unless param_type.nil?
21
+ param_type = options[:type]&.to_s
23
22
 
24
- if param_type_is_array?(param_type)
23
+ if array_param?(param_type)
25
24
  options[:is_array] = true
26
25
  name += '[]' if array_use_braces?
27
26
  end
@@ -36,8 +35,8 @@ module GrapeSwagger
36
35
  @array_use_braces ||= settings[:array_use_braces] && !includes_body_param?
37
36
  end
38
37
 
39
- def param_type_is_array?(param_type)
40
- return false unless param_type
38
+ def array_param?(param_type)
39
+ return false if param_type.nil?
41
40
  return true if param_type == 'Array'
42
41
 
43
42
  param_types = param_type.match(/\[(.*)\]$/)
@@ -48,21 +47,14 @@ module GrapeSwagger
48
47
  end
49
48
 
50
49
  def public_params
51
- params.select { |param| public_parameter?(param) }
50
+ params.select { |_key, param| public_parameter?(param) }
52
51
  end
53
52
 
54
- def public_parameter?(param)
55
- param_options = param.last
53
+ def public_parameter?(param_options)
56
54
  return true unless param_options.key?(:documentation) && !param_options[:required]
57
55
 
58
56
  param_hidden = param_options[:documentation].fetch(:hidden, false)
59
- if param_hidden.is_a?(Proc)
60
- param_hidden = if settings[:token_owner]
61
- param_hidden.call(endpoint.send(settings[:token_owner].to_sym))
62
- else
63
- param_hidden.call
64
- end
65
- end
57
+ param_hidden = evaluate_hidden_proc(param_hidden) if param_hidden.is_a?(Proc)
66
58
  !param_hidden
67
59
  end
68
60
 
@@ -71,6 +63,13 @@ module GrapeSwagger
71
63
  options.dig(:documentation, :param_type) == 'body' || options.dig(:documentation, :in) == 'body'
72
64
  end
73
65
  end
66
+
67
+ def evaluate_hidden_proc(hidden_proc)
68
+ return hidden_proc.call unless settings[:token_owner]
69
+
70
+ token_owner = GrapeSwagger::TokenOwnerResolver.resolve(endpoint, settings[:token_owner])
71
+ GrapeSwagger::TokenOwnerResolver.evaluate_proc(hidden_proc, token_owner)
72
+ end
74
73
  end
75
74
  end
76
75
  end