Skip to content

Commit 49e8e63

Browse files
authored
Merge pull request #2583 from ericproulx/optimize_api_documentation
Optimize API parameter documentation and memory usage
2 parents 5203702 + ac5f211 commit 49e8e63

File tree

6 files changed

+209
-236
lines changed

6 files changed

+209
-236
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* [#2580](https://github.com/ruby-grape/grape/pull/2580): Refactor endpoint helpers and error middleware integration - [@ericproulx](https://github.com/ericproulx).
1010
* [#2581](https://github.com/ruby-grape/grape/pull/2581): Delegate `to_s` in Grape::API::Instance - [@ericproulx](https://github.com/ericproulx).
1111
* [#2582](https://github.com/ruby-grape/grape/pull/2582): Fix leaky slash when normalizing - [@ericproulx](https://github.com/ericproulx).
12+
* [#2583](https://github.com/ruby-grape/grape/pull/2583): Optimize api parameter documentation and memory usage - [@ericproulx](https://github.com/ericproulx).
1213
* Your contribution here.
1314

1415
#### Fixes

lib/grape/validations/attributes_doc.rb

Lines changed: 0 additions & 60 deletions
This file was deleted.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# frozen_string_literal: true
2+
3+
module Grape
4+
module Validations
5+
# Documents parameters of an endpoint. If documentation isn't needed (for instance, it is an
6+
# internal API), the class only cleans up attributes to avoid junk in RAM.
7+
8+
module ParamsDocumentation
9+
def document_params(attrs, validations, type = nil, values = nil, except_values = nil)
10+
if @api.namespace_inheritable(:do_not_document)
11+
validations.except!(:desc, :description, :documentation)
12+
else
13+
documented_attrs = attrs.each_with_object({}) do |name, memo|
14+
memo[full_name(name)] = extract_details(validations, type, values, except_values)
15+
end
16+
@api.namespace_stackable(:params, documented_attrs)
17+
end
18+
end
19+
20+
private
21+
22+
def extract_details(validations, type, values, except_values)
23+
{}.tap do |details|
24+
details[:required] = validations.key?(:presence)
25+
details[:type] = TypeCache[type] if type
26+
details[:values] = values if values
27+
details[:except_values] = except_values if except_values
28+
details[:default] = validations[:default] if validations.key?(:default)
29+
if validations.key?(:length)
30+
details[:min_length] = validations[:length][:min] if validations[:length].key?(:min)
31+
details[:max_length] = validations[:length][:max] if validations[:length].key?(:max)
32+
end
33+
34+
desc = validations.delete(:desc) || validations.delete(:description)
35+
details[:desc] = desc if desc
36+
37+
documentation = validations.delete(:documentation)
38+
details[:documentation] = documentation if documentation
39+
end
40+
end
41+
42+
class TypeCache < Grape::Util::Cache
43+
def initialize
44+
super
45+
@cache = Hash.new do |h, type|
46+
h[type] = type.to_s
47+
end
48+
end
49+
end
50+
end
51+
end
52+
end

lib/grape/validations/params_scope.rb

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class ParamsScope
77
attr_reader :type, :params_meeting_dependency
88

99
include Grape::DSL::Parameters
10+
include Grape::Validations::ParamsDocumentation
1011

1112
# There are a number of documentation options on entities that don't have
1213
# corresponding validators. Since there is nowhere that enumerates them all,
@@ -323,23 +324,14 @@ def configure_declared_params
323324
end
324325

325326
def validates(attrs, validations)
326-
doc = AttributesDoc.new @api, self
327-
doc.extract_details validations
328-
329327
coerce_type = infer_coercion(validations)
330-
331-
doc.type = coerce_type
332-
328+
required = validations.key?(:presence)
333329
default = validations[:default]
334330
values = validations[:values].is_a?(Hash) ? validations.dig(:values, :value) : validations[:values]
335-
336-
doc.values = values
337-
338331
except_values = validations[:except_values].is_a?(Hash) ? validations.dig(:except_values, :value) : validations[:except_values]
339332

340333
# NB. values and excepts should be nil, Proc, Array, or Range.
341334
# Specifically, values should NOT be a Hash
342-
343335
# use values or excepts to guess coerce type when stated type is Array
344336
coerce_type = guess_coerce_type(coerce_type, values, except_values)
345337

@@ -349,23 +341,23 @@ def validates(attrs, validations)
349341
# type should be compatible with values array, if both exist
350342
validate_value_coercion(coerce_type, values, except_values)
351343

352-
doc.document attrs
344+
document_params attrs, validations, coerce_type, values, except_values
353345

354346
opts = derive_validator_options(validations)
355347

356348
# Validate for presence before any other validators
357-
validates_presence(validations, attrs, doc, opts)
349+
validates_presence(validations, attrs, opts)
358350

359351
# Before we run the rest of the validators, let's handle
360352
# whatever coercion so that we are working with correctly
361353
# type casted values
362-
coerce_type validations, attrs, doc, opts
354+
coerce_type validations, attrs, required, opts
363355

364356
validations.each do |type, options|
365357
# Don't try to look up validators for documentation params that don't have one.
366358
next if RESERVED_DOCUMENTATION_KEYWORDS.include?(type)
367359

368-
validate(type, options, attrs, doc, opts)
360+
validate(type, options, attrs, required, opts)
369361
end
370362
end
371363

@@ -429,7 +421,7 @@ def check_coerce_with(validations)
429421
# composited from more than one +requires+/+optional+
430422
# parameter, and needs to be run before most other
431423
# validations.
432-
def coerce_type(validations, attrs, doc, opts)
424+
def coerce_type(validations, attrs, required, opts)
433425
check_coerce_with(validations)
434426

435427
return unless validations.key?(:coerce)
@@ -439,7 +431,7 @@ def coerce_type(validations, attrs, doc, opts)
439431
method: validations[:coerce_with],
440432
message: validations[:coerce_message]
441433
}
442-
validate('coerce', coerce_options, attrs, doc, opts)
434+
validate('coerce', coerce_options, attrs, required, opts)
443435
validations.delete(:coerce_with)
444436
validations.delete(:coerce)
445437
validations.delete(:coerce_message)
@@ -465,11 +457,11 @@ def check_incompatible_option_values(default, values, except_values)
465457
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
466458
end
467459

468-
def validate(type, options, attrs, doc, opts)
460+
def validate(type, options, attrs, required, opts)
469461
validator_options = {
470462
attributes: attrs,
471463
options: options,
472-
required: doc.required,
464+
required: required,
473465
params_scope: self,
474466
opts: opts,
475467
validator_class: Validations.require_validator(type)
@@ -516,10 +508,10 @@ def derive_validator_options(validations)
516508
}
517509
end
518510

519-
def validates_presence(validations, attrs, doc, opts)
511+
def validates_presence(validations, attrs, opts)
520512
return unless validations.key?(:presence) && validations[:presence]
521513

522-
validate('presence', validations.delete(:presence), attrs, doc, opts)
514+
validate('presence', validations.delete(:presence), attrs, true, opts)
523515
validations.delete(:message) if validations.key?(:message)
524516
end
525517
end

spec/grape/validations/attributes_doc_spec.rb

Lines changed: 0 additions & 156 deletions
This file was deleted.

0 commit comments

Comments
 (0)