require 'tempfile' require 'fileutils' module PowerStencil module Utils module FileEdit include Climatic::Proxy EDITOR_ENVIRONMENT_VARIABLE = 'EDITOR' LAST_EDITED_FILE = '/tmp/last-failed-edit.yaml' attr_writer :editor, :nb_max_edit_retries def editor default = config[:editor] || ENV[EDITOR_ENVIRONMENT_VARIABLE] @editor ||= default end def nb_max_edit_retries @nb_max_edit_retries ||= config[:max_file_edit_retry_times] end def securely_edit_entity(entity, &modifications_validation_block) initial_uri = entity.source_uri modified_entity = nil Tempfile.create([entity.type.to_s, entity.name.to_s]) do |tmpfile| tmpfile.puts entity.to_yaml tmpfile.flush securely_edit_file(tmpfile, &modifications_validation_block) modified_entity = UniverseCompiler::Entity::Persistence.load tmpfile modified_entity.source_uri = initial_uri end return modified_entity end def securely_edit_file(file, &modifications_validation_block) unless File.exists? file and File.writable? file raise PowerStencil::Error, "Cannot edit file '#{file}' !" end ext = File.extname file base = File.basename file, ext tmp_file_path = Tempfile.new([base, ext]).path retry_count = 1 begin FileUtils.copy file, tmp_file_path if retry_count == 1 edit_file tmp_file_path if block_given? unless modifications_validation_block.call tmp_file_path, file raise PowerStencil::Error, "Your modifications to '#{file}' are invalid !" end end FileUtils.copy tmp_file_path, file logger.info "File '#{file}' updated successfully." file rescue StandardError => e retry_count += 1 if retry_count > nb_max_edit_retries logger.error "Your modifications are kept in '#{LAST_EDITED_FILE}' as it was invalid !" begin FileUtils.copy tmp_file_path, LAST_EDITED_FILE rescue => ue logger.error 'Cannot keep your modifications !' logger.debug PowerStencil::Error.report_error(ue) end else retry end raise e ensure File.unlink tmp_file_path end end private def edit_file(file) raise PowerStencil::Error, 'No editor specified' if editor.nil? logger.debug "Editing file '#{file}', using editor '#{editor}'" system "#{editor} '#{file}'" end end end end