copy_tuner_client 2.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.rubocop.yml +46 -1
- data/CLAUDE.md +1 -0
- data/Gemfile +1 -0
- data/copy_tuner_client.gemspec +3 -3
- data/gemfiles/8.0.gemfile +3 -3
- data/gemfiles/8.1.gemfile +3 -3
- data/gemfiles/main.gemfile +3 -3
- data/lib/copy_tuner_client/cache.rb +34 -27
- data/lib/copy_tuner_client/client.rb +35 -23
- data/lib/copy_tuner_client/configuration.rb +62 -40
- data/lib/copy_tuner_client/copyray/rewriter.rb +7 -4
- data/lib/copy_tuner_client/copyray.rb +1 -0
- data/lib/copy_tuner_client/copyray_middleware.rb +39 -23
- data/lib/copy_tuner_client/dotted_hash.rb +7 -7
- data/lib/copy_tuner_client/engine.rb +4 -6
- data/lib/copy_tuner_client/helper_extension.rb +58 -54
- data/lib/copy_tuner_client/i18n_backend.rb +12 -14
- data/lib/copy_tuner_client/i18n_compat.rb +2 -1
- data/lib/copy_tuner_client/poller.rb +3 -4
- data/lib/copy_tuner_client/prefixed_logger.rb +13 -12
- data/lib/copy_tuner_client/process_guard.rb +29 -26
- data/lib/copy_tuner_client/queue_with_timeout.rb +29 -19
- data/lib/copy_tuner_client/rails.rb +9 -8
- data/lib/copy_tuner_client/request_sync.rb +26 -24
- data/lib/copy_tuner_client/simple_form_extention.rb +10 -6
- data/lib/copy_tuner_client/translation_log.rb +10 -5
- data/lib/copy_tuner_client/version.rb +1 -1
- data/lib/copy_tuner_client.rb +4 -8
- data/lib/tasks/copy_tuner_client_tasks.rake +14 -16
- data/skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb +14 -7
- data/skills/copy-tuner-to-t-migrate/scripts/migrate_tt.rb +19 -17
- data/spec/copy_tuner_client/cache_spec.rb +63 -57
- data/spec/copy_tuner_client/client_spec.rb +82 -81
- data/spec/copy_tuner_client/configuration_spec.rb +223 -222
- data/spec/copy_tuner_client/copyray/marker_spec.rb +2 -1
- data/spec/copy_tuner_client/copyray/rewriter_spec.rb +24 -3
- data/spec/copy_tuner_client/copyray_middleware_spec.rb +42 -5
- data/spec/copy_tuner_client/copyray_spec.rb +8 -8
- data/spec/copy_tuner_client/dotted_hash_spec.rb +33 -33
- data/spec/copy_tuner_client/helper_extension_spec.rb +47 -44
- data/spec/copy_tuner_client/i18n_backend_spec.rb +144 -143
- data/spec/copy_tuner_client/poller_spec.rb +25 -24
- data/spec/copy_tuner_client/prefixed_logger_spec.rb +15 -12
- data/spec/copy_tuner_client/process_guard_spec.rb +27 -27
- data/spec/copy_tuner_client/request_sync_spec.rb +51 -58
- data/spec/copy_tuner_client/translation_log_spec.rb +38 -24
- data/spec/copy_tuner_client_spec.rb +5 -6
- data/spec/support/client_spec_helpers.rb +1 -1
- data/spec/support/defines_constants.rb +3 -34
- data/spec/support/fake_client.rb +1 -3
- data/spec/support/fake_copy_tuner_app.rb +54 -62
- data/spec/support/fake_html_safe_string.rb +2 -3
- data/spec/support/fake_logger.rb +22 -21
- data/spec/support/fake_passenger.rb +4 -6
- data/spec/support/fake_unicorn.rb +2 -4
- data/spec/support/middleware_stack.rb +3 -3
- data/spec/support/writing_cache.rb +4 -4
- metadata +2 -2
|
@@ -4,15 +4,16 @@ module CopyTunerClient
|
|
|
4
4
|
# Sets up the logger, environment, name, project root, and framework name
|
|
5
5
|
# for Rails applications. Must be called after framework initialization.
|
|
6
6
|
def self.initialize
|
|
7
|
-
CopyTunerClient.configure(false) do |config|
|
|
7
|
+
CopyTunerClient.configure(apply: false) do |config|
|
|
8
8
|
config.environment_name = ::Rails.env
|
|
9
|
-
config.logger =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
config.logger =
|
|
10
|
+
if defined?(::Rails::Console)
|
|
11
|
+
Logger.new(File::NULL)
|
|
12
|
+
elsif defined?(::Rails) && ::Rails.env.development?
|
|
13
|
+
Logger.new('log/copy_tuner.log')
|
|
14
|
+
else
|
|
15
|
+
::Rails.logger
|
|
16
|
+
end
|
|
16
17
|
config.framework = "Rails: #{::Rails::VERSION::STRING}"
|
|
17
18
|
config.middleware = ::Rails.configuration.middleware
|
|
18
19
|
config.download_cache_dir = ::Rails.root.join('tmp', 'cache', 'copy_tuner_client')
|
|
@@ -8,8 +8,8 @@ module CopyTunerClient
|
|
|
8
8
|
# Rack middleware that synchronizes with CopyTuner during each request.
|
|
9
9
|
#
|
|
10
10
|
# This is injected into the Rails middleware stack in development environments.
|
|
11
|
-
class RequestSync
|
|
12
|
-
VIEW_PATH = File.expand_path('
|
|
11
|
+
class RequestSync # rubocop:disable Metrics/ClassLength
|
|
12
|
+
VIEW_PATH = File.expand_path('../../ui/views', __dir__)
|
|
13
13
|
|
|
14
14
|
# @param app [Rack] the upstream app into whose responses to inject the editor
|
|
15
15
|
# @param options [Hash]
|
|
@@ -29,7 +29,7 @@ module CopyTunerClient
|
|
|
29
29
|
# Invokes the upstream Rack application and flushes the cache after each
|
|
30
30
|
# request.
|
|
31
31
|
def call(env)
|
|
32
|
-
if
|
|
32
|
+
if %r{^/copytuner} =~ ::Rack::Request.new(env).path_info
|
|
33
33
|
dup._call(env)
|
|
34
34
|
else
|
|
35
35
|
first_request = @first
|
|
@@ -53,7 +53,7 @@ module CopyTunerClient
|
|
|
53
53
|
@req = ::Rack::Request.new(env)
|
|
54
54
|
|
|
55
55
|
case @req.path_info
|
|
56
|
-
when
|
|
56
|
+
when %r{^/copytuner/?$}
|
|
57
57
|
index
|
|
58
58
|
when /sync/
|
|
59
59
|
sync
|
|
@@ -71,21 +71,19 @@ module CopyTunerClient
|
|
|
71
71
|
|
|
72
72
|
def sync
|
|
73
73
|
@cache.sync
|
|
74
|
-
::Rack::Response.new{|r| r.redirect('/copytuner/')}.finish
|
|
74
|
+
::Rack::Response.new { |r| r.redirect('/copytuner/') }.finish
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
def render(view, layout
|
|
77
|
+
def render(view, layout: true) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
78
78
|
add_rack_array = true
|
|
79
|
-
if view.is_a?
|
|
79
|
+
if view.is_a?(Hash)
|
|
80
80
|
layout = false
|
|
81
|
-
if view[:object]
|
|
82
|
-
object = view[:object]
|
|
83
|
-
end
|
|
81
|
+
object = view[:object] if view[:object]
|
|
84
82
|
|
|
85
83
|
if view[:collection]
|
|
86
|
-
return view[:collection].map
|
|
87
|
-
render({:
|
|
88
|
-
|
|
84
|
+
return view[:collection].map { |object|
|
|
85
|
+
render({ partial: view[:partial], object: })
|
|
86
|
+
}.join(' ')
|
|
89
87
|
end
|
|
90
88
|
|
|
91
89
|
if view[:partial]
|
|
@@ -93,17 +91,19 @@ module CopyTunerClient
|
|
|
93
91
|
view = "_#{view[:partial]}"
|
|
94
92
|
end
|
|
95
93
|
end
|
|
96
|
-
binding =
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
binding = proc {}.binding
|
|
95
|
+
body =
|
|
96
|
+
if layout
|
|
97
|
+
render_with_layout(view) do
|
|
98
|
+
render_without_layout(view, binding)
|
|
99
|
+
end
|
|
100
|
+
else
|
|
99
101
|
render_without_layout(view, binding)
|
|
100
102
|
end
|
|
101
|
-
else
|
|
102
|
-
body = render_without_layout(view, binding)
|
|
103
|
-
end
|
|
104
103
|
if add_rack_array
|
|
105
104
|
::Rack::Response.new(
|
|
106
|
-
body,
|
|
105
|
+
body,
|
|
106
|
+
200,
|
|
107
107
|
::Rack::CONTENT_TYPE => 'text/html; charset=utf-8'
|
|
108
108
|
).finish
|
|
109
109
|
else
|
|
@@ -115,13 +115,14 @@ module CopyTunerClient
|
|
|
115
115
|
# that block are interpolated into the layout
|
|
116
116
|
def render_with_layout(view)
|
|
117
117
|
body = ERB.new(File.read(File.join(VIEW_PATH, 'layouts/copytuner_default.html.erb')))
|
|
118
|
-
body.result(
|
|
118
|
+
body.result(proc {}.binding)
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
# you have to pass a binding to this (a proc) so that ERB can have
|
|
122
122
|
# access to helper functions and local variables
|
|
123
123
|
def render_without_layout(view, binding)
|
|
124
|
-
|
|
124
|
+
path = File.join(VIEW_PATH, 'copytuner', "#{view}.html.erb")
|
|
125
|
+
ERB.new(File.read(path), eoutvar: 'frobnitz').result(binding)
|
|
125
126
|
end
|
|
126
127
|
|
|
127
128
|
def cancel_sync?(env)
|
|
@@ -133,17 +134,18 @@ module CopyTunerClient
|
|
|
133
134
|
end
|
|
134
135
|
|
|
135
136
|
def asset_request?(env)
|
|
136
|
-
env['PATH_INFO'] =~
|
|
137
|
+
env['PATH_INFO'] =~ %r{^/assets}
|
|
137
138
|
end
|
|
138
139
|
|
|
139
140
|
def in_interval?
|
|
140
141
|
return false if @last_synced.nil?
|
|
141
142
|
return false if @interval <= 0
|
|
143
|
+
|
|
142
144
|
next_sync_at > Time.now.utc
|
|
143
145
|
end
|
|
144
146
|
|
|
145
147
|
def next_sync_at
|
|
146
|
-
@last_synced + @interval if @last_synced
|
|
148
|
+
@last_synced + @interval if @last_synced && @interval
|
|
147
149
|
end
|
|
148
150
|
|
|
149
151
|
def update_last_synced
|
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
require 'copy_tuner_client/copyray'
|
|
2
2
|
|
|
3
3
|
begin
|
|
4
|
-
require
|
|
4
|
+
require 'simple_form'
|
|
5
5
|
rescue LoadError
|
|
6
|
+
# simple_form は任意依存のため、未導入環境では以降の拡張定義自体をスキップする
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
if defined?(SimpleForm)
|
|
10
|
+
# SimpleForm のラベル生成に Copyray のオーバーレイマーカーを注入する
|
|
9
11
|
module SimpleForm::Components::Labels
|
|
10
12
|
protected
|
|
13
|
+
|
|
11
14
|
def label_translation_with_copyray_comment
|
|
12
15
|
source = label_translation_without_copyray_comment
|
|
13
16
|
|
|
14
17
|
if !CopyTunerClient.configuration.disable_copyray_comment_injection && object.class.respond_to?(:lookup_ancestors)
|
|
15
18
|
attributes_scope = "#{object.class.i18n_scope}.attributes"
|
|
16
|
-
defaults =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
defaults =
|
|
20
|
+
object.class.lookup_ancestors.map do |klass|
|
|
21
|
+
"#{attributes_scope}.#{klass.model_name.i18n_key}.#{reflection_or_attribute_name}"
|
|
22
|
+
end
|
|
19
23
|
CopyTunerClient::Copyray.augment_template(source, defaults.shift)
|
|
20
24
|
else
|
|
21
25
|
source
|
|
22
26
|
end
|
|
23
27
|
end
|
|
24
28
|
if CopyTunerClient.configuration.enable_middleware?
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
alias label_translation_without_copyray_comment label_translation
|
|
30
|
+
alias label_translation label_translation_with_copyray_comment
|
|
27
31
|
end
|
|
28
32
|
end
|
|
29
33
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
module CopyTunerClient
|
|
2
|
+
# リクエスト中に翻訳されたキーと結果をスレッドローカルに記録する(Copyray オーバーレイの JSON 出力に使う)
|
|
2
3
|
class TranslationLog
|
|
3
4
|
def self.translations
|
|
4
5
|
Thread.current[:translations]
|
|
@@ -23,16 +24,19 @@ module CopyTunerClient
|
|
|
23
24
|
|
|
24
25
|
def self.install_hook
|
|
25
26
|
I18n.class_eval do
|
|
27
|
+
# alias_method が参照するため、alias_method 群より先に定義する必要がある
|
|
28
|
+
# rubocop:disable Style/ClassMethodsDefinitions
|
|
26
29
|
class << self
|
|
27
30
|
def translate_with_copy_tuner_hook(key = nil, **options)
|
|
28
31
|
scope = options[:scope]
|
|
29
32
|
scope = scope.dup if scope.is_a?(Array) || scope.is_a?(String)
|
|
30
33
|
result = translate_without_copy_tuner_hook(key, **options)
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
pairs = key.is_a?(Array) ? key.zip(result) : [[key, result]]
|
|
36
|
+
pairs.each do |k, v|
|
|
37
|
+
next if v.is_a?(Array)
|
|
38
|
+
|
|
39
|
+
CopyTunerClient::TranslationLog.add(I18n.normalize_keys(nil, k, scope).compact.join('.'), v)
|
|
36
40
|
end
|
|
37
41
|
result
|
|
38
42
|
end
|
|
@@ -40,9 +44,10 @@ module CopyTunerClient
|
|
|
40
44
|
if CopyTunerClient.configuration.enable_middleware?
|
|
41
45
|
alias_method :translate_without_copy_tuner_hook, :translate
|
|
42
46
|
alias_method :translate, :translate_with_copy_tuner_hook
|
|
43
|
-
|
|
47
|
+
alias_method :t, :translate
|
|
44
48
|
end
|
|
45
49
|
end
|
|
50
|
+
# rubocop:enable Style/ClassMethodsDefinitions
|
|
46
51
|
end
|
|
47
52
|
end
|
|
48
53
|
end
|
data/lib/copy_tuner_client.rb
CHANGED
|
@@ -55,16 +55,12 @@ module CopyTunerClient
|
|
|
55
55
|
# @param apply [Boolean] (internal) whether the configuration should be applied yet.
|
|
56
56
|
#
|
|
57
57
|
# @yield [Configuration] the configuration to be modified
|
|
58
|
-
def self.configure(apply
|
|
58
|
+
def self.configure(apply: true)
|
|
59
59
|
self.configuration ||= Configuration.new
|
|
60
|
-
yield
|
|
60
|
+
yield(configuration)
|
|
61
61
|
|
|
62
|
-
if apply
|
|
63
|
-
configuration.apply
|
|
64
|
-
end
|
|
62
|
+
configuration.apply if apply
|
|
65
63
|
end
|
|
66
64
|
end
|
|
67
65
|
|
|
68
|
-
if defined?
|
|
69
|
-
require 'copy_tuner_client/rails'
|
|
70
|
-
end
|
|
66
|
+
require 'copy_tuner_client/rails' if defined? Rails
|
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
namespace :copy_tuner do
|
|
2
|
-
desc
|
|
3
|
-
task :
|
|
1
|
+
namespace :copy_tuner do # rubocop:disable Metrics/BlockLength
|
|
2
|
+
desc 'Notify CopyTuner of a new deploy.'
|
|
3
|
+
task deploy: :environment do
|
|
4
4
|
CopyTunerClient.deploy
|
|
5
|
-
puts
|
|
5
|
+
puts 'Successfully marked all blurbs as published.'
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
desc
|
|
8
|
+
desc 'Export CopyTuner blurbs to yaml.'
|
|
9
9
|
task :export, %i[path] => :environment do |_, args|
|
|
10
|
-
args.with_defaults(path:
|
|
10
|
+
args.with_defaults(path: 'config/locales/copy_tuner.yml')
|
|
11
11
|
CopyTunerClient.cache.sync
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
raise "No blurbs have been cached."
|
|
18
|
-
end
|
|
13
|
+
yml = CopyTunerClient.export or raise 'No blurbs have been cached.'
|
|
14
|
+
|
|
15
|
+
File.new("#{Rails.root}/#{args[:path]}", 'w').write(yml)
|
|
16
|
+
puts "Successfully exported blurbs to #{args[:path]}."
|
|
19
17
|
end
|
|
20
18
|
|
|
21
|
-
desc
|
|
22
|
-
task :
|
|
19
|
+
desc 'Detect invalid keys.'
|
|
20
|
+
task detect_conflict_keys: :environment do
|
|
23
21
|
conflict_keys = CopyTunerClient::DottedHash.conflict_keys(CopyTunerClient.cache.blurbs)
|
|
24
22
|
|
|
25
23
|
if conflict_keys.empty?
|
|
@@ -30,8 +28,8 @@ namespace :copy_tuner do
|
|
|
30
28
|
end
|
|
31
29
|
end
|
|
32
30
|
|
|
33
|
-
desc
|
|
34
|
-
task :
|
|
31
|
+
desc 'Detect html incompatible keys.'
|
|
32
|
+
task detect_html_incompatible_keys: :environment do
|
|
35
33
|
require 'copy_tuner_client/i18n_compat'
|
|
36
34
|
html_incompatible_blurbs = CopyTunerClient::I18nCompat.select_html_incompatible_blurbs(CopyTunerClient.cache.blurbs)
|
|
37
35
|
|
|
@@ -40,12 +40,16 @@ end
|
|
|
40
40
|
options = {}
|
|
41
41
|
parser =
|
|
42
42
|
OptionParser.new do |opts|
|
|
43
|
-
opts.banner =
|
|
43
|
+
opts.banner =
|
|
44
|
+
'Usage: bin/rails runner migrate_prefix.rb -- --prefix PREFIX --export PATH --out PATH ' \
|
|
45
|
+
'[--regexp PATTERN] [--originals-glob GLOB]'
|
|
44
46
|
opts.on('--prefix PREFIX', 'ドット区切りの対象 prefix(例: date / activerecord.attributes)') { |v| options[:prefix] = v }
|
|
45
47
|
opts.on('--export PATH', '全件 export YAML(手順3で出力)') { |v| options[:export] = v }
|
|
46
48
|
opts.on('--out PATH', '移行分の出力先(例: config/locales/0010_date.yml)') { |v| options[:out] = v }
|
|
47
49
|
opts.on('--regexp PATTERN', 'leaf キー検証用の正規表現(省略時は prefix から \A<prefix>\. を生成)') { |v| options[:regexp] = v }
|
|
48
|
-
opts.on('--originals-glob GLOB', 'オリジナルファイルの glob(既定: config/locales/0000_original_*.yml)')
|
|
50
|
+
opts.on('--originals-glob GLOB', 'オリジナルファイルの glob(既定: config/locales/0000_original_*.yml)') do |v|
|
|
51
|
+
options[:originals_glob] = v
|
|
52
|
+
end
|
|
49
53
|
opts.on('--locales LIST', 'カンマ区切りの対象 locale(既定: I18n.available_locales。検証用の上書き)') { |v| options[:locales] = v }
|
|
50
54
|
end
|
|
51
55
|
# `bin/rails runner script -- ...` の `--` 以降だけを渡したいが、runner が剥がさない環境もあるため両対応。
|
|
@@ -186,9 +190,7 @@ LOCALES.each do |locale|
|
|
|
186
190
|
end
|
|
187
191
|
|
|
188
192
|
# 全 locale でスキップ=当該 prefix がどこにも存在しない(prefix の typo)。
|
|
189
|
-
if merged_by_locale.empty?
|
|
190
|
-
die("prefix '#{PREFIX}' のキーが export にもオリジナルにも見つからない。prefix を確認すること。")
|
|
191
|
-
end
|
|
193
|
+
die("prefix '#{PREFIX}' のキーが export にもオリジナルにも見つからない。prefix を確認すること。") if merged_by_locale.empty?
|
|
192
194
|
|
|
193
195
|
# ---- (2) 静的ガード(書き出し前にできる検証を先に。中途半端な --out を残さない)----
|
|
194
196
|
# --out が次回起動時に Rails の i18n glob でロードされる場所か(採番ミス・配置ミスの検出)。
|
|
@@ -206,7 +208,10 @@ merged_by_locale.each do |locale, merged|
|
|
|
206
208
|
non_matching = leaf_entries(merged).keys.grep_v(regexp)
|
|
207
209
|
next if non_matching.empty?
|
|
208
210
|
|
|
209
|
-
die(
|
|
211
|
+
die(
|
|
212
|
+
"locale #{locale}: regexp #{regexp.inspect} にマッチしない leaf キーがある(regexp/prefix 不一致):\n " \
|
|
213
|
+
"#{non_matching.join("\n ")}"
|
|
214
|
+
)
|
|
210
215
|
end
|
|
211
216
|
|
|
212
217
|
# 書き出し(ここまでの検証を通過してから)。merged_by_locale は { "ja" => {...}, "en" => {...} } 形なので
|
|
@@ -217,7 +222,9 @@ puts "配置: #{out_path} (locale #{merged_by_locale.keys.join(',')} / leaf
|
|
|
217
222
|
|
|
218
223
|
# YAML ラウンドトリップ(to_yaml → 再読込で各 locale の merged と一致するか)。
|
|
219
224
|
merged_by_locale.each do |locale, merged|
|
|
220
|
-
|
|
225
|
+
next if load_locale_tree(out_path, locale) == merged
|
|
226
|
+
|
|
227
|
+
die("YAML ラウンドトリップ不一致。書き出し結果が壊れている: #{out_path} (locale #{locale})")
|
|
221
228
|
end
|
|
222
229
|
|
|
223
230
|
# ---- (3) 移行漏れ検証(削除をメモリ上でシミュレート)----
|
|
@@ -86,7 +86,7 @@ RELATIVE_KEY_RE = /\btt\b\s*(?:\(\s*)?['":]\s*\./
|
|
|
86
86
|
# Rewriter(大文字 ⟦CT: 固定)の除去網をすり抜け画面に残る。I18n.t 化では直らない。
|
|
87
87
|
# 正しい修正は method 名を第一引数・表示テキストを第二引数へ分離すること。
|
|
88
88
|
LABEL_FIRST_ARG_RE = /
|
|
89
|
-
(?: (?<![\w.])\w+\.label\b | \blabel_tag\b | (?<![\w.])label(
|
|
89
|
+
(?: (?<![\w.])\w+\.label\b | \blabel_tag\b | (?<![\w.])label(?!\w) )
|
|
90
90
|
\s*\(?\s*
|
|
91
91
|
tt\b (?=\s*[("':@])
|
|
92
92
|
/x
|
|
@@ -110,14 +110,14 @@ def scan_file(path, in_app:)
|
|
|
110
110
|
next unless line =~ TT_CALL
|
|
111
111
|
|
|
112
112
|
entry = { file: rel, lineno: idx + 1, code: line.rstrip }
|
|
113
|
-
yield(classify_line(line, entry, in_app:
|
|
113
|
+
yield(classify_line(line, entry, in_app:))
|
|
114
114
|
end
|
|
115
115
|
rescue StandardError
|
|
116
116
|
nil
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
# ヒット 1 行を分類し、[:safe | :suspicious | :other, entry] を返す。
|
|
120
|
-
def classify_line(line, entry, in_app:)
|
|
120
|
+
def classify_line(line, entry, in_app:) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
121
121
|
# tt の定義(def/alias)は呼び出しではない。t( へ置換すると Rails の t を壊すので絶対に自動変換しない。
|
|
122
122
|
if line.match?(TT_DEFINITION)
|
|
123
123
|
entry[:reason] = 'tt の定義(def/alias)。自動変換禁止。呼び出しを全て t へ移した後に手動で削除する'
|
|
@@ -143,15 +143,16 @@ def classify_line(line, entry, in_app:)
|
|
|
143
143
|
|
|
144
144
|
entry[:kind] = :string_manipulation
|
|
145
145
|
entry[:relative_key] = line.match?(RELATIVE_KEY_RE)
|
|
146
|
-
entry[:hint] =
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
entry[:hint] =
|
|
147
|
+
if entry[:relative_key]
|
|
148
|
+
'I18n.t へ。相対キー(.foo)は絶対キーへ書き換えが必要'
|
|
149
|
+
else
|
|
150
|
+
'I18n.t(絶対キー) へ置換を検討'
|
|
151
|
+
end
|
|
151
152
|
[:suspicious, entry]
|
|
152
153
|
end
|
|
153
154
|
|
|
154
|
-
buckets = { safe
|
|
155
|
+
buckets = { safe:, suspicious:, other: }
|
|
155
156
|
Dir.glob(ALL_GLOB).each do |path|
|
|
156
157
|
# vendor / node_modules / tmp 等のノイズを除外
|
|
157
158
|
next if relative(path).start_with?('vendor/', 'node_modules/', 'tmp/', '.git/')
|
|
@@ -163,7 +164,7 @@ end
|
|
|
163
164
|
|
|
164
165
|
if options[:json]
|
|
165
166
|
require 'json'
|
|
166
|
-
puts JSON.pretty_generate(safe
|
|
167
|
+
puts JSON.pretty_generate(safe:, suspicious:, other:)
|
|
167
168
|
exit(0)
|
|
168
169
|
end
|
|
169
170
|
|
|
@@ -172,13 +173,14 @@ def print_section(title, entries)
|
|
|
172
173
|
entries.each do |e|
|
|
173
174
|
# hint があれば必ず kind も付く(classify_line で対で設定)ので素直に出す。
|
|
174
175
|
# suspicious 内のサブ種別(label_arg / string_manipulation)を可視化する。
|
|
175
|
-
suffix =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
suffix =
|
|
177
|
+
if e[:hint]
|
|
178
|
+
" # [#{e[:kind]}] #{e[:hint]}"
|
|
179
|
+
elsif e[:reason]
|
|
180
|
+
" # #{e[:reason]}"
|
|
181
|
+
else
|
|
182
|
+
''
|
|
183
|
+
end
|
|
182
184
|
puts "#{e[:file]}:#{e[:lineno]}: #{e[:code].strip}#{suffix}"
|
|
183
185
|
end
|
|
184
186
|
end
|