Module: I18n::Tasks::UsedKeys

Included in:
BaseTask
Defined in:
lib/i18n/tasks/used_keys.rb

Overview

rubocop:disable Metrics/ModuleLength

Constant Summary collapse

SEARCH_DEFAULTS =
{
  paths: %w[app/].freeze,
  relative_exclude_method_name_paths: [],
  relative_roots: %w[app/controllers app/helpers app/mailers app/presenters app/views].freeze,
  scanners: [
    ['::I18n::Tasks::Scanners::RubyAstScanner', { only: %w[*.rb] }],
    ['::I18n::Tasks::Scanners::ErbAstScanner', { only: %w[*.erb] }],
    ['::I18n::Tasks::Scanners::PatternWithScopeScanner', { exclude: %w[*.erb *.rb] }]
  ],
  ast_matchers: [],
  strict: true
}.freeze
ALWAYS_EXCLUDE =
%w[*.jpg *.jpeg *.png *.gif *.svg *.ico *.eot *.otf *.ttf *.woff *.woff2 *.pdf *.css *.sass *.scss
*.less *.yml *.json *.zip *.tar.gz *.swf *.flv *.mp3 *.wav *.flac *.webm *.mp4 *.ogg *.opus
*.webp *.map *.xlsx].freeze

Instance Method Summary collapse

Instance Method Details

#caching_file_finder_providerObject



122
123
124
# File 'lib/i18n/tasks/used_keys.rb', line 122

def caching_file_finder_provider
  @caching_file_finder_provider ||= Scanners::Files::CachingFileFinderProvider.new(exclude: ALWAYS_EXCLUDE)
end

#caching_file_readerObject



126
127
128
# File 'lib/i18n/tasks/used_keys.rb', line 126

def caching_file_reader
  @caching_file_reader ||= Scanners::Files::CachingFileReader.new
end

#merge_scanner_configs(a, b) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
# File 'lib/i18n/tasks/used_keys.rb', line 110

def merge_scanner_configs(a, b)
  a.deep_merge(b).tap do |c|
    %i[scanners paths relative_exclude_method_name_paths relative_roots].each do |key|
      c[key] = a[key] if b[key].blank?
    end
    %i[exclude].each do |key|
      merged = Array(a[key]) + Array(b[key])
      c[key] = merged unless merged.empty?
    end
  end
end

#scanner(strict: nil) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/i18n/tasks/used_keys.rb', line 69

def scanner(strict: nil)
  (@scanner ||= {})[strict?(strict)] ||= begin
    shared_options = search_config.dup
    shared_options.delete(:scanners)
    shared_options[:strict] = strict unless strict.nil?
    log_verbose 'Scanners: '
    Scanners::ScannerMultiplexer.new(
      scanners: search_config[:scanners].map do |(class_name, args)|
        if args && args[:strict]
          fail CommandError, 'the strict option is global and cannot be applied on the scanner level'
        end

        ActiveSupport::Inflector.constantize(class_name).new(
          config: merge_scanner_configs(shared_options, args || {}),
          file_finder_provider: caching_file_finder_provider,
          file_reader: caching_file_reader
        )
      end.tap { |scanners| log_verbose { scanners.map { |s| "  #{s.class.name} #{s.config.inspect}" } * "\n" } }
    )
  end
end

#search_configObject



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/i18n/tasks/used_keys.rb', line 91

def search_config
  @search_config ||= begin
    conf = (config[:search] || {}).deep_symbolize_keys
    if conf[:scanner]
      warn_deprecated 'search.scanner is now search.scanners, an array of [ScannerClass, options]'
      conf[:scanners] = [[conf.delete(:scanner)]]
    end
    if conf[:ignore_lines]
      warn_deprecated 'search.ignore_lines is no longer a global setting: pass it directly to the pattern scanner.'
      conf.delete(:ignore_lines)
    end
    if conf[:include]
      warn_deprecated 'search.include is now search.only'
      conf[:only] = conf.delete(:include)
    end
    merge_scanner_configs(SEARCH_DEFAULTS, conf).freeze
  end
end

#used_in_expr?(key) ⇒ Boolean

Returns whether the key is potentially used in a code expression such as ‘t(“category.#category_key”)`.

Returns:

  • (Boolean)

    whether the key is potentially used in a code expression such as ‘t(“category.#category_key”)`



131
132
133
# File 'lib/i18n/tasks/used_keys.rb', line 131

def used_in_expr?(key)
  !!(key =~ expr_key_re)
end

#used_in_source_tree(key_filter: nil, strict: nil) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/i18n/tasks/used_keys.rb', line 55

def used_in_source_tree(key_filter: nil, strict: nil)
  keys = ((@keys_used_in_source_tree ||= {})[strict?(strict)] ||=
            scanner(strict: strict).keys.freeze)
  if key_filter
    key_filter_re = compile_key_pattern(key_filter)
    keys          = keys.select { |k| k.key =~ key_filter_re }
  end
  Data::Tree::Node.new(
    key: 'used',
    data: { key_filter: key_filter },
    children: Data::Tree::Siblings.from_key_occurrences(keys)
  ).to_siblings
end

#used_tree(key_filter: nil, strict: nil, include_raw_references: false) ⇒ Data::Tree::Siblings

Find all keys in the source and return a forest with the keys in absolute form and their occurrences.

Parameters:

  • key_filter (String) (defaults to: nil)

    only return keys matching this pattern.

  • strict (Boolean) (defaults to: nil)

    if true, dynamic keys are excluded (e.g. ‘t(“category.#{ category.key }”)`)

  • include_raw_references (Boolean) (defaults to: false)

    if true, includes reference usages as they appear in the source

Returns:



41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/i18n/tasks/used_keys.rb', line 41

def used_tree(key_filter: nil, strict: nil, include_raw_references: false)
  src_tree = used_in_source_tree(key_filter: key_filter, strict: strict)
  raw_refs, resolved_refs, used_refs = process_references(src_tree['used'].children)
  raw_refs.leaves { |node| node.data[:ref_type] = :reference_usage }
  resolved_refs.leaves { |node| node.data[:ref_type] = :reference_usage_resolved }
  used_refs.leaves { |node| node.data[:ref_type] = :reference_usage_key }
  src_tree.tap do |result|
    tree = result['used'].children
    tree.subtract_by_key!(raw_refs)
    tree.merge!(raw_refs) if include_raw_references
    tree.merge!(used_refs).merge!(resolved_refs)
  end
end