Class: Rhales::SchemaGenerator
- Inherits:
-
Object
- Object
- Rhales::SchemaGenerator
- Defined in:
- lib/rhales/utils/schema_generator.rb
Overview
Generates JSON Schemas from Zod schemas using TypeScript execution
This class uses pnpm exec tsx to execute Zod schema code and convert it to JSON Schema format. The generated schemas are saved to disk for use by the validation middleware.
Usage: generator = SchemaGenerator.new( templates_dir: ‘./templates’, output_dir: ‘./public/schemas’ ) results = generator.generate_all
Defined Under Namespace
Classes: GenerationError
Instance Attribute Summary collapse
-
#output_dir ⇒ Object
readonly
Returns the value of attribute output_dir.
-
#templates_dir ⇒ Object
readonly
Returns the value of attribute templates_dir.
Instance Method Summary collapse
-
#build_typescript_script(schema_info) ⇒ Object
private
-
#ensure_output_directory! ⇒ Object
private
-
#generate_all ⇒ Hash
Generate JSON Schemas for all templates with
sections. -
#generate_schema(schema_info) ⇒ Hash
Generate JSON Schema for a single template.
-
#initialize(templates_dir:, output_dir: nil) ⇒ SchemaGenerator
constructor
A new instance of SchemaGenerator.
-
#save_schema(template_name, json_schema) ⇒ Object
private
-
#validate_setup! ⇒ Object
private
Constructor Details
#initialize(templates_dir:, output_dir: nil) ⇒ SchemaGenerator
Returns a new instance of SchemaGenerator.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/rhales/utils/schema_generator.rb', line 30 def initialize(templates_dir:, output_dir: nil) @templates_dir = File.(templates_dir) # Smart default: place schemas in public/schemas relative to current working directory # This ensures schemas are generated in the implementing project, not the gem directory @output_dir = if output_dir File.(output_dir) else # Default to public/schemas in current working directory File.('./public/schemas') end validate_setup! ensure_output_directory! end |
Instance Attribute Details
#output_dir ⇒ Object (readonly)
Returns the value of attribute output_dir.
25 26 27 |
# File 'lib/rhales/utils/schema_generator.rb', line 25 def output_dir @output_dir end |
#templates_dir ⇒ Object (readonly)
Returns the value of attribute templates_dir.
25 26 27 |
# File 'lib/rhales/utils/schema_generator.rb', line 25 def templates_dir @templates_dir end |
Instance Method Details
#build_typescript_script(schema_info) ⇒ Object (private)
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 155 156 157 158 159 |
# File 'lib/rhales/utils/schema_generator.rb', line 123 def build_typescript_script(schema_info) # Escape single quotes in template name for TypeScript string safe_name = schema_info[:template_name].gsub("'", "\\'") <<~TYPESCRIPT // Auto-generated schema generator for #{safe_name} import { z } from 'zod/v4'; // Schema code from .rue template #{schema_info[:schema_code].strip} // Generate JSON Schema try { const jsonSchema = z.toJSONSchema(schema, { target: 'draft-2020-12', unrepresentable: 'any', cycles: 'ref', reused: 'inline', }); // Add metadata const schemaWithMeta = { $schema: 'https://json-schema.org/draft/2020-12/schema', $id: `https://rhales.dev/schemas/#{safe_name}.json`, title: '#{safe_name}', description: 'Schema for #{safe_name} template', ...jsonSchema, }; // Output JSON to stdout console.log(JSON.stringify(schemaWithMeta, null, 2)); } catch (error) { console.error('Schema generation error:', error.message); process.exit(1); } TYPESCRIPT end |
#ensure_output_directory! ⇒ Object (private)
188 189 190 |
# File 'lib/rhales/utils/schema_generator.rb', line 188 def ensure_output_directory! FileUtils.mkdir_p(@output_dir) unless File.directory?(@output_dir) end |
#generate_all ⇒ Hash
Generate JSON Schemas for all templates with
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/rhales/utils/schema_generator.rb', line 49 def generate_all extractor = SchemaExtractor.new(@templates_dir) schemas = extractor.extract_all if schemas.empty? return { success: true, generated: 0, failed: 0, message: 'No schemas found in templates' } end results = { success: true, generated: 0, failed: 0, errors: [] } schemas.each do |schema_info| begin generate_schema(schema_info) results[:generated] += 1 puts "✓ Generated schema for: #{schema_info[:template_name]}" rescue => e results[:failed] += 1 results[:success] = false error_msg = "Failed to generate schema for #{schema_info[:template_name]}: #{e.}" results[:errors] << error_msg warn error_msg end end results end |
#generate_schema(schema_info) ⇒ Hash
Generate JSON Schema for a single template
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/rhales/utils/schema_generator.rb', line 90 def generate_schema(schema_info) # Create temp file in project directory so Node.js can resolve modules temp_dir = File.join(Dir.pwd, 'tmp') FileUtils.mkdir_p(temp_dir) unless Dir.exist?(temp_dir) temp_file = Tempfile.new(['schema', '.mts'], temp_dir) begin # Write TypeScript script temp_file.write(build_typescript_script(schema_info)) temp_file.close # Execute with tsx via pnpm stdout, stderr, status = Open3.capture3('pnpm', 'exec', 'tsx', temp_file.path) unless status.success? raise GenerationError, "TypeScript execution failed: #{stderr}" end # Parse JSON Schema from stdout json_schema = JSONSerializer.parse(stdout) # Save to disk save_schema(schema_info[:template_name], json_schema) json_schema ensure temp_file.unlink if temp_file end end |
#save_schema(template_name, json_schema) ⇒ Object (private)
161 162 163 164 165 166 167 168 |
# File 'lib/rhales/utils/schema_generator.rb', line 161 def save_schema(template_name, json_schema) # Create subdirectories if template name contains paths schema_file = File.join(@output_dir, "#{template_name}.json") schema_dir = File.dirname(schema_file) FileUtils.mkdir_p(schema_dir) unless File.directory?(schema_dir) File.write(schema_file, JSONSerializer.dump(json_schema)) end |
#validate_setup! ⇒ Object (private)
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/rhales/utils/schema_generator.rb', line 170 def validate_setup! unless File.directory?(@templates_dir) raise GenerationError, "Templates directory does not exist: #{@templates_dir}" end # Check pnpm is available stdout, stderr, status = Open3.capture3('pnpm', '--version') unless status.success? raise GenerationError, "pnpm not found. Install pnpm to generate schemas: npm install -g pnpm" end # Check tsx is available (will be installed by pnpm if needed) stdout, stderr, status = Open3.capture3('pnpm', 'exec', 'tsx', '--version') unless status.success? raise GenerationError, "tsx not found. Run: pnpm install tsx --save-dev" end end |