Class: SoberSwag::Compiler::Type
- Inherits:
-
Object
- Object
- SoberSwag::Compiler::Type
- Defined in:
- lib/sober_swag/compiler/type.rb
Overview
A compiler for swagger-able types.
This class turns Swagger-able types into a schema. This Schema may be:
- a schema object with #object_schema
- a path schema with #path_schema
- a query schema with #query_schema
As such, it compiles all types to all applicable schemas.
While this class compiles one type at a time, it keeps track of the other types needed to describe this schema. It stores these types in a set, available at #found_types.
For example, with a schema like:
class Bar < SoberSwag::InputObject
attribute :baz, primitive(:String)
end
class Foo < SoberSwag::InputObject
attribute :bar, Bar
end
If you compile Foo
with this class, #found_types will include Bar
.
Defined Under Namespace
Classes: TooComplicatedError, TooComplicatedForPathError, TooComplicatedForQueryError
Constant Summary collapse
- METADATA_KEYS =
A list of acceptable keys to use as metadata for an object schema. All other metadata keys defined on a type with InputObject.meta will be ignored.
%i[description deprecated].freeze
- DEFAULT_QUERY_SCHEMA_ATTRS =
{ in: :query, style: :deepObject, explode: true }.freeze
Instance Attribute Summary collapse
-
#type ⇒ Class
readonly
The type we are compiling.
Instance Method Summary collapse
- #ensure_uncomplicated(key, value) ⇒ Object private
-
#eql?(other) ⇒ Boolean
Standard ruby equality.
- #flatten_one_ofs(object) ⇒ Object private
- #flatten_oneofs_hash(object) ⇒ Object private
-
#found_types ⇒ Set<Class>
Get a set of all other types needed to compile this type.
- #generate_schema_stub ⇒ Object private
-
#hash ⇒ Object
Standard ruby hashing method.
-
#initialize(type) ⇒ Type
constructor
Create a new compiler for a swagger-able type.
- #make_object_schema(metadata_keys: METADATA_KEYS) ⇒ Object private
- #mapped_type ⇒ Object private
- #normalize(object) ⇒ Object private
-
#object_schema ⇒ Hash
Get back the schema object for the type described.
-
#object_schema_meta ⇒ Hash
private
Get metadata attributes to be used if compiling an object schema.
- #one_of_to_schema(object) ⇒ Object private
-
#parsed_result ⇒ Object
This type, parsed into an AST.
- #parsed_type ⇒ Object private
-
#path_schema ⇒ Hash
The schema for this type when it is path of the path.
- #path_schema_stub ⇒ Object private
-
#query_schema ⇒ Hash
The schema for this type when it is part of the query.
-
#ref_name ⇒ String
Get the name of this type if it is to be used in a
$ref
key. -
#rewrite_sums(object) ⇒ Object
private
rubocop:disable Metrics/MethodLength.
-
#schema_stub ⇒ Hash
Give a "stub type" for this schema.
-
#standalone? ⇒ true, false
Is this type standalone, IE, worth serializing on its own in the schemas section of our schema?.
-
#to_object_schema(object, metadata_keys = METADATA_KEYS) ⇒ Object
private
rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity.
- #type_for_parser ⇒ Object private
Constructor Details
#initialize(type) ⇒ Type
Create a new compiler for a swagger-able type.
53 54 55 |
# File 'lib/sober_swag/compiler/type.rb', line 53 def initialize(type) @type = type end |
Instance Attribute Details
#type ⇒ Class (readonly)
Returns the type we are compiling.
59 60 61 |
# File 'lib/sober_swag/compiler/type.rb', line 59 def type @type end |
Instance Method Details
#ensure_uncomplicated(key, value) ⇒ Object (private)
301 302 303 304 305 306 307 308 309 |
# File 'lib/sober_swag/compiler/type.rb', line 301 def ensure_uncomplicated(key, value) return if value[:type] return value[:oneOf].each { |member| ensure_uncomplicated(key, member) } if value[:oneOf] raise TooComplicatedError, <<~ERROR Property #{key} has object-schema #{value}, but this type of param should be simple (IE a primitive of some kind) ERROR end |
#eql?(other) ⇒ Boolean
Standard ruby equality.
144 145 146 |
# File 'lib/sober_swag/compiler/type.rb', line 144 def eql?(other) other.class == self.class && other.type == type end |
#flatten_one_ofs(object) ⇒ Object (private)
224 225 226 227 228 229 230 231 |
# File 'lib/sober_swag/compiler/type.rb', line 224 def flatten_one_ofs(object) case object when Nodes::OneOf Nodes::OneOf.new(object.deconstruct.uniq) else object end end |
#flatten_oneofs_hash(object) ⇒ Object (private)
281 282 283 284 285 |
# File 'lib/sober_swag/compiler/type.rb', line 281 def flatten_oneofs_hash(object) object.map { |h| h[:oneOf] || h }.flatten end |
#found_types ⇒ Set<Class>
Get a set of all other types needed to compile this type. This set will not include the type being compiled.
128 129 130 131 132 133 134 |
# File 'lib/sober_swag/compiler/type.rb', line 128 def found_types @found_types ||= begin (_, found_types) = parsed_result found_types end end |
#generate_schema_stub ⇒ Object (private)
181 182 183 184 185 186 187 |
# File 'lib/sober_swag/compiler/type.rb', line 181 def generate_schema_stub if type.is_a?(Class) SoberSwag::Compiler::Primitive.new(type).type_hash else object_schema end end |
#hash ⇒ Object
Standard ruby hashing method. Compilers hash to the same value if they are compiling the same type.
151 152 153 |
# File 'lib/sober_swag/compiler/type.rb', line 151 def hash [self.class, type].hash end |
#make_object_schema(metadata_keys: METADATA_KEYS) ⇒ Object (private)
198 199 200 |
# File 'lib/sober_swag/compiler/type.rb', line 198 def make_object_schema(metadata_keys: METADATA_KEYS) normalize(mapped_type).cata { |e| to_object_schema(e, ) }.merge() end |
#mapped_type ⇒ Object (private)
177 178 179 |
# File 'lib/sober_swag/compiler/type.rb', line 177 def mapped_type @mapped_type ||= parsed_type.map { |v| SoberSwag::Compiler::Primitive.new(v).type_hash } end |
#normalize(object) ⇒ Object (private)
202 203 204 |
# File 'lib/sober_swag/compiler/type.rb', line 202 def normalize(object) object.cata { |e| rewrite_sums(e) }.cata { |e| flatten_one_ofs(e) } end |
#object_schema ⇒ Hash
Get back the schema object for the type described.
74 75 76 77 |
# File 'lib/sober_swag/compiler/type.rb', line 74 def object_schema @object_schema ||= make_object_schema end |
#object_schema_meta ⇒ Hash (private)
Get metadata attributes to be used if compiling an object schema.
161 162 163 164 165 166 167 |
# File 'lib/sober_swag/compiler/type.rb', line 161 def return {} unless standalone? && type <= SoberSwag::Type::Named { description: type.description }.reject { |_, v| v.nil? } end |
#one_of_to_schema(object) ⇒ Object (private)
268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/sober_swag/compiler/type.rb', line 268 def one_of_to_schema(object) if object.deconstruct.include?({ type: :null }) rejected = object.deconstruct.reject { |e| e[:type] == :null } if rejected.length == 1 rejected.first.merge(nullable: true) else { oneOf: flatten_oneofs_hash(rejected), nullable: true } end else { oneOf: flatten_oneofs_hash(object.deconstruct) } end end |
#parsed_result ⇒ Object
This type, parsed into an AST.
138 139 140 |
# File 'lib/sober_swag/compiler/type.rb', line 138 def parsed_result @parsed_result ||= Parser.new(type_for_parser).run_parser end |
#parsed_type ⇒ Object (private)
169 170 171 172 173 174 175 |
# File 'lib/sober_swag/compiler/type.rb', line 169 def parsed_type @parsed_type ||= begin (parsed,) = parsed_result parsed end end |
#path_schema ⇒ Hash
The schema for this type when it is path of the path.
93 94 95 96 97 98 99 100 |
# File 'lib/sober_swag/compiler/type.rb', line 93 def path_schema path_schema_stub.map do |e| ensure_uncomplicated(e[:name], e[:schema]) e.merge(in: :path) end rescue TooComplicatedError => e raise TooComplicatedForPathError, e. end |
#path_schema_stub ⇒ Object (private)
287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/sober_swag/compiler/type.rb', line 287 def path_schema_stub @path_schema_stub ||= make_object_schema(metadata_keys: METADATA_KEYS | %i[style explode])[:properties].map do |k, v| # ensure_uncomplicated(k, v) { name: k, schema: v.reject { |key, _| %i[required nullable explode style].include?(key) }, required: object_schema[:required].include?(k) || false, style: v[:style], explode: v[:explode] }.reject { |_, v2| v2.nil? } end end |
#query_schema ⇒ Hash
The schema for this type when it is part of the query.
108 109 110 111 112 |
# File 'lib/sober_swag/compiler/type.rb', line 108 def query_schema path_schema_stub.map { |e| DEFAULT_QUERY_SCHEMA_ATTRS.merge(e) } rescue TooComplicatedError => e raise TooComplicatedForQueryError, e. end |
#ref_name ⇒ String
Get the name of this type if it is to be used in a $ref
key.
This is useful if we are going to use this type compiler to compile an attribute of another object.
119 120 121 |
# File 'lib/sober_swag/compiler/type.rb', line 119 def ref_name SoberSwag::Compiler::Primitive.new(type).ref_name end |
#rewrite_sums(object) ⇒ Object (private)
rubocop:disable Metrics/MethodLength
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/sober_swag/compiler/type.rb', line 206 def rewrite_sums(object) # rubocop:disable Metrics/MethodLength case object when Nodes::Sum lhs, rhs = object.deconstruct if lhs.is_a?(Nodes::OneOf) && rhs.is_a?(Nodes::OneOf) Nodes::OneOf.new(lhs.deconstruct + rhs.deconstruct) elsif lhs.is_a?(Nodes::OneOf) Nodes::OneOf.new([*lhs.deconstruct, rhs]) elsif rhs.is_a?(Nodes::OneOf) Nodes::OneOf.new([lhs, *rhs.deconstruct]) else Nodes::OneOf.new([lhs, rhs]) end else object end end |
#schema_stub ⇒ Hash
Give a "stub type" for this schema. This is suitable to use as the schema for attributes of other schemas. Almost always generates a ref object.
84 85 86 |
# File 'lib/sober_swag/compiler/type.rb', line 84 def schema_stub @schema_stub ||= generate_schema_stub end |
#standalone? ⇒ true, false
Is this type standalone, IE, worth serializing on its own in the schemas section of our schema?
65 66 67 |
# File 'lib/sober_swag/compiler/type.rb', line 65 def standalone? type.is_a?(Class) end |
#to_object_schema(object, metadata_keys = METADATA_KEYS) ⇒ Object (private)
rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/sober_swag/compiler/type.rb', line 233 def to_object_schema(object, = METADATA_KEYS) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity case object when Nodes::List { type: :array, items: object.element } when Nodes::Enum { type: :string, enum: object.values } when Nodes::OneOf one_of_to_schema(object) when Nodes::Object # openAPI requires that you give a list of required attributes # (which IMO is the *totally* wrong thing to do but whatever) # so we must do this garbage required = object.deconstruct.filter { |(_, b)| b[:required] }.map(&:first) { type: :object, properties: object.deconstruct.map { |(a, b)| [a, b.reject { |k, _| k == :required }] }.to_h, required: required } when Nodes::Attribute name, req, value, = object.deconstruct value = value.merge(&.select { |k, _| .include?(k) } || {}) if req [name, value.merge(required: true)] else [name, value] end when Nodes::Primitive object.value.merge(object..select { |k, _| .include?(k) }) else raise ArgumentError, "Got confusing node #{object} (#{object.class})" end end |
#type_for_parser ⇒ Object (private)
189 190 191 192 193 194 195 196 |
# File 'lib/sober_swag/compiler/type.rb', line 189 def type_for_parser if type.is_a?(Class) type.schema.type else # Probably a constrained array type end end |