Class: BetterTranslate::YAMLHandler
- Inherits:
-
Object
- Object
- BetterTranslate::YAMLHandler
- Defined in:
- lib/better_translate/yaml_handler.rb
Overview
Handles YAML file operations
Provides methods for:
- Reading and parsing YAML files
- Writing YAML files with proper formatting
- Merging translations (incremental mode)
- Handling exclusions
- Flattening/unflattening nested structures
Instance Attribute Summary collapse
-
#config ⇒ Configuration
readonly
Configuration object.
Instance Method Summary collapse
-
#build_output_path(target_lang_code) ⇒ String
Build output file path for target language.
-
#create_backup_file(file_path) ⇒ void
private
private
Create backup file with rotation support.
-
#filter_exclusions(strings, target_lang_code) ⇒ Hash
Filter out excluded keys for a specific language.
-
#get_source_strings ⇒ Hash
Get translatable strings from source YAML.
-
#initialize(config) ⇒ YAMLHandler
constructor
Initialize YAML handler.
-
#merge_translations(file_path, new_translations) ⇒ Hash
Merge translated strings with existing file (incremental mode).
-
#read_yaml(file_path) ⇒ Hash
Read and parse YAML file.
-
#rotate_backups(file_path) ⇒ void
private
private
Rotate backup files, keeping only max_backups.
-
#write_yaml(file_path, data, diff_preview: nil) ⇒ Hash?
Write hash to YAML file.
Constructor Details
#initialize(config) ⇒ YAMLHandler
Initialize YAML handler
35 36 37 |
# File 'lib/better_translate/yaml_handler.rb', line 35 def initialize(config) @config = config end |
Instance Attribute Details
#config ⇒ Configuration (readonly)
Returns Configuration object.
25 26 27 |
# File 'lib/better_translate/yaml_handler.rb', line 25 def config @config end |
Instance Method Details
#build_output_path(target_lang_code) ⇒ String
Build output file path for target language
165 166 167 168 169 |
# File 'lib/better_translate/yaml_handler.rb', line 165 def build_output_path(target_lang_code) return "#{target_lang_code}.yml" unless config.output_folder File.join(config.output_folder, "#{target_lang_code}.yml") end |
#create_backup_file(file_path) ⇒ void (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Create backup file with rotation support
179 180 181 182 183 184 185 186 187 188 |
# File 'lib/better_translate/yaml_handler.rb', line 179 def create_backup_file(file_path) return unless File.exist?(file_path) # Rotate existing backups if max_backups > 1 rotate_backups(file_path) if config.max_backups > 1 # Create primary backup backup_path = "#{file_path}.bak" FileUtils.cp(file_path, backup_path) end |
#filter_exclusions(strings, target_lang_code) ⇒ Hash
Filter out excluded keys for a specific language
129 130 131 132 133 134 |
# File 'lib/better_translate/yaml_handler.rb', line 129 def filter_exclusions(strings, target_lang_code) excluded_keys = config.global_exclusions.dup excluded_keys += config.exclusions_per_language[target_lang_code] || [] strings.reject { |key, _| excluded_keys.include?(key) } end |
#get_source_strings ⇒ Hash
Get translatable strings from source YAML
Reads the input file and returns a flattened hash of strings. Removes the root language key if present.
110 111 112 113 114 115 116 117 118 |
# File 'lib/better_translate/yaml_handler.rb', line 110 def get_source_strings return {} unless config.input_file source_data = read_yaml(config.input_file) # Remove root language key if present (e.g., "en:") source_data = source_data[config.source_language] || source_data Utils::HashFlattener.flatten(source_data) end |
#merge_translations(file_path, new_translations) ⇒ Hash
Merge translated strings with existing file (incremental mode)
145 146 147 148 149 150 151 152 153 154 |
# File 'lib/better_translate/yaml_handler.rb', line 145 def merge_translations(file_path, new_translations) # @type var existing: Hash[String, untyped] existing = File.exist?(file_path) ? read_yaml(file_path) : {} existing_flat = Utils::HashFlattener.flatten(existing) # Merge: existing takes precedence merged = new_translations.merge(existing_flat) Utils::HashFlattener.unflatten(merged) end |
#read_yaml(file_path) ⇒ Hash
Read and parse YAML file
50 51 52 53 54 55 56 57 58 59 |
# File 'lib/better_translate/yaml_handler.rb', line 50 def read_yaml(file_path) Validator.validate_file_exists!(file_path) content = File.read(file_path) YAML.safe_load(content) || {} rescue Errno::ENOENT => e raise FileError.new("File not found: #{file_path}", context: { error: e. }) rescue Psych::SyntaxError => e raise YamlError.new("Invalid YAML syntax in #{file_path}", context: { error: e. }) end |
#rotate_backups(file_path) ⇒ void (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Rotate backup files, keeping only max_backups
Rotation strategy:
- .bak is always the most recent
- .bak.1, .bak.2, etc. are progressively older
- When we reach max_backups, oldest is deleted
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 |
# File 'lib/better_translate/yaml_handler.rb', line 201 def rotate_backups(file_path) primary_backup = "#{file_path}.bak" return unless File.exist?(primary_backup) # Clean up ANY backups that would exceed max_backups after rotation # max_backups includes .bak itself, so numbered backups go from 1 to max_backups-1 # After rotation, .bak -> .bak.1, so we can have at most .bak.1 through .bak.(max_backups-1) 10.downto(config.max_backups) do |i| numbered_backup = "#{file_path}.bak.#{i}" FileUtils.rm_f(numbered_backup) if File.exist?(numbered_backup) end # Rotate numbered backups from high to low to avoid overwrites # max_backups=2: nothing to rotate (only .bak -> .bak.1) # max_backups=3: .bak.1 -> .bak.2 (if exists) (config.max_backups - 2).downto(1) do |i| old_path = "#{file_path}.bak.#{i}" new_path = "#{file_path}.bak.#{i + 1}" FileUtils.mv(old_path, new_path) if File.exist?(old_path) end # Move primary backup to .bak.1 FileUtils.mv(primary_backup, "#{file_path}.bak.1") end |
#write_yaml(file_path, data, diff_preview: nil) ⇒ Hash?
Write hash to YAML file
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/better_translate/yaml_handler.rb', line 72 def write_yaml(file_path, data, diff_preview: nil) summary = nil # Show diff preview if in dry run mode if config.dry_run && diff_preview # @type var existing_data: Hash[String, untyped] existing_data = File.exist?(file_path) ? read_yaml(file_path) : {} summary = diff_preview.show_diff(existing_data, data, file_path) end return summary if config.dry_run # Create backup if enabled and file exists create_backup_file(file_path) if config.create_backup && File.exist?(file_path) # Ensure output directory exists FileUtils.mkdir_p(File.dirname(file_path)) File.write(file_path, YAML.dump(data)) nil rescue Errno::EACCES => e raise FileError.new("Permission denied: #{file_path}", context: { error: e. }) rescue StandardError => e raise FileError.new("Failed to write YAML: #{file_path}", context: { error: e. }) end |