Module: Literal::OpenAPI::Serializable::ClassMethods

Defined in:
lib/literal/openapi/serializable.rb

Constant Summary collapse

OPENAPI_PROP_KWARGS =

OpenAPI-specific kwargs that ‘prop` should accept. Extracted before forwarding to Literal::Properties#prop.

%i[enum format items_enum].freeze
OPENAPI_CONSTRAINT_KWARGS =

Schema constraints that map directly to OpenAPI keywords. Stripped from ‘prop` kwargs, stashed on the class, and merged into the property schema at build time.

%i[
  minimum maximum exclusive_minimum exclusive_maximum multiple_of
  min_length max_length pattern
  min_items max_items unique_items
  min_properties max_properties
  const examples
].freeze

Instance Method Summary collapse

Instance Method Details

#__literal_property_class__Object



52
53
54
# File 'lib/literal/openapi/serializable.rb', line 52

def __literal_property_class__
  Literal::OpenAPI::Property
end

#__openapi_constraints__Object

Per-class map of property-name → extra schema constraints (minimum, maximum, pattern, etc.). Populated by ‘prop` when constraint kwargs are passed, and merged into the property schema at build time.



73
74
75
# File 'lib/literal/openapi/serializable.rb', line 73

def __openapi_constraints__
  @__openapi_constraints__ ||= {}
end

#__openapi_optional__Object

Per-class set of property names declared with ‘optional: true`. These are omitted from the emitted `required` list regardless of runtime nilability or defaults.



66
67
68
# File 'lib/literal/openapi/serializable.rb', line 66

def __openapi_optional__
  @__openapi_optional__ ||= ::Set.new
end

#__openapi_required__Object

Per-class set of property names that should appear in ‘required`. Populated automatically by `prop` unless `optional: true` was passed —every declared prop is required by default.



59
60
61
# File 'lib/literal/openapi/serializable.rb', line 59

def __openapi_required__
  @__openapi_required__ ||= ::Set.new
end

#openapi_additional_properties(value) ⇒ Object

Opt out of ‘additionalProperties: false` for this class. NOT inherited — each class explicitly sets it.



86
87
88
# File 'lib/literal/openapi/serializable.rb', line 86

def openapi_additional_properties(value)
  @openapi_additional_properties = value
end

#openapi_additional_properties_valueObject



90
91
92
93
94
# File 'lib/literal/openapi/serializable.rb', line 90

def openapi_additional_properties_value
  return @openapi_additional_properties if instance_variable_defined?(:@openapi_additional_properties)

  false
end

#openapi_description(value = nil) ⇒ Object

Schema-level description for the class. Emitted at the top level of the generated object schema. Call with no args to read.



79
80
81
82
# File 'lib/literal/openapi/serializable.rb', line 79

def openapi_description(value = nil)
  @openapi_description = value unless value.nil?
  @openapi_description if instance_variable_defined?(:@openapi_description)
end

#openapi_schema(adapter: Literal::OpenAPI["3.0"]) ⇒ Object



96
97
98
99
# File 'lib/literal/openapi/serializable.rb', line 96

def openapi_schema(adapter: Literal::OpenAPI["3.0"])
  instance = adapter.is_a?(Class) ? adapter.new : adapter
  instance.build_schema(self)
end

#prop(name, type, kind = :keyword, **kwargs, &coercion) ⇒ Object

Override Literal::Properties#prop to accept OpenAPI-specific kwargs (enum, format, items_enum, optional, schema constraints). Strips those kwargs, forwards the rest to super (which creates a Literal::OpenAPI::Property via literal_property_class), then tags the created property with the OpenAPI extras and registers the name in the required/optional/constraints sets. rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/literal/openapi/serializable.rb', line 108

def prop(name, type, kind = :keyword, **kwargs, &coercion)
  optional = kwargs.delete(:optional)
  enum = kwargs.delete(:enum)
  format = kwargs.delete(:format)
  items_enum = kwargs.delete(:items_enum)
  constraints = OPENAPI_CONSTRAINT_KWARGS.each_with_object({}) do |k, h|
    h[k] = kwargs.delete(k) if kwargs.key?(k)
  end

  effective_type =
    if enum
      enum_values = enum.freeze
      predicate = Literal::Types::PredicateType.new(
        message: "enum(#{enum_values.inspect})",
        block: ->(v) { enum_values.include?(v) }
      )
      # When the declared type is nilable, apply the enum predicate only
      # to the non-nil inner type so that nil stays acceptable. Otherwise
      # `_Nilable(String) & enum(...)` would reject nil (predicate fails
      # because nil isn't in the enum), which contradicts the nilability.
      if type.is_a?(Literal::Types::NilableType)
        Literal::Types::NilableType.new(
          Literal::Types::IntersectionType.new([type.type, predicate])
        )
      else
        Literal::Types::IntersectionType.new([type, predicate])
      end
    else
      type
    end

  super(name, effective_type, kind, **kwargs, &coercion)

  property = literal_properties[name]
  property.instance_variable_set(:@enum, enum) if enum
  property.instance_variable_set(:@format, format) if format
  property.instance_variable_set(:@items_enum, items_enum) if items_enum

  if optional
    __openapi_optional__ << name
  else
    __openapi_required__ << name
  end
  __openapi_constraints__[name] = constraints if constraints.any?

  name
end