Class: Pvectl::EditorSession
- Inherits:
-
Object
- Object
- Pvectl::EditorSession
- Defined in:
- lib/pvectl/editor_session.rb,
sig/pvectl/editor_session.rbs
Overview
Manages the editor lifecycle for interactive config editing.
Creates a temporary file with content, opens it in an editor, reads the result, and supports a retry loop with error injection when validation fails.
Defined Under Namespace
Modules: _Callable
Constant Summary collapse
- ERROR_SEPARATOR =
"# -----------------------------------------------"
Instance Method Summary collapse
-
#build_error_block(errors) ⇒ String
Builds an error block string from error messages.
-
#cancelled?(content, original_content) ⇒ Boolean
Detects whether the user cancelled editing.
-
#default_editor ⇒ String?
Returns the default editor fallback.
-
#edit(original_content) ⇒ String?
Opens content in an editor and returns the edited result.
-
#edit_loop(path, original_content) ⇒ String?
Runs the edit-validate-retry loop.
-
#initialize(editor: nil, validator: nil) ⇒ EditorSession
constructor
Creates a new EditorSession.
-
#inject_errors(path, content, errors) ⇒ void
Injects error comments at the top of the file for the retry loop.
-
#strip_error_block(content) ⇒ String
Strips a previously injected error block from content.
-
#system_editor(path) ⇒ void
Opens a file in the system editor.
Constructor Details
#initialize(editor: nil, validator: nil) ⇒ EditorSession
Creates a new EditorSession.
35 36 37 38 |
# File 'lib/pvectl/editor_session.rb', line 35 def initialize(editor: nil, validator: nil) @editor = editor || method(:system_editor) @validator = validator end |
Instance Method Details
#build_error_block(errors) ⇒ String
Builds an error block string from error messages.
127 128 129 130 131 132 133 |
# File 'lib/pvectl/editor_session.rb', line 127 def build_error_block(errors) lines = [ERROR_SEPARATOR] errors.each { |error| lines << "# ERROR: #{error}" } lines << "# Please fix the error above or save empty file to cancel." lines << ERROR_SEPARATOR "#{lines.join("\n")}\n" end |
#cancelled?(content, original_content) ⇒ Boolean
Detects whether the user cancelled editing.
87 88 89 |
# File 'lib/pvectl/editor_session.rb', line 87 def cancelled?(content, original_content) content.empty? || content == original_content end |
#default_editor ⇒ String?
Returns the default editor fallback. Tries vi first, then nano. Returns nil if neither is found.
150 151 152 153 154 155 |
# File 'lib/pvectl/editor_session.rb', line 150 def default_editor %w[vi nano].each do |cmd| return cmd if system("which", cmd, out: File::NULL, err: File::NULL) end nil end |
#edit(original_content) ⇒ String?
Opens content in an editor and returns the edited result.
Creates a temp file, invokes the editor, and reads back the content. Supports validation with retry loop and error injection.
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/pvectl/editor_session.rb', line 48 def edit(original_content) tempfile = Tempfile.new(["pvectl-edit-", ".yaml"]) tempfile.write(original_content) tempfile.flush path = tempfile.path edit_loop(path, original_content) ensure tempfile&.close tempfile&.unlink end |
#edit_loop(path, original_content) ⇒ String?
Runs the edit-validate-retry loop.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/pvectl/editor_session.rb', line 67 def edit_loop(path, original_content) loop do @editor.call(path) content = File.read(path) return nil if cancelled?(content, original_content) return content unless @validator errors = @validator.call(content) return content if errors.empty? inject_errors(path, content, errors) end end |
#inject_errors(path, content, errors) ⇒ void
This method returns an undefined value.
Injects error comments at the top of the file for the retry loop. Strips any previous error block before injecting new ones.
97 98 99 100 101 |
# File 'lib/pvectl/editor_session.rb', line 97 def inject_errors(path, content, errors) clean_content = strip_error_block(content) error_block = build_error_block(errors) File.write(path, "#{error_block}#{clean_content}") end |
#strip_error_block(content) ⇒ String
Strips a previously injected error block from content.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/pvectl/editor_session.rb', line 107 def strip_error_block(content) lines = content.lines return content unless lines.first&.strip == ERROR_SEPARATOR # Find the end of the error block (second separator line) separator_count = 0 end_index = lines.index do |line| separator_count += 1 if line.strip == ERROR_SEPARATOR separator_count >= 2 end return content unless end_index lines[(end_index + 1)..].join end |
#system_editor(path) ⇒ void
This method returns an undefined value.
Opens a file in the system editor.
139 140 141 142 143 144 |
# File 'lib/pvectl/editor_session.rb', line 139 def system_editor(path) editor = ENV["EDITOR"] || ENV["VISUAL"] || default_editor raise "No editor found. Set $EDITOR or $VISUAL environment variable." unless editor system(editor, path) end |