class ERBLint::Linters::HardCodedString

Checks for hardcoded strings. Useful if you want to ensure a string can be translated using i18n.

Constants

ALLOWED_CORRECTORS
ForbiddenCorrector
MissingCorrector
MissingI18nLoadPath
NON_TEXT_TAGS
TEXT_NOT_ALLOWED

Public Instance Methods

autocorrect(processed_source, offense) click to toggle source
# File lib/erb_lint/linters/hard_coded_string.rb, line 82
def autocorrect(processed_source, offense)
  string = offense.source_range.source
  return unless (klass = load_corrector)
  return unless string.strip.length > 1
  node = ::RuboCop::AST::StrNode.new(:str, [string])
  corrector = klass.new(node, processed_source.filename, corrector_i18n_load_path, offense.source_range)
  corrector.autocorrect(tag_start: "<%= ", tag_end: " %>")
rescue MissingCorrector, MissingI18nLoadPath
  nil
end
find_range(node, str) click to toggle source
# File lib/erb_lint/linters/hard_coded_string.rb, line 73
def find_range(node, str)
  match = node.loc.source.match(Regexp.new(Regexp.quote(str.strip)))
  return unless match

  range_begin = match.begin(0) + node.loc.begin_pos
  range_end   = match.end(0) + node.loc.begin_pos
  (range_begin...range_end)
end
run(processed_source) click to toggle source
# File lib/erb_lint/linters/hard_coded_string.rb, line 50
def run(processed_source)
  hardcoded_strings = processed_source.ast.descendants(:text).each_with_object([]) do |text_node, to_check|
    next if non_text_tag?(processed_source, text_node)

    offended_strings = text_node.to_a.select { |node| relevant_node(node) }
    offended_strings.each do |offended_string|
      offended_string.split("\n").each do |str|
        to_check << [text_node, str] if check_string?(str)
      end
    end
  end

  hardcoded_strings.compact.each do |text_node, offended_str|
    range = find_range(text_node, offended_str)
    source_range = processed_source.to_source_range(range)

    add_offense(
      source_range,
      message(source_range.source)
    )
  end
end

Private Instance Methods

check_string?(str) click to toggle source
# File lib/erb_lint/linters/hard_coded_string.rb, line 95
def check_string?(str)
  string = str.gsub(/\s*/, "")
  string.length > 1 && !TEXT_NOT_ALLOWED.include?(string)
end
corrector_i18n_load_path() click to toggle source
# File lib/erb_lint/linters/hard_coded_string.rb, line 108
def corrector_i18n_load_path
  @config["corrector"].fetch("i18n_load_path") { raise MissingI18nLoadPath }
end
load_corrector() click to toggle source
# File lib/erb_lint/linters/hard_coded_string.rb, line 100
def load_corrector
  corrector_name = @config["corrector"].fetch("name") { raise MissingCorrector }
  raise ForbiddenCorrector unless ALLOWED_CORRECTORS.include?(corrector_name)
  require @config["corrector"].fetch("path") { raise MissingCorrector }

  corrector_name.safe_constantize
end
message(string) click to toggle source
# File lib/erb_lint/linters/hard_coded_string.rb, line 133
def message(string)
  stripped_string = string.strip

  "String not translated: #{stripped_string}"
end
non_text_tag?(processed_source, text_node) click to toggle source
# File lib/erb_lint/linters/hard_coded_string.rb, line 112
def non_text_tag?(processed_source, text_node)
  ast = processed_source.parser.ast.to_a
  index = ast.find_index(text_node)

  previous_node = ast[index - 1]

  if previous_node.type == :tag
    tag = BetterHtml::Tree::Tag.from_node(previous_node)

    NON_TEXT_TAGS.include?(tag.name) && !tag.closing?
  end
end
relevant_node(inner_node) click to toggle source
# File lib/erb_lint/linters/hard_coded_string.rb, line 125
def relevant_node(inner_node)
  if inner_node.is_a?(String)
    inner_node.strip.empty? ? false : inner_node
  else
    false
  end
end