Skip to content

[OpenAPI] Support enums via application-level constants #372

Description

@rsamoilov

Description

Currently, Rage::OpenAPI's @request and @response tags support inline schemas, shared references, and automatic generation from ActiveRecord models or Alba/Blueprinter serializers. However, there is no way to reference application-level Ruby constants — such as enums, status lists, or category arrays — in the OpenAPI schema.

This means users who define constants in their Ruby code must duplicate those values manually in the OpenAPI specification:

# In the application
module OrderStatus
  PENDING = "pending"
  CONFIRMED = "confirmed"
  SHIPPED = "shipped"
  CANCELLED = "cancelled"

  ALL = [PENDING, CONFIRMED, SHIPPED, CANCELLED].freeze
end

# In the controller — values are duplicated
class Api::V1::OrdersController < ApplicationController
  # @response { id: Integer, status: String }
  def show
  end
end

The status field in the OpenAPI output has no enum constraint — consumers don't know which values are valid. And if the developer adds the values manually to a shared components file, they must remember to update it every time the Ruby constant changes.

The goal is to extend the @request and @response syntax so users can reference Ruby constants directly, and Rage::OpenAPI will resolve them to OpenAPI enum values:

class Api::V1::OrdersController < ApplicationController
  # @response { id: Integer, status: OrderStatus::ALL }
  def show
  end
end

This would generate:

status:
  type: string
  enum:
    - pending
    - confirmed
    - shipped
    - cancelled

Design considerations

  • Which constant types to support. Arrays and hashes of simple values (strings, integers) are the most common case. Consider whether to also support Set, ranges, or other enumerables in the initial implementation.
  • Constant resolution. Constants must be resolved at spec generation time, not at boot. Since Rage::OpenAPI analyzes controller source code using YARD-style tags, the implementation needs a strategy for resolving constant references from tag strings to actual Ruby values. Consider how namespacing and relative constant lookup should work.
  • Inline vs. named schemas. When a constant is referenced in multiple places, should it produce a named schema in components/schemas (DRY, but requires a naming convention) or inline the enum each time (simpler, but duplicates values in the spec)?
  • Syntax design. The syntax should feel natural within the existing @request/@response tag format. Consider how to differentiate a constant reference from a type name — OrderStatus::ALL is clearly a constant, but something like Status could be ambiguous (is it a type or a constant?).

Tips

  • Review the OpenAPI docs to understand the existing @request and @response tag syntax, shared references, and automatic schema generation.
  • Look at how Rage::OpenAPI currently resolves inline schemas and type references (e.g. Integer, String, Alba serializers) to understand where constant resolution would fit in.
  • Check the architecture doc to see how Rage's core components interact and to understand the design principles.
  • Read the contributing guide for coding conventions and design principles used across the codebase.
  • Before starting the implementation, please share your proposed design approach. The syntax and resolution strategy need careful consideration, so discussing the approach early will drastically increase the chances of acceptance and help avoid rework.
  • Feel free to ask any questions or request help in the comments below!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions