Class: Synthra::REPL::EnhancedREPL
- Inherits:
-
Object
- Object
- Synthra::REPL::EnhancedREPL
- Defined in:
- lib/synthra/repl/enhanced_repl.rb
Overview
Enhanced REPL with history, completion, and export
Instance Attribute Summary collapse
-
#current_mode ⇒ Symbol
readonly
The current generation mode.
-
#current_seed ⇒ Integer?
readonly
The current seed for deterministic generation.
-
#history ⇒ Array<String>
readonly
Command history.
-
#registry ⇒ Registry
readonly
The schema registry.
Instance Method Summary collapse
-
#build_generation_opts ⇒ Hash
private
Build generation options hash.
-
#capture_output { ... } ⇒ String
private
Capture stdout output from a block.
-
#cmd_debug(name) ⇒ void
private
Debug generation.
-
#cmd_gen(name, count) ⇒ void
private
Generate records.
-
#cmd_help ⇒ void
private
Show help.
-
#cmd_info(name) ⇒ void
private
Show schema info.
-
#cmd_inspect(name) ⇒ void
private
Inspect generation.
-
#cmd_list ⇒ void
private
List loaded schemas.
-
#cmd_load(path) ⇒ void
private
Load schemas from path.
-
#cmd_mode(mode) ⇒ void
private
Set mode.
-
#cmd_quit ⇒ void
private
Quit REPL.
-
#cmd_seed(seed) ⇒ void
private
Set seed.
-
#cmd_table(name, count) ⇒ void
private
Generate table output.
-
#completions_for(input) ⇒ Array<String>
private
Get completions for the current input.
-
#execute_command(input) ⇒ void
private
Execute a single command.
-
#execute_with_export(command, filename) ⇒ void
private
Execute command with export to file.
-
#find_schema(name) ⇒ Schema?
private
Find schema by name with suggestions.
-
#initialize(registry: nil, verbose: false) ⇒ EnhancedREPL
constructor
Initialize the Enhanced REPL.
-
#print_welcome ⇒ void
private
Print welcome message.
-
#process_command(input) ⇒ void
Process a single command.
-
#read_input ⇒ String?
private
Read input from user.
-
#readline_available? ⇒ Boolean
Check if readline is available.
-
#run ⇒ Integer
Run the REPL.
-
#setup_completion ⇒ void
private
Set up tab completion.
-
#setup_readline ⇒ void
private
Set up readline if available.
-
#stop ⇒ void
Stop the REPL.
-
#suggest_command(unknown) ⇒ void
private
Suggest similar command for typos.
Constructor Details
#initialize(registry: nil, verbose: false) ⇒ EnhancedREPL
Initialize the Enhanced REPL
59 60 61 62 63 64 65 66 67 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 59 def initialize(registry: nil, verbose: false) @registry = registry || Registry.new @current_seed = nil @current_mode = :random @verbose = verbose @history = [] @running = false setup_readline end |
Instance Attribute Details
#current_mode ⇒ Symbol (readonly)
Returns the current generation mode.
49 50 51 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 49 def current_mode @current_mode end |
#current_seed ⇒ Integer? (readonly)
Returns the current seed for deterministic generation.
46 47 48 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 46 def current_seed @current_seed end |
#history ⇒ Array<String> (readonly)
Returns command history.
52 53 54 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 52 def history @history end |
#registry ⇒ Registry (readonly)
Returns the schema registry.
43 44 45 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 43 def registry @registry end |
Instance Method Details
#build_generation_opts ⇒ Hash (private)
Build generation options hash
600 601 602 603 604 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 600 def build_generation_opts opts = { registry: @registry, mode: @current_mode } opts[:seed] = @current_seed if @current_seed opts end |
#capture_output { ... } ⇒ String (private)
Capture stdout output from a block
254 255 256 257 258 259 260 261 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 254 def capture_output original_stdout = $stdout $stdout = StringIO.new yield $stdout.string ensure $stdout = original_stdout end |
#cmd_debug(name) ⇒ void (private)
This method returns an undefined value.
Debug generation
:nocov:
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 433 def cmd_debug(name) schema = find_schema(name) return unless schema puts "š Debug Mode: Generating #{schema.name}" puts "=" * 60 puts "Press Enter to continue after each field, 'q' to quit, 'c' to continue without pausing" puts field_index = 0 generated_fields = {} auto_continue = false original_callback = Synthra.configuration.on_field_generated Synthra.configuration.on_field_generated = lambda do |schema_name, field_name, value, duration| field_index += 1 generated_fields[field_name] = value puts "\n[Field #{field_index}] #{field_name}" puts "ā" * 60 puts "Value: #{Formatter.format_value(value, 80)}" puts "Duration: #{duration}ms" if duration puts "Schema: #{schema_name}" # Show context state - all previously generated fields if generated_fields.size > 1 puts "\nš Context (#{generated_fields.size - 1} prior fields):" generated_fields.each do |k, v| next if k == field_name puts " #{k.to_s.ljust(20)} = #{Formatter.format_value(v, 50)}" end end puts return if auto_continue loop do print "Press Enter to continue, 'q' to quit, 'c' to continue without pausing: " response = $stdin.gets&.strip&.downcase case response when "q", "quit" Synthra.configuration.on_field_generated = original_callback puts "Debug cancelled." throw :debug_cancelled when "c", "continue" auto_continue = true puts "Continuing without pausing..." break when "", nil break else puts "Invalid input. Press Enter, 'q', or 'c'." end end end begin catch(:debug_cancelled) do opts = build_generation_opts result = schema.generate(**opts) puts puts "=" * 60 puts "ā Generation Complete!" puts "=" * 60 puts puts "Final Record:" puts Formatter.table(result) end ensure Synthra.configuration.on_field_generated = original_callback end end |
#cmd_gen(name, count) ⇒ void (private)
This method returns an undefined value.
Generate records
391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 391 def cmd_gen(name, count) schema = find_schema(name) return unless schema opts = build_generation_opts if count == 1 record = schema.generate(**opts) puts JSON.pretty_generate(record) else records = schema.generate_many(count, **opts) puts JSON.pretty_generate(records) end end |
#cmd_help ⇒ void (private)
This method returns an undefined value.
Show help
568 569 570 571 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 568 def cmd_help puts "Commands: load, list (l), info, gen (g), table (t), debug (d), inspect (i), seed, mode, help (h), quit (q)" puts "Export: Use '> filename' to export output (e.g., 'gen User 100 > users.json')" end |
#cmd_info(name) ⇒ void (private)
This method returns an undefined value.
Show schema info
371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 371 def cmd_info(name) schema = find_schema(name) return unless schema puts "Schema: #{name}" puts " Version: #{schema.version || 'not specified'}" puts " Fields: #{schema.fields.length}" schema.fields.each do |f| optional = f.optional? ? "?" : "" nullable = f.nullable? ? " (nullable)" : "" puts " - #{f.name}#{optional}: #{f.type_name}#{nullable}" end end |
#cmd_inspect(name) ⇒ void (private)
This method returns an undefined value.
Inspect generation
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 515 def cmd_inspect(name) schema = find_schema(name) return unless schema opts = build_generation_opts record = schema.generate(**opts) puts "š Generated Record:" puts "=" * 60 puts Formatter.table(record) puts puts "š JSON Format:" puts JSON.pretty_generate(record) puts puts "š Statistics:" puts " Fields: #{record.keys.length}" puts " Total size: #{JSON.generate(record).bytesize} bytes" record.each do |key, value| type = value.class.name size = case value when String then "#{value.bytesize} bytes" when Hash then "#{value.keys.length} keys" when Array then "#{value.length} items" else value.inspect.length.to_s + " chars" end puts " #{key}: #{type} (#{size})" end end |
#cmd_list ⇒ void (private)
This method returns an undefined value.
List loaded schemas
353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 353 def cmd_list if @registry.size == 0 puts "No schemas loaded. Use 'load <path>' first." else puts "Loaded schemas:" @registry.schemas.each do |name, schema| version = schema.version ? " v#{schema.version}" : "" deprecated = schema.deprecated? ? " [DEPRECATED]" : "" puts " - #{name}#{version}#{deprecated}" end end end |
#cmd_load(path) ⇒ void (private)
This method returns an undefined value.
Load schemas from path
336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 336 def cmd_load(path) if File.directory?(path) Dir.glob(File.join(path, "*.dsl")).each do |f| @registry.load_file(f, validate: false) end else @registry.load_file(path, validate: false) end puts "ā Loaded #{@registry.size} schema(s)" rescue => e puts "ā Error: #{e.}" end |
#cmd_mode(mode) ⇒ void (private)
This method returns an undefined value.
Set mode
559 560 561 562 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 559 def cmd_mode(mode) @current_mode = mode puts "ā Mode set to #{@current_mode}" end |
#cmd_quit ⇒ void (private)
This method returns an undefined value.
Quit REPL
577 578 579 580 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 577 def cmd_quit puts "Goodbye!" stop end |
#cmd_seed(seed) ⇒ void (private)
This method returns an undefined value.
Set seed
549 550 551 552 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 549 def cmd_seed(seed) @current_seed = seed puts "ā Seed set to #{@current_seed}" end |
#cmd_table(name, count) ⇒ void (private)
This method returns an undefined value.
Generate table output
412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 412 def cmd_table(name, count) schema = find_schema(name) return unless schema opts = build_generation_opts if count == 1 record = schema.generate(**opts) puts Formatter.table(record) else records = schema.generate_many(count, **opts) puts Formatter.table(records) end end |
#completions_for(input) ⇒ Array<String> (private)
Get completions for the current input
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 161 def completions_for(input) # Get the line buffer to understand context line = Readline.line_buffer rescue input if line.nil? || line.strip.empty? || !line.include?(" ") # Complete commands all_commands = COMMANDS + COMMAND_ALIASES.keys all_commands.select { |c| c.start_with?(input.downcase) } else # Complete schema names for commands that take them command = line.split.first&.downcase if %w[gen table debug inspect info g t d i].include?(command) @registry.names.select { |name| name.downcase.start_with?(input.downcase) } else [] end end end |
#execute_command(input) ⇒ void (private)
This method returns an undefined value.
Execute a single command
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 268 def execute_command(input) return if input.nil? || input.strip.empty? # Expand aliases parts = input.split(/\s+/, 2) command = parts[0]&.downcase rest = parts[1] return if command.nil? || command.empty? # Check for alias if COMMAND_ALIASES.key?(command) command = COMMAND_ALIASES[command] end # Reconstruct full input full_input = rest ? "#{command} #{rest}" : command case full_input when /^load\s+(.+)$/ cmd_load($1.strip) when "list", "l" cmd_list when /^info\s+(\w+)$/ cmd_info($1) when /^gen\s+(\w+)(?:\s+(\d+))?$/ cmd_gen($1, ($2 || "1").to_i) when /^table\s+(\w+)(?:\s+(\d+))?$/ cmd_table($1, ($2 || "1").to_i) when /^debug\s+(\w+)$/ cmd_debug($1) when /^inspect\s+(\w+)$/ cmd_inspect($1) when /^seed\s+(\d+)$/ cmd_seed($1.to_i) when /^mode\s+(random|edge|invalid|hostile|mixed)$/ cmd_mode($1.to_sym) when "help", "h" cmd_help when "quit", "exit", "q" cmd_quit else suggest_command(command) end end |
#execute_with_export(command, filename) ⇒ void (private)
This method returns an undefined value.
Execute command with export to file
237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 237 def execute_with_export(command, filename) # Capture output output = capture_output { execute_command(command) } if output && !output.strip.empty? File.write(filename, output) puts "ā Exported to #{filename} (#{output.bytesize} bytes)" else puts "ā No output to export" end end |
#find_schema(name) ⇒ Schema? (private)
Find schema by name with suggestions
587 588 589 590 591 592 593 594 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 587 def find_schema(name) @registry.schema(name) rescue KeyError suggestions = Utils::StringDistance.similar_strings(name, @registry.names, max_distance: 3) puts "ā Schema '#{name}' not found" puts Utils::StringDistance.format_suggestions(suggestions) if suggestions.any? nil end |
#print_welcome ⇒ void (private)
This method returns an undefined value.
Print welcome message
:nocov:
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 200 def print_welcome puts "=" * 60 puts "Synthra Interactive REPL (Enhanced)" puts "=" * 60 puts puts "Commands (aliases in parentheses):" puts " load <path> - Load schemas from file/directory" puts " list (l) - List loaded schemas" puts " info <Schema> - Show schema details" puts " gen <Schema> [N] (g) - Generate records (JSON)" puts " table <Schema> [N] (t)- Generate records (table view)" puts " debug <Schema> (d) - Step-through debug generation" puts " inspect <Schema> (i) - Generate and show detailed inspection" puts " seed <number> - Set seed for deterministic output" puts " mode <mode> - Set mode (random/edge/invalid/hostile/mixed)" puts " help (h) - Show this help" puts " quit/exit (q) - Exit REPL" puts puts "Export to file:" puts " gen User 100 > users.json - Export to JSON file" puts " table User 10 > users.txt - Export table to text file" puts if @readline_available puts "Tab completion and command history enabled!" else puts "Install 'readline' gem for history and tab completion" end puts end |
#process_command(input) ⇒ void
This method returns an undefined value.
Process a single command
106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 106 def process_command(input) # Check for export redirect syntax: command > filename if input.include?(" > ") command_part, filename = input.split(" > ", 2) execute_with_export(command_part.strip, filename.strip) else execute_command(input) end rescue => e puts "ā Error: #{e.}" puts e.backtrace.first(3).join("\n") if @verbose end |
#read_input ⇒ String? (private)
Read input from user
:nocov:
185 186 187 188 189 190 191 192 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 185 def read_input if @readline_available Readline.readline("synthra> ", true) else print "synthra> " $stdin.gets end end |
#readline_available? ⇒ Boolean
Check if readline is available
73 74 75 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 73 def readline_available? @readline_available end |
#run ⇒ Integer
Run the REPL
:nocov:
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 82 def run @running = true print_welcome while @running input = read_input break if input.nil? input = input.strip next if input.empty? @history << input process_command(input) end CLI::EXIT_SUCCESS end |
#setup_completion ⇒ void (private)
This method returns an undefined value.
Set up tab completion
147 148 149 150 151 152 153 154 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 147 def setup_completion return unless @readline_available Readline.completion_proc = proc do |input| completions_for(input) end Readline.completion_append_character = " " end |
#setup_readline ⇒ void (private)
This method returns an undefined value.
Set up readline if available
133 134 135 136 137 138 139 140 141 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 133 def setup_readline begin require "readline" @readline_available = true setup_completion rescue LoadError @readline_available = false end end |
#stop ⇒ void
This method returns an undefined value.
Stop the REPL
123 124 125 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 123 def stop @running = false end |
#suggest_command(unknown) ⇒ void (private)
This method returns an undefined value.
Suggest similar command for typos
319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/synthra/repl/enhanced_repl.rb', line 319 def suggest_command(unknown) all_commands = COMMANDS + COMMAND_ALIASES.keys suggestions = Utils::StringDistance.similar_strings(unknown, all_commands, max_distance: 2) if suggestions.any? puts "Unknown command: #{unknown}" puts Utils::StringDistance.format_suggestions(suggestions) else puts "Unknown command: #{unknown}. Type 'help' for available commands." end end |