Module: GrapeOAS::TypeResolvers::Base
- Included in:
- ArrayResolver, DryTypeResolver, PrimitiveResolver
- Defined in:
- lib/grape_oas/type_resolvers/base.rb
Overview
Base module that defines the interface for all type resolvers.
Why TypeResolvers Exist
Grape stores parameter types as strings for documentation purposes. When you declare ‘type: [MyApp::Types::UUID]` in Grape, it gets stored as the string “[MyApp::Types::UUID]” in route.params. This happens in Grape::Validations::ParamsDocumentation::TypeCache which calls `type.to_s` for memory optimization (singleton cache avoids repeated string allocations).
Why Not Direct Grape Access?
While the original type IS preserved in Grape’s CoerceValidator (accessible via ‘endpoint.send(:validations)`), this approach has drawbacks:
-
**Protected API**: ‘validations` is a protected method, requiring `send`
-
Coupling: Tight coupling to Grape’s internal validator structure
-
Complexity: Matching validators to params by name is error-prone
-
Performance: Iterating validators for each param is inefficient
TypeResolvers provide a cleaner abstraction:
-
Try to resolve stringified types back to actual classes via ‘Object.const_get`
-
Extract rich metadata from resolved types (Dry::Types format, constraints)
-
Fall back gracefully to string parsing when classes aren’t available
-
Extensible registry allows custom type handling
Implementing a Custom Resolver
class MyCustomTypeResolver
extend GrapeOAS::TypeResolvers::Base
def self.handles?(type)
# Return true if this resolver can handle the type
resolve_class(type)&.ancestors&.include?(MyCustomType)
end
def self.build_schema(type)
# Build and return an ApiModel::Schema
end
end
GrapeOAS.type_resolvers.register(MyCustomTypeResolver)
Instance Method Summary collapse
-
#build_schema(type) ⇒ ApiModel::Schema
Builds an OpenAPI schema from the given type.
-
#handles?(type) ⇒ Boolean
Checks if this resolver can handle the given type.
-
#infer_format_from_name(name) ⇒ String?
Infers OpenAPI format from a type name suffix.
-
#primitive_to_schema_type(klass) ⇒ String
Converts a Ruby class to its OpenAPI schema type.
-
#resolve_class(type) ⇒ Class?
Attempts to resolve a stringified type back to its actual class.
Instance Method Details
#build_schema(type) ⇒ ApiModel::Schema
Builds an OpenAPI schema from the given type.
61 62 63 |
# File 'lib/grape_oas/type_resolvers/base.rb', line 61 def build_schema(type) raise NotImplementedError, "#{self} must implement .build_schema(type)" end |
#handles?(type) ⇒ Boolean
Checks if this resolver can handle the given type.
53 54 55 |
# File 'lib/grape_oas/type_resolvers/base.rb', line 53 def handles?(type) raise NotImplementedError, "#{self} must implement .handles?(type)" end |
#infer_format_from_name(name) ⇒ String?
Infers OpenAPI format from a type name suffix. Shared across resolvers for consistent format detection.
87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/grape_oas/type_resolvers/base.rb', line 87 def infer_format_from_name(name) last_segment = name.to_s.split("::").last.to_s return nil if last_segment.empty? return "uuid" if last_segment.end_with?("UUID") return "date-time" if last_segment.end_with?("DateTime") return "date" if last_segment.end_with?("Date") return "email" if last_segment.end_with?("Email") return "uri" if last_segment.end_with?("URI", "Url", "URL") nil end |
#primitive_to_schema_type(klass) ⇒ String
Converts a Ruby class to its OpenAPI schema type. Shared across resolvers for consistent type mapping.
105 106 107 |
# File 'lib/grape_oas/type_resolvers/base.rb', line 105 def primitive_to_schema_type(klass) Constants.primitive_type(klass) || Constants::SchemaTypes::STRING end |
#resolve_class(type) ⇒ Class?
Attempts to resolve a stringified type back to its actual class. Uses Object.const_get for resolution, similar to grape-swagger’s approach.
70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/grape_oas/type_resolvers/base.rb', line 70 def resolve_class(type) return type if type.is_a?(Class) return type if type.respond_to?(:primitive) # Dry::Type return nil unless type.is_a?(String) return nil if type.empty? Object.const_get(type) rescue NameError nil end |