Skip to content

Feature Request: JSON Schema to Ruby Generator (Reverse Engineering) #87

@sergiobayona

Description

@sergiobayona

Description

Create a CLI tool that generates Ruby EasyTalk model classes from existing JSON Schema files.

Current State

The library generates JSON Schema from Ruby code:

# Ruby → JSON Schema ✓
class User
  include EasyTalk::Model
  define_schema do
    property :name, String
  end
end

User.json_schema  # => { "type": "object", "properties": { "name": { "type": "string" } } }

Problem

Developers often start with an existing JSON Schema and need to create Ruby backing models:

  • Partner APIs: Received schema definitions from external partners
  • Standards compliance: Implementing industry standards (FHIR, OpenAPI, etc.)
  • Migration: Converting from other languages/frameworks that use JSON Schema
  • Contract-first development: Schema designed before implementation

Currently, developers must manually translate JSON Schema to Ruby, which is:

  • Time-consuming for large schemas
  • Error-prone (easy to miss constraints or types)
  • Tedious for deeply nested structures

Proposed Solution

CLI Tool

# Generate Ruby files from JSON Schema
easy_talk generate schema.json

# Specify output directory
easy_talk generate schema.json --output app/models/

# Generate from URL
easy_talk generate https://example.com/api/schema.json

# Dry run (preview without writing files)
easy_talk generate schema.json --dry-run

# Single file output (all classes in one file)
easy_talk generate schema.json --single-file

Input JSON Schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/user.schema.json",
  "title": "User",
  "type": "object",
  "properties": {
    "id": { "type": "integer" },
    "name": { "type": "string", "minLength": 1, "maxLength": 100 },
    "email": { "type": "string", "format": "email" },
    "age": { "type": "integer", "minimum": 0, "maximum": 150 },
    "role": { "type": "string", "enum": ["admin", "user", "guest"] },
    "address": {
      "type": "object",
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" }
      },
      "required": ["street", "city"]
    },
    "tags": {
      "type": "array",
      "items": { "type": "string" },
      "minItems": 1,
      "maxItems": 10
    }
  },
  "required": ["id", "name", "email"]
}

Generated Ruby Output

# app/models/address.rb
class Address
  include EasyTalk::Model

  define_schema do
    property :street, String
    property :city, String
  end
end

# app/models/user.rb
class User
  include EasyTalk::Model

  define_schema do
    title 'User'
    
    property :id, Integer
    property :name, String, min_length: 1, max_length: 100
    property :email, String, format: 'email'
    property :age, Integer, minimum: 0, maximum: 150, optional: true
    property :role, String, enum: %w[admin user guest], optional: true
    property :address, Address, optional: true
    property :tags, T::Array[String], min_items: 1, max_items: 10, optional: true
  end
end

Type Mapping

JSON Schema Type Ruby/EasyTalk Type
string String
integer Integer
number Float
boolean T::Boolean
null NilClass
array T::Array[ItemType]
object (inline) Generates new class
object (with $ref) References existing class
oneOf T::OneOf[A, B, ...]
anyOf T::AnyOf[A, B, ...]
allOf T::AllOf[A, B, ...]
Union with null T.nilable(Type)

Programmatic API

# Generate Ruby source code as string
source = EasyTalk::Generator.from_schema(json_schema_hash)

# Generate and eval (for dynamic use)
klass = EasyTalk::Generator.create_class(json_schema_hash)

# Generate from file
source = EasyTalk::Generator.from_file('schema.json')

Benefits

  • Rapid onboarding: Instantly create Ruby models from partner schemas
  • Accuracy: Automated translation eliminates human error
  • Productivity: Save hours of manual conversion work
  • Standards support: Easy adoption of standard schemas
  • Round-trip capable: Generate schema → modify → regenerate

Implementation Considerations

  1. Handle $ref and $defs for shared definitions
  2. Generate meaningful class names from $id or title
  3. Support nested objects (generate separate classes or inline)
  4. Map JSON Schema constraints to EasyTalk constraints
  5. Handle additionalProperties
  6. Support oneOf, anyOf, allOf compositions
  7. Preserve descriptions as comments
  8. Handle conflicting class names
  9. Support multiple schema files with cross-references
  10. Consider generating RBS type signatures alongside Ruby files

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: lowAdvanced features and enhancements

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions