class I18n::Tasks::Scanners::RubyAstScanner

Scan for I18n.translate calls using whitequark/parser

Constants

MAGIC_COMMENT_PREFIX

Public Class Methods

new(**args) click to toggle source
Calls superclass method I18n::Tasks::Scanners::FileScanner::new
# File lib/i18n/tasks/scanners/ruby_ast_scanner.rb, line 19
def initialize(**args)
  super(**args)
  @parser = ::Parser::CurrentRuby.new
  @magic_comment_parser = ::Parser::CurrentRuby.new
  @matchers = setup_matchers
end

Protected Instance Methods

ast_to_occurences(ast) click to toggle source

Convert {Parser::AST::Node} to occurrences.

@param ast {Parser::Source::Comment} @return [nil, [key, Occurrence]] full absolute key name and the occurrence.

# File lib/i18n/tasks/scanners/ruby_ast_scanner.rb, line 103
def ast_to_occurences(ast)
  calls = RubyAstCallFinder.new.collect_calls(ast)
  results = []
  calls.each do |send_node, method_name|
    @matchers.each do |matcher|
      result = matcher.convert_to_key_occurrences(send_node, method_name)
      results << result if result
    end
  end

  results
end
comments_to_occurences(path, ast, comments) click to toggle source

Convert an array of {Parser::Source::Comment} to occurrences.

@param path Path to file @param ast Parser::AST::Node @param comments [Parser::Source::Comment] @return [nil, [key, Occurrence]] full absolute key name and the occurrence.

# File lib/i18n/tasks/scanners/ruby_ast_scanner.rb, line 70
def comments_to_occurences(path, ast, comments)
  magic_comments = comments.select { |comment| comment.text =~ MAGIC_COMMENT_PREFIX }
  comment_to_node = Parser::Source::Comment.associate_locations(ast, magic_comments).tap do |h|
    h.transform_values!(&:first)
  end.invert

  magic_comments.flat_map do |comment|
    @parser.reset
    associated_node = comment_to_node[comment]
    ast = @parser.parse(make_buffer(path, comment.text.sub(MAGIC_COMMENT_PREFIX, '').split(/\s+(?=t)/).join('; ')))
    calls = RubyAstCallFinder.new.collect_calls(ast)
    results = []

    # method_name is not available at this stage
    calls.each do |(send_node, _method_name)|
      @matchers.each do |matcher|
        result = matcher.convert_to_key_occurrences(
          send_node,
          nil,
          location: associated_node || comment.location
        )
        results << result if result
      end
    end

    results
  end
end
keys_relative_to_calling_method?(path) click to toggle source
# File lib/i18n/tasks/scanners/ruby_ast_scanner.rb, line 48
def keys_relative_to_calling_method?(path)
  /controllers|mailers/.match(path)
end
make_buffer(path, contents = read_file(path)) click to toggle source

Create an {Parser::Source::Buffer} with the given contents. The contents are assigned a {Parser::Source::Buffer#raw_source}.

@param path [String] Path to assign as the buffer name. @param contents [String] @return [Parser::Source::Buffer] file contents

# File lib/i18n/tasks/scanners/ruby_ast_scanner.rb, line 58
def make_buffer(path, contents = read_file(path))
  Parser::Source::Buffer.new(path).tap do |buffer|
    buffer.raw_source = contents
  end
end
path_to_ast_and_comments(path) click to toggle source

Parse file on path and returns AST and comments.

@param path Path to file to parse @return [{Parser::AST::Node}, [Parser::Source::Comment]]

# File lib/i18n/tasks/scanners/ruby_ast_scanner.rb, line 43
def path_to_ast_and_comments(path)
  @parser.reset
  @parser.parse_with_comments(make_buffer(path))
end
scan_file(path) click to toggle source

Extract all occurrences of translate calls from the file at the given path.

@return [Array<[key, Results::KeyOccurrence]>] each occurrence found in the file

# File lib/i18n/tasks/scanners/ruby_ast_scanner.rb, line 31
def scan_file(path)
  ast, comments = path_to_ast_and_comments(path)

  ast_to_occurences(ast) + comments_to_occurences(path, ast, comments)
rescue Exception => e # rubocop:disable Lint/RescueException
  raise ::I18n::Tasks::CommandError.new(e, "Error scanning #{path}: #{e.message}")
end
setup_matchers() click to toggle source
# File lib/i18n/tasks/scanners/ruby_ast_scanner.rb, line 116
def setup_matchers
  if config[:receiver_messages]
    config[:receiver_messages].map do |receiver, message|
      AstMatchers::MessageReceiversMatcher.new(
        receivers: [receiver],
        message: message,
        scanner: self
      )
    end
  else
    matchers = %i[t t! translate translate!].map do |message|
      AstMatchers::MessageReceiversMatcher.new(
        receivers: [
          AST::Node.new(:const, [nil, :I18n]),
          nil
        ],
        message: message,
        scanner: self
      )
    end

    Array(config[:ast_matchers]).each do |class_name|
      matchers << ActiveSupport::Inflector.constantize(class_name).new(scanner: self)
    end

    matchers
  end
end