Class: RailsI18nOnair::FileManager

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_i18n_onair/file_manager.rb

Constant Summary collapse

LOCALE_FILE_PATTERN =

Basename must end in .yml, optionally with a dotted prefix:

en.yml, pt-BR.yml, devise.en.yml, simple_form.pt-BR.yml

The captured group is the language code.

/\A(?:[\w-]+(?:\.[\w-]+)*\.)?([a-z]{2,3}(?:-[A-Z]{2})?)\.yml\z/.freeze
SAFE_SEGMENT_PATTERN =

Path segments for nested files (config/locales/models/en.yml)

/\A[\w.-]+\z/.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeFileManager

Returns a new instance of FileManager.



13
14
15
# File 'lib/rails_i18n_onair/file_manager.rb', line 13

def initialize
  @errors = []
end

Instance Attribute Details

#errorsObject (readonly)

Returns the value of attribute errors.



11
12
13
# File 'lib/rails_i18n_onair/file_manager.rb', line 11

def errors
  @errors
end

Instance Method Details

#file_exists?(filename) ⇒ Boolean

Returns:

  • (Boolean)


101
102
103
104
# File 'lib/rails_i18n_onair/file_manager.rb', line 101

def file_exists?(filename)
  file_path = resolve_path(filename)
  !file_path.nil? && File.exist?(file_path)
end

#files_for_language(language) ⇒ Object

All files that can hold translations for the given language, including region variants ("en" also matches en-US.yml).



44
45
46
47
48
49
# File 'lib/rails_i18n_onair/file_manager.rb', line 44

def files_for_language(language)
  lang = language.to_s
  list_files.select do |file|
    file[:language] == lang || file[:language].split("-").first == lang
  end
end

#get_file_info(filename) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/rails_i18n_onair/file_manager.rb', line 106

def get_file_info(filename)
  file_path = resolve_path(filename)
  return nil unless file_path && File.exist?(file_path)

  {
    filename: filename,
    language: extract_language_code(File.basename(filename)),
    path: file_path,
    size: File.size(file_path),
    modified_at: File.mtime(file_path),
    content: File.read(file_path)
  }
end

#list_filesObject

Lists locale files recursively. :filename is the path relative to the locale directory ("en.yml", "devise.en.yml", "models/en.yml") and is the identifier accepted by read_file / write_file. :editable marks files the dashboard can edit in place (top-level only — nested paths don't fit the dashboard routes).



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/rails_i18n_onair/file_manager.rb', line 22

def list_files
  locale_path = full_locale_path
  return [] unless File.directory?(locale_path)

  Dir.glob("#{locale_path}/**/*.yml").map do |file_path|
    relative = relative_path(file_path)
    basename = File.basename(file_path)
    next unless basename.match?(LOCALE_FILE_PATTERN)

    {
      filename: relative,
      language: extract_language_code(basename),
      editable: !relative.include?("/"),
      path: file_path,
      size: File.size(file_path),
      modified_at: File.mtime(file_path)
    }
  end.compact.sort_by { |f| f[:filename] }
end

#read_file(filename) ⇒ Object



51
52
53
54
55
56
# File 'lib/rails_i18n_onair/file_manager.rb', line 51

def read_file(filename)
  file_path = resolve_path(filename)
  return nil unless file_path && File.exist?(file_path)

  File.read(file_path)
end

#validate_yaml(content) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/rails_i18n_onair/file_manager.rb', line 83

def validate_yaml(content)
  errors = []

  begin
    parsed = YAML.safe_load(content, permitted_classes: [Symbol], aliases: true)

    unless parsed.is_a?(Hash)
      errors << "YAML content must be a hash/object"
    end
  rescue Psych::SyntaxError => e
    errors << "Invalid YAML syntax: #{e.message}"
  rescue StandardError => e
    errors << "Error parsing YAML: #{e.message}"
  end

  errors
end

#write_file(filename, content) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/rails_i18n_onair/file_manager.rb', line 58

def write_file(filename, content)
  file_path = resolve_path(filename)
  return false unless file_path

  # Validate YAML syntax before writing
  errors = validate_yaml(content)
  if errors.any?
    @errors = errors
    return false
  end

  # Atomic write: write to temp file, then move
  temp_path = "#{file_path}.tmp"

  begin
    File.write(temp_path, content)
    File.rename(temp_path, file_path)
    true
  rescue StandardError => e
    @errors << "Failed to write file: #{e.message}"
    File.delete(temp_path) if File.exist?(temp_path)
    false
  end
end