From 04de45432bdf46864b0fac360765a07f4e83a32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Sun, 26 Aug 2018 14:13:06 -0300 Subject: [PATCH 1/6] Test against the latest ruby --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 124c084d..d06d9218 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ sudo: false dist: trusty language: ruby rvm: -- 2.2.3 +- 2.5.1 - jruby - rbx-3 cache: bundler From c99e7a26d148dcdababf5a078c1c435856791f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 13 Sep 2018 12:21:41 -0300 Subject: [PATCH 2/6] Test against all major supported MRI's --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index d06d9218..0c6a3f98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,8 @@ dist: trusty language: ruby rvm: - 2.5.1 +- 2.4.4 +- 2.3.7 - jruby - rbx-3 cache: bundler From 1c1380e0e90726ba29ac4a8a7cfa2e2de2bf95d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 13 Sep 2018 12:22:24 -0300 Subject: [PATCH 3/6] Drop support for EOL'd rubies --- i18n-tasks.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n-tasks.gemspec b/i18n-tasks.gemspec index d5df804d..17ff07a3 100644 --- a/i18n-tasks.gemspec +++ b/i18n-tasks.gemspec @@ -26,7 +26,7 @@ TEXT s.homepage = 'https://github.com/glebm/i18n-tasks' s.metadata = { 'issue_tracker' => 'https://github.com/glebm/i18n-tasks' } if s.respond_to?(:metadata=) # rubocop:disable Gemspec/RequiredRubyVersion - s.required_ruby_version = '~> 2.1' if s.respond_to?(:required_ruby_version=) + s.required_ruby_version = '~> 2.3' if s.respond_to?(:required_ruby_version=) # rubocop:enable Gemspec/RequiredRubyVersion s.files = `git ls-files`.split($/) From 7bd2a5cea972e293bba5dfd2e5fb910493e29d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 13 Sep 2018 12:26:08 -0300 Subject: [PATCH 4/6] Test major supported jrubies as well 9.1 as MRI 2.3-compatible and 9.2 as MRI 2.5-compatible. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0c6a3f98..ba59784a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,8 @@ rvm: - 2.5.1 - 2.4.4 - 2.3.7 -- jruby +- jruby-9.1.17.0 +- jruby-9.2.0.0 - rbx-3 cache: bundler bundler_args: --without debug From 87daa799159dff254eb3219683391cc255c24d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 13 Sep 2018 12:32:31 -0300 Subject: [PATCH 5/6] This check should now pass --- i18n-tasks.gemspec | 2 -- 1 file changed, 2 deletions(-) diff --git a/i18n-tasks.gemspec b/i18n-tasks.gemspec index 17ff07a3..06bb85f9 100644 --- a/i18n-tasks.gemspec +++ b/i18n-tasks.gemspec @@ -25,9 +25,7 @@ cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/ TEXT s.homepage = 'https://github.com/glebm/i18n-tasks' s.metadata = { 'issue_tracker' => 'https://github.com/glebm/i18n-tasks' } if s.respond_to?(:metadata=) - # rubocop:disable Gemspec/RequiredRubyVersion s.required_ruby_version = '~> 2.3' if s.respond_to?(:required_ruby_version=) - # rubocop:enable Gemspec/RequiredRubyVersion s.files = `git ls-files`.split($/) s.files -= s.files.grep(%r{^(doc/|\.|spec/)}) + %w[CHANGES.md config/i18n-tasks.yml Gemfile] From f85da4cf60247f8c8ab2983898216c76b4244967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 13 Sep 2018 12:34:02 -0300 Subject: [PATCH 6/6] Fix TODO's that can now be fixed --- .rubocop.yml | 12 ---- i18n-tasks.gemspec | 18 +++--- .../tasks/command/option_parsers/locale.rb | 2 +- lib/i18n/tasks/console_context.rb | 60 +++++++++---------- lib/i18n/tasks/data/file_system_base.rb | 20 +++---- lib/i18n/tasks/data/tree/node.rb | 2 +- lib/i18n/tasks/data/tree/traversal.rb | 2 +- lib/i18n/tasks/locale_pathname.rb | 2 +- lib/i18n/tasks/reports/base.rb | 2 +- lib/i18n/tasks/scanners/relative_keys.rb | 2 +- .../tasks/scanners/ruby_ast_call_finder.rb | 2 +- lib/i18n/tasks/translators/base_translator.rb | 10 ++-- spec/i18n_tasks_spec.rb | 38 ++++++------ spec/support/i18n_tasks_output_matcher.rb | 8 +-- spec/used_keys_spec.rb | 20 +++---- 15 files changed, 94 insertions(+), 106 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 9ef9932c..fd35fb63 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -52,21 +52,9 @@ Style/FormatStringToken: Style/GuardClause: Enabled: false -Layout/IndentHeredoc: - # TODO(glebm): Remove this when we update the minimum Ruby version to 2.3+. - Enabled: false - Style/MultilineBlockChain: Enabled: false -Style/NumericPredicate: - # TODO(glebm): Remove this when we update the minimum Ruby version to 2.3+. - Enabled: false - -Style/SafeNavigation: - # TODO(glebm): Remove this when we update the minimum Ruby version to 2.3+. - Enabled: false - Style/StderrPuts: # This is intentional, i18n-tasks stderr output is mostly anything but warnings. Enabled: false diff --git a/i18n-tasks.gemspec b/i18n-tasks.gemspec index 06bb85f9..3a295d07 100644 --- a/i18n-tasks.gemspec +++ b/i18n-tasks.gemspec @@ -11,17 +11,17 @@ Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength s.email = ['glex.spb@gmail.com'] s.license = 'MIT' s.summary = 'Manage localization and translation with the awesome power of static analysis' - s.description = <<-TEXT -i18n-tasks helps you find and manage missing and unused translations. + s.description = <<~TEXT + i18n-tasks helps you find and manage missing and unused translations. -It analyses code statically for key usages, such as `I18n.t('some.key')`, in order to report keys that are missing or unused, -pre-fill missing keys (optionally from Google Translate), and remove unused keys. + It analyses code statically for key usages, such as `I18n.t('some.key')`, in order to report keys that are missing or unused, + pre-fill missing keys (optionally from Google Translate), and remove unused keys. TEXT - s.post_install_message = <<-TEXT -# Install default configuration: -cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/ -# Add an RSpec for missing and unused keys: -cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/ + s.post_install_message = <<~TEXT + # Install default configuration: + cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/ + # Add an RSpec for missing and unused keys: + cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/ TEXT s.homepage = 'https://github.com/glebm/i18n-tasks' s.metadata = { 'issue_tracker' => 'https://github.com/glebm/i18n-tasks' } if s.respond_to?(:metadata=) diff --git a/lib/i18n/tasks/command/option_parsers/locale.rb b/lib/i18n/tasks/command/option_parsers/locale.rb index 1a384d56..b3a20eaa 100644 --- a/lib/i18n/tasks/command/option_parsers/locale.rb +++ b/lib/i18n/tasks/command/option_parsers/locale.rb @@ -47,7 +47,7 @@ def call(vals, context) end def move_base_locale_to_front!(locales, base_locale) - if (pos = locales.index(base_locale)) && pos > 0 + if (pos = locales.index(base_locale)) && pos.positive? locales[pos], locales[0] = locales[0], locales[pos] end locales diff --git a/lib/i18n/tasks/console_context.rb b/lib/i18n/tasks/console_context.rb index abeb2183..15251b47 100644 --- a/lib/i18n/tasks/console_context.rb +++ b/lib/i18n/tasks/console_context.rb @@ -34,41 +34,41 @@ def banner end def guide - Rainbow('i18n-tasks IRB Quick Start guide').green.bright + "\n" + <<-TEXT -#{Rainbow('Data as trees').yellow} - tree(locale) - used_tree(key_filter: nil, strict: nil) - unused_tree(locale: base_locale, strict: nil) - build_tree('es' => {'hello' => 'Hola'}) + Rainbow('i18n-tasks IRB Quick Start guide').green.bright + "\n" + <<~TEXT + #{Rainbow('Data as trees').yellow} + tree(locale) + used_tree(key_filter: nil, strict: nil) + unused_tree(locale: base_locale, strict: nil) + build_tree('es' => {'hello' => 'Hola'}) -#{Rainbow('Traversal').yellow} - tree = missing_diff_tree('es') - tree.nodes { |node| } - tree.nodes.to_a - tree.leaves { |node| } - tree.each { |root_node| } - # also levels, depth_first, and breadth_first + #{Rainbow('Traversal').yellow} + tree = missing_diff_tree('es') + tree.nodes { |node| } + tree.nodes.to_a + tree.leaves { |node| } + tree.each { |root_node| } + # also levels, depth_first, and breadth_first -#{Rainbow('Select nodes').yellow} - tree.select_nodes { |node| } # new tree with only selected nodes + #{Rainbow('Select nodes').yellow} + tree.select_nodes { |node| } # new tree with only selected nodes -#{Rainbow('Match by full key').yellow} - tree.select_keys { |key, leaf| } # new tree with only selected keys - tree.grep_keys(/hello/) # grep, using === - tree.keys { |key, leaf| } # enumerate over [full_key, leaf_node] - # Pass {root: true} to include root node in full_key (usually locale) + #{Rainbow('Match by full key').yellow} + tree.select_keys { |key, leaf| } # new tree with only selected keys + tree.grep_keys(/hello/) # grep, using === + tree.keys { |key, leaf| } # enumerate over [full_key, leaf_node] + # Pass {root: true} to include root node in full_key (usually locale) -#{Rainbow('Nodes').yellow} - node = node(key, locale) - node.key # only the part after the last dot - node.full_key # full key. Includes root key, pass {root: false} to override. - # also: value, value_or_children_hash, data, walk_to_root, walk_from_root - Tree::Node.new(key: 'en') + #{Rainbow('Nodes').yellow} + node = node(key, locale) + node.key # only the part after the last dot + node.full_key # full key. Includes root key, pass {root: false} to override. + # also: value, value_or_children_hash, data, walk_to_root, walk_from_root + Tree::Node.new(key: 'en') -#{Rainbow('Keys').yellow} - t(key, locale) - key_value?(key, locale) - depluralize_key(key, locale) # convert 'hat.one' to 'hat' + #{Rainbow('Keys').yellow} + t(key, locale) + key_value?(key, locale) + depluralize_key(key, locale) # convert 'hat.one' to 'hat' TEXT end end diff --git a/lib/i18n/tasks/data/file_system_base.rb b/lib/i18n/tasks/data/file_system_base.rb index af5757df..57a57384 100644 --- a/lib/i18n/tasks/data/file_system_base.rb +++ b/lib/i18n/tasks/data/file_system_base.rb @@ -56,7 +56,7 @@ def external(locale) # @param [::I18n::Tasks::Data::Siblings] tree def set(locale, tree) locale = locale.to_s - @trees.delete(locale) if @trees + @trees&.delete(locale) paths_before = Set.new(get(locale)[locale].leaves.map { |node| node.data[:path] }) paths_after = Set.new([]) router.route locale, tree do |path, tree_slice| @@ -66,7 +66,7 @@ def set(locale, tree) (paths_before - paths_after).each do |path| FileUtils.remove_file(path) if File.exist?(path) end - @trees.delete(locale) if @trees + @trees&.delete(locale) @available_locales = nil end @@ -180,14 +180,14 @@ def filter_nil_keys!(path, data, suffix = []) data.each do |key, value| if key.nil? data.delete(key) - log_warn <<-TEXT -Skipping a nil key found in #{path.inspect}: - key: #{suffix.join('.')}.`nil` - value: #{value.inspect} -Nil keys are not supported by i18n. -The following unquoted YAML keys result in a nil key: - #{%w[null Null NULL ~].join(', ')} -See http://yaml.org/type/null.html + log_warn <<~TEXT + Skipping a nil key found in #{path.inspect}: + key: #{suffix.join('.')}.`nil` + value: #{value.inspect} + Nil keys are not supported by i18n. + The following unquoted YAML keys result in a nil key: + #{%w[null Null NULL ~].join(', ')} + See http://yaml.org/type/null.html TEXT elsif value.is_a?(Hash) filter_nil_keys! path, value, suffix + [key] diff --git a/lib/i18n/tasks/data/tree/node.rb b/lib/i18n/tasks/data/tree/node.rb index 45ebc72a..a8aae56d 100644 --- a/lib/i18n/tasks/data/tree/node.rb +++ b/lib/i18n/tasks/data/tree/node.rb @@ -143,7 +143,7 @@ def to_nodes end def to_siblings - parent && parent.children || Siblings.new(nodes: [self]) + parent&.children || Siblings.new(nodes: [self]) end def to_hash(sort = false) diff --git a/lib/i18n/tasks/data/tree/traversal.rb b/lib/i18n/tasks/data/tree/traversal.rb index 57ea5297..f9c2c3c1 100644 --- a/lib/i18n/tasks/data/tree/traversal.rb +++ b/lib/i18n/tasks/data/tree/traversal.rb @@ -104,7 +104,7 @@ def select_nodes!(&block) to_remove = [] each do |node| if block.yield(node) - node.children.select_nodes!(&block) if node.children + node.children&.select_nodes!(&block) else # removing during each is unsafe to_remove << node diff --git a/lib/i18n/tasks/locale_pathname.rb b/lib/i18n/tasks/locale_pathname.rb index d83fc208..bdccaf7b 100644 --- a/lib/i18n/tasks/locale_pathname.rb +++ b/lib/i18n/tasks/locale_pathname.rb @@ -4,7 +4,7 @@ module I18n::Tasks module LocalePathname class << self def replace_locale(path, from, to) - path && path.gsub(path_locale_re(from), to) + path&.gsub(path_locale_re(from), to) end private diff --git a/lib/i18n/tasks/reports/base.rb b/lib/i18n/tasks/reports/base.rb index c758e7e4..4a078234 100644 --- a/lib/i18n/tasks/reports/base.rb +++ b/lib/i18n/tasks/reports/base.rb @@ -32,7 +32,7 @@ def eq_base_title(key_values, locale = base_locale) def used_title(keys_nodes, filter) used_n = keys_nodes.map { |_k, node| node.data[:occurrences].size }.reduce(:+).to_i "#{keys_nodes.size} key#{'s' if keys_nodes.size != 1}#{" matching '#{filter}'" if filter}"\ - "#{" (#{used_n} usage#{'s' if used_n != 1})" if used_n > 0}" + "#{" (#{used_n} usage#{'s' if used_n != 1})" if used_n.positive?}" end # Sort keys by their attributes in order diff --git a/lib/i18n/tasks/scanners/relative_keys.rb b/lib/i18n/tasks/scanners/relative_keys.rb index 7da5702a..5624c98d 100644 --- a/lib/i18n/tasks/scanners/relative_keys.rb +++ b/lib/i18n/tasks/scanners/relative_keys.rb @@ -41,7 +41,7 @@ def path_root(path, roots) def prefix(normalized_path, calling_method: nil) file_key = normalized_path.gsub(%r{(\.[^/]+)*$}, '').tr(File::SEPARATOR, DOT) calling_method = calling_method.call if calling_method.respond_to?(:call) - if calling_method && calling_method.present? + if calling_method&.present? # Relative keys in mailers have a `_mailer` infix, but relative keys in controllers do not have one: "#{file_key.sub(/_controller$/, '')}.#{calling_method}" else diff --git a/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb b/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb index 24636c0e..073170f4 100644 --- a/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +++ b/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb @@ -48,7 +48,7 @@ def on_send(send_node) message = send_node.children[1] valid_receivers = @message_receivers[message] # use `any?` because `include?` checks type equality, but the receiver is a Parser::AST::Node != AST::Node. - @callback.call(send_node, @method_name) if valid_receivers && valid_receivers.any? { |r| r == receiver } + @callback.call(send_node, @method_name) if valid_receivers&.any? { |r| r == receiver } # always invoke handler_missing to get nested translations in children handler_missing send_node nil diff --git a/lib/i18n/tasks/translators/base_translator.rb b/lib/i18n/tasks/translators/base_translator.rb index daf3c142..ccac3ac7 100644 --- a/lib/i18n/tasks/translators/base_translator.rb +++ b/lib/i18n/tasks/translators/base_translator.rb @@ -119,11 +119,11 @@ def restore_interpolations(untranslated, translated) end def raise_interpolation_error(untranslated, translated, e) - fail CommandError.new(e, <<-TEXT.strip) -Error when restoring interpolations: - original: "#{untranslated}" - response: "#{translated}" - error: #{e.message} (#{e.class.name}) + fail CommandError.new(e, <<~TEXT.strip) + Error when restoring interpolations: + original: "#{untranslated}" + response: "#{translated}" + error: #{e.message} (#{e.class.name}) TEXT end diff --git a/spec/i18n_tasks_spec.rb b/spec/i18n_tasks_spec.rb index c9471d6a..65d518d0 100644 --- a/spec/i18n_tasks_spec.rb +++ b/spec/i18n_tasks_spec.rb @@ -308,30 +308,30 @@ describe 'find' do it 'prints usages' do result = strip_ansi_escape(run_cmd('find', 'used.*')) - expect(result).to eq(<<-TXT) -used.a 2 - app/views/usages.html.slim:1 p = t 'used.a' - app/views/usages.html.slim:2 b = t 'used.a' + expect(result).to eq(<<~TXT) + used.a 2 + app/views/usages.html.slim:1 p = t 'used.a' + app/views/usages.html.slim:2 b = t 'used.a' TXT end it 'finds references' do result = strip_ansi_escape(run_cmd('find', 'reference*')) - expect(result).to eq(<<-TXT) -missing_target.a (resolved ref) - app/views/index.html.slim:36 = t 'reference-missing-target.a' -reference-missing-target (ref key) - app/views/index.html.slim:36 = t 'reference-missing-target.a' -reference-missing-target.a (ref) - app/views/index.html.slim:36 = t 'reference-missing-target.a' -reference-ok-nested (ref key) - app/views/index.html.slim:35 = t 'reference-ok-nested.a' -reference-ok-nested.a (ref) - app/views/index.html.slim:35 = t 'reference-ok-nested.a' -reference-ok-plain (ref key) - app/views/index.html.slim:34 = t 'reference-ok-plain' -resolved_reference_target.a (resolved ref) - app/views/index.html.slim:35 = t 'reference-ok-nested.a' + expect(result).to eq(<<~TXT) + missing_target.a (resolved ref) + app/views/index.html.slim:36 = t 'reference-missing-target.a' + reference-missing-target (ref key) + app/views/index.html.slim:36 = t 'reference-missing-target.a' + reference-missing-target.a (ref) + app/views/index.html.slim:36 = t 'reference-missing-target.a' + reference-ok-nested (ref key) + app/views/index.html.slim:35 = t 'reference-ok-nested.a' + reference-ok-nested.a (ref) + app/views/index.html.slim:35 = t 'reference-ok-nested.a' + reference-ok-plain (ref key) + app/views/index.html.slim:34 = t 'reference-ok-plain' + resolved_reference_target.a (resolved ref) + app/views/index.html.slim:35 = t 'reference-ok-nested.a' TXT end end diff --git a/spec/support/i18n_tasks_output_matcher.rb b/spec/support/i18n_tasks_output_matcher.rb index a91b1385..c71564b0 100644 --- a/spec/support/i18n_tasks_output_matcher.rb +++ b/spec/support/i18n_tasks_output_matcher.rb @@ -30,11 +30,11 @@ def extract_keys(actual) e = expected.sort a = extract_keys(actual).sort - <<-MSG.strip -Expected #{e}, but had #{a}. Diff: + <<~MSG.strip + Expected #{e}, but had #{a}. Diff: -missing: #{e - a} -extra: #{a - e} + missing: #{e - a} + extra: #{a - e} MSG end end diff --git a/spec/used_keys_spec.rb b/spec/used_keys_spec.rb index 195f7128..ac6e834b 100644 --- a/spec/used_keys_spec.rb +++ b/spec/used_keys_spec.rb @@ -6,12 +6,12 @@ let!(:task) { I18n::Tasks::BaseTask.new } let(:file_name) { 'a.html.slim' } let(:file_content) do - <<-SLIM -div = t 'a' - p = t 'a' -h1 = t 'b' -h2 = t 'c.layer' -h3 = t 'c.layer.underneath_c' + <<~SLIM + div = t 'a' + p = t 'a' + h1 = t 'b' + h2 = t 'c.layer' + h3 = t 'c.layer.underneath_c' SLIM end @@ -63,10 +63,10 @@ describe 'when input is haml' do let(:file_name) { 'a.html.haml' } let(:file_content) do - <<-HAML -#first{ title: t('a') } -.second{ title: t('a') } -- # t('a') in a comment is ignored + <<~HAML + #first{ title: t('a') } + .second{ title: t('a') } + - # t('a') in a comment is ignored HAML end