copy_tuner_client 2.1.2 → 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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +46 -1
  4. data/CLAUDE.md +1 -0
  5. data/Gemfile +1 -0
  6. data/copy_tuner_client.gemspec +3 -3
  7. data/gemfiles/8.0.gemfile +3 -3
  8. data/gemfiles/8.1.gemfile +3 -3
  9. data/gemfiles/main.gemfile +3 -3
  10. data/lib/copy_tuner_client/cache.rb +34 -27
  11. data/lib/copy_tuner_client/client.rb +35 -23
  12. data/lib/copy_tuner_client/configuration.rb +62 -40
  13. data/lib/copy_tuner_client/copyray/rewriter.rb +1 -1
  14. data/lib/copy_tuner_client/copyray.rb +1 -0
  15. data/lib/copy_tuner_client/copyray_middleware.rb +31 -24
  16. data/lib/copy_tuner_client/dotted_hash.rb +7 -7
  17. data/lib/copy_tuner_client/engine.rb +4 -6
  18. data/lib/copy_tuner_client/helper_extension.rb +58 -54
  19. data/lib/copy_tuner_client/i18n_backend.rb +12 -14
  20. data/lib/copy_tuner_client/i18n_compat.rb +2 -1
  21. data/lib/copy_tuner_client/poller.rb +3 -4
  22. data/lib/copy_tuner_client/prefixed_logger.rb +13 -12
  23. data/lib/copy_tuner_client/process_guard.rb +29 -26
  24. data/lib/copy_tuner_client/queue_with_timeout.rb +29 -19
  25. data/lib/copy_tuner_client/rails.rb +9 -8
  26. data/lib/copy_tuner_client/request_sync.rb +26 -24
  27. data/lib/copy_tuner_client/simple_form_extention.rb +10 -6
  28. data/lib/copy_tuner_client/translation_log.rb +10 -5
  29. data/lib/copy_tuner_client/version.rb +1 -1
  30. data/lib/copy_tuner_client.rb +4 -8
  31. data/lib/tasks/copy_tuner_client_tasks.rake +14 -16
  32. data/skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb +14 -7
  33. data/skills/copy-tuner-to-t-migrate/scripts/migrate_tt.rb +19 -17
  34. data/spec/copy_tuner_client/cache_spec.rb +63 -57
  35. data/spec/copy_tuner_client/client_spec.rb +82 -81
  36. data/spec/copy_tuner_client/configuration_spec.rb +223 -222
  37. data/spec/copy_tuner_client/copyray/marker_spec.rb +2 -1
  38. data/spec/copy_tuner_client/copyray/rewriter_spec.rb +15 -10
  39. data/spec/copy_tuner_client/copyray_middleware_spec.rb +9 -6
  40. data/spec/copy_tuner_client/copyray_spec.rb +8 -8
  41. data/spec/copy_tuner_client/dotted_hash_spec.rb +33 -33
  42. data/spec/copy_tuner_client/helper_extension_spec.rb +47 -44
  43. data/spec/copy_tuner_client/i18n_backend_spec.rb +144 -143
  44. data/spec/copy_tuner_client/poller_spec.rb +25 -24
  45. data/spec/copy_tuner_client/prefixed_logger_spec.rb +15 -12
  46. data/spec/copy_tuner_client/process_guard_spec.rb +27 -27
  47. data/spec/copy_tuner_client/request_sync_spec.rb +51 -58
  48. data/spec/copy_tuner_client/translation_log_spec.rb +38 -24
  49. data/spec/copy_tuner_client_spec.rb +5 -6
  50. data/spec/support/client_spec_helpers.rb +1 -1
  51. data/spec/support/defines_constants.rb +3 -34
  52. data/spec/support/fake_client.rb +1 -3
  53. data/spec/support/fake_copy_tuner_app.rb +54 -62
  54. data/spec/support/fake_html_safe_string.rb +2 -3
  55. data/spec/support/fake_logger.rb +22 -21
  56. data/spec/support/fake_passenger.rb +4 -6
  57. data/spec/support/fake_unicorn.rb +2 -4
  58. data/spec/support/middleware_stack.rb +3 -3
  59. data/spec/support/writing_cache.rb +4 -4
  60. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34fcab16da8e238f416a8d4be1c68212818ae95afb7f82112391517e901fbb8f
4
- data.tar.gz: dc9a727af71a83e39cb4d4c4aa41a8d1e982244936f294462da8e44b0b57d9fc
3
+ metadata.gz: 3fb9ea1caf782db8dac7ded7d77803a61586b5369b221b62d34e9e0290c58b06
4
+ data.tar.gz: ad4723a7fe5af378f68af6f786644cc549a1ebbe2523c3424af8fdaba5381433
5
5
  SHA512:
6
- metadata.gz: aed1f966e3c35e5d25f82fc96c56ff5532dd0b8d651be0a275f7f6f85f10a0bc58efe6b414ef004140306be70c2c2294c8c1054a112530a32e21070b098f4557
7
- data.tar.gz: 9179c177ecbc48104ea0b17db2e6adc1f88e8aa4a5dac4eb0366e171543d2f83bc03e160d7f6cadf65ccb4e8664280904b0c06eca2260d222c9463a51449ff04
6
+ metadata.gz: 7a894e4afd8428064c4fef7dd34db7bdfaab23879b5fa3aaabc5921437a3a80b7cf036fac920f8bf76e7274141f5a522f8364a493797084e7fee879f435d7eb4
7
+ data.tar.gz: 34211866166e6491e7488a8f87aadc96810afcea2f3b5069ae2b29b4b9e5f0fe9a620556e3fd55d403ed419c9ab917c34f4fee0eafea90e889a7b6d5653b0540
data/.gitignore CHANGED
@@ -22,3 +22,8 @@ node_modules
22
22
 
23
23
  Gemfile.lock
24
24
  .claude/settings.local.json
25
+
26
+ # >>> bundler-skills managed >>>
27
+ .claude/skills/gem-*
28
+ .agents/skills/gem-*
29
+ # <<< bundler-skills managed <<<
data/.rubocop.yml CHANGED
@@ -1,7 +1,52 @@
1
1
  inherit_gem:
2
- sgcop: ruby/rubocop.yml
2
+ sgcop:
3
+ - ruby/rubocop_strict.yml
4
+ - ruby/rubocop_rspec_strict.yml
5
+
6
+ plugins:
7
+ - rubocop-rake
8
+ - rubocop-rspec
3
9
 
4
10
  # .ruby-version が RuboCop 未対応の新しい Ruby を指すと解析が落ちるため、
5
11
  # サポート最低バージョンを明示する
12
+ # rubocop-rails は Rails プロジェクトではないため意図的に読み込まない
6
13
  AllCops:
7
14
  TargetRubyVersion: 3.3
15
+ NewCops: enable
16
+ SuggestExtensions: false
17
+
18
+ # gemfiles/*.gemfile が `gemspec path: '../'` で本体 gemspec の development dependency を
19
+ # 読み込む構成のため、Gemfile 側への移行は各 gemfiles/*.gemfile への書き戻しが必要になり
20
+ # 意味を変えない自動修正の範囲を超える。gemspec でのdevelopment dependency 宣言を維持する。
21
+ Gemspec/DevelopmentDependencies:
22
+ Exclude:
23
+ - 'copy_tuner_client.gemspec'
24
+
25
+ # middleware(テスト対象自身)の private メソッド append_js / helpers を no-op や
26
+ # フェイクに差し替えないと、Rails の ActionController::Base.helpers 依存を外して
27
+ # Rewriter の効果だけを検証できないため対象外。
28
+ RSpec/SubjectStub:
29
+ Exclude:
30
+ - 'spec/copy_tuner_client/copyray_middleware_spec.rb'
31
+
32
+ # Ruby 標準 Queue#pop(non_block=false) のインターフェースに意図的に合わせた実装のため、
33
+ # キーワード引数化すると標準 Queue との互換性が崩れる。
34
+ Style/OptionalBooleanParameter:
35
+ Exclude:
36
+ - 'lib/copy_tuner_client/queue_with_timeout.rb'
37
+
38
+ # Client#download は yield 前提の実装で、ブロックを渡さないと downloaded 時に
39
+ # LocalJumpError になる。ダウンロード結果自体を検証しないテストでは空ブロックが必須。
40
+ Lint/EmptyBlock:
41
+ Exclude:
42
+ - 'spec/copy_tuner_client/client_spec.rb'
43
+
44
+ # 要判断: configuration_spec.rb の i18n/config/application は Rails gem 自体を
45
+ # stub_const('Rails', Module.new) で丸ごと偽装するための double。ActiveSupport::OrderedOptions
46
+ # は method_missing 経由でアクセサを実装するため instance_double では検証できない。
47
+ # request_sync_spec.rb の app は Rack app プロトコル(call(env) を実装する任意オブジェクト)で
48
+ # 対応する具体的なクラスが存在しないため、Duck Typing 前提の double が妥当。
49
+ RSpec/VerifiedDoubles:
50
+ Exclude:
51
+ - 'spec/copy_tuner_client/configuration_spec.rb'
52
+ - 'spec/copy_tuner_client/request_sync_spec.rb'
data/CLAUDE.md CHANGED
@@ -26,6 +26,7 @@ Rails 統合は engine.rb のイニシャライザ経由(ヘルパー/SimpleFo
26
26
  - 新機能・バグ修正ともに「テスト追加 → RED 確認 → 実装 → GREEN」のサイクルを守る
27
27
  - テストを書かずに実装を先行させない
28
28
  - **コメントは WHY のみ日本語で書く**: 識別子やコードから読み取れる WHAT は書かない
29
+ (例外: RuboCop Style/Documentation 対応でクラス/モジュール直前に追加する一行の説明コメントは、名前だけでは把握しづらいクラスの役割を一目で示す目的のため、WHAT の説明も可)
29
30
  - **テストの `describe` / `context` / `it` の説明文は日本語で書く**: 識別子(クラス名・メソッド名など)はそのまま
30
31
 
31
32
  ## Gotchas
data/Gemfile CHANGED
@@ -2,4 +2,5 @@ source 'http://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ gem 'bundler-skills'
5
6
  gem 'sgcop', github: 'SonicGarden/sgcop'
@@ -21,9 +21,9 @@ Gem::Specification.new do |s|
21
21
  s.files = `git ls-files`.split("\n")
22
22
  s.homepage = 'https://github.com/SonicGarden/copy-tuner-ruby-client'
23
23
 
24
- s.metadata["homepage_uri"] = s.homepage
25
- s.metadata["source_code_uri"] = "https://github.com/SonicGarden/copy-tuner-ruby-client"
26
- s.metadata["changelog_uri"] = "https://github.com/SonicGarden/copy-tuner-ruby-client/releases"
24
+ s.metadata['homepage_uri'] = s.homepage
25
+ s.metadata['source_code_uri'] = 'https://github.com/SonicGarden/copy-tuner-ruby-client'
26
+ s.metadata['changelog_uri'] = 'https://github.com/SonicGarden/copy-tuner-ruby-client/releases'
27
27
  s.metadata['rubygems_mfa_required'] = 'true'
28
28
 
29
29
  s.name = 'copy_tuner_client'
data/gemfiles/8.0.gemfile CHANGED
@@ -1,5 +1,5 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
 
3
- gem "rails", "~> 8.0"
3
+ gem 'rails', '~> 8.0'
4
4
 
5
- gemspec :path => "../"
5
+ gemspec path: '../'
data/gemfiles/8.1.gemfile CHANGED
@@ -1,5 +1,5 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
 
3
- gem "rails", "~> 8.1"
3
+ gem 'rails', '~> 8.1'
4
4
 
5
- gemspec :path => "../"
5
+ gemspec path: '../'
@@ -1,5 +1,5 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
 
3
- gem "rails", github: "rails/rails", branch: "main"
3
+ gem 'rails', github: 'rails/rails', branch: 'main'
4
4
 
5
- gemspec :path => "../"
5
+ gemspec path: '../'
@@ -23,7 +23,7 @@ module CopyTunerClient
23
23
  @local_first_key_regexp = options[:local_first_key_regexp]
24
24
  @upload_disabled = options[:upload_disabled]
25
25
  @ignored_keys = options.fetch(:ignored_keys, [])
26
- @ignored_key_handler = options.fetch(:ignored_key_handler, -> (e) { raise e })
26
+ @ignored_key_handler = options.fetch(:ignored_key_handler, ->(e) { raise e })
27
27
  @locales = Array(options[:locales]).map(&:to_s)
28
28
  # mutable states
29
29
  @blurbs = {}
@@ -48,18 +48,14 @@ module CopyTunerClient
48
48
  # @param key [String] the key of the blurb to update
49
49
  # @param value [String] the new contents of the blurb
50
50
  def []=(key, value)
51
- return unless key.include?('.')
52
- return if @locales.present? && !@locales.member?(key.split('.').first)
53
- return if @upload_disabled
51
+ return if skip_upload?(key)
54
52
 
55
53
  # NOTE: config/locales以下のファイルに除外キーが残っていた場合の対応
56
54
  key_without_locale = key.split('.')[1..].join('.')
57
55
  # NOTE: local_first キー(組み込みの Rails number.*.format + ユーザー設定)は copy_tuner と完全分離するためアップロードしない
58
56
  return if local_first_key?(key_without_locale)
59
57
 
60
- if @ignored_keys.include?(key_without_locale)
61
- @ignored_key_handler.call(IgnoredKey.new("Ignored key: #{key_without_locale}"))
62
- end
58
+ handle_ignored_key(key_without_locale)
63
59
 
64
60
  lock do
65
61
  return if @blank_keys.member?(key) || @blurbs.key?(key)
@@ -100,17 +96,16 @@ module CopyTunerClient
100
96
 
101
97
  logger.info 'Waiting for first download'
102
98
 
103
- if logger.respond_to? :flush
104
- logger.flush
105
- end
99
+ logger.flush if logger.respond_to?(:flush)
106
100
 
107
- sleep 0.1 while pending?
101
+ sleep(0.1) while pending?
108
102
  end
109
103
 
110
104
  def flush
111
- res = with_queued_changes do |queued|
112
- client.upload queued
113
- end
105
+ res =
106
+ with_queued_changes do |queued|
107
+ client.upload(queued)
108
+ end
114
109
 
115
110
  @last_uploaded_at = Time.now.utc
116
111
 
@@ -122,15 +117,16 @@ module CopyTunerClient
122
117
  def download
123
118
  @status = STATUS_PENDING unless ready?
124
119
 
125
- res = client.download(cache_fallback: pending?) do |downloaded_blurbs|
126
- blank_keys = Set.new
127
- blurbs = {}
128
- downloaded_blurbs.each { |key, value| value == '' ? blank_keys << key : blurbs[key] = value }
129
- lock do
130
- @blank_keys = blank_keys
131
- @blurbs = blurbs
120
+ res =
121
+ client.download(cache_fallback: pending?) do |downloaded_blurbs|
122
+ blank_keys = Set.new
123
+ blurbs = {}
124
+ downloaded_blurbs.each { |key, value| value == '' ? blank_keys << key : blurbs[key] = value }
125
+ lock do
126
+ @blank_keys = blank_keys
127
+ @blurbs = blurbs
128
+ end
132
129
  end
133
- end
134
130
 
135
131
  @last_downloaded_at = Time.now.utc
136
132
  @status = STATUS_READY unless ready?
@@ -163,6 +159,13 @@ module CopyTunerClient
163
159
 
164
160
  attr_reader :client, :logger
165
161
 
162
+ def skip_upload?(key)
163
+ return true unless key.include?('.')
164
+ return true if @locales.present? && !@locales.member?(key.split('.').first)
165
+
166
+ @upload_disabled
167
+ end
168
+
166
169
  # NOTE: 組み込みの Rails number.*.format キーは lookup 経路(Configuration#local_first_key?)と
167
170
  # アップロード抑止経路(ここ)で同じ判定を共有する。判定本体は Configuration に集約し付け忘れの穴を防ぐ。
168
171
  def local_first_key?(key_without_locale)
@@ -171,6 +174,12 @@ module CopyTunerClient
171
174
  @local_first_key_regexp && key_without_locale.match?(@local_first_key_regexp)
172
175
  end
173
176
 
177
+ def handle_ignored_key(key_without_locale)
178
+ return unless @ignored_keys.include?(key_without_locale)
179
+
180
+ @ignored_key_handler.call(IgnoredKey.new("Ignored key: #{key_without_locale}"))
181
+ end
182
+
174
183
  def with_queued_changes
175
184
  changes_to_push = nil
176
185
 
@@ -181,9 +190,7 @@ module CopyTunerClient
181
190
  end
182
191
  end
183
192
 
184
- if changes_to_push
185
- yield nil_value_to_empty(changes_to_push)
186
- end
193
+ yield(nil_value_to_empty(changes_to_push)) if changes_to_push
187
194
  end
188
195
 
189
196
  def nil_value_to_empty(hash)
@@ -197,8 +204,8 @@ module CopyTunerClient
197
204
  hash
198
205
  end
199
206
 
200
- def lock(&block)
201
- @mutex.synchronize &block
207
+ def lock(&)
208
+ @mutex.synchronize(&)
202
209
  end
203
210
  end
204
211
  end
@@ -9,14 +9,16 @@ module CopyTunerClient
9
9
  #
10
10
  # A client is usually instantiated when {Configuration#apply} is called, and
11
11
  # the application will not need to interact with it directly.
12
- class Client
12
+ class Client # rubocop:disable Metrics/ClassLength
13
13
  # These errors will be rescued when connecting CopyTuner.
14
- HTTP_ERRORS = [Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
15
- Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
16
- Net::ProtocolError, SocketError, OpenSSL::SSL::SSLError,
17
- Errno::ECONNREFUSED]
14
+ HTTP_ERRORS = [
15
+ Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
16
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
17
+ Net::ProtocolError, SocketError, OpenSSL::SSL::SSLError,
18
+ Errno::ECONNREFUSED
19
+ ].freeze
18
20
 
19
- USER_AGENT = "copy_tuner_client #{CopyTunerClient::VERSION}"
21
+ USER_AGENT = "copy_tuner_client #{CopyTunerClient::VERSION}".freeze
20
22
 
21
23
  # ETags を外部から取得可能にする
22
24
  # @return [String, nil] 現在のETag値
@@ -36,9 +38,11 @@ module CopyTunerClient
36
38
  @etag = nil
37
39
  @downloaded_blurbs = {}
38
40
 
39
- [:api_key, :host, :port, :public, :http_read_timeout,
40
- :http_open_timeout, :secure, :logger, :ca_file, :s3_host, :download_cache_dir].each do |option|
41
- instance_variable_set "@#{option}", options[option]
41
+ %i[
42
+ api_key host port public http_read_timeout
43
+ http_open_timeout secure logger ca_file s3_host download_cache_dir
44
+ ].each do |option|
45
+ instance_variable_set("@#{option}", options[option])
42
46
  end
43
47
 
44
48
  @download_cache_dir.mkpath
@@ -59,19 +63,19 @@ module CopyTunerClient
59
63
  connect(s3_host) do |http|
60
64
  request = Net::HTTP::Get.new(uri(download_resource))
61
65
  request['If-None-Match'] = @etag
62
- log 'Start downloading translations'
66
+ log('Start downloading translations')
63
67
  t = Time.now
64
68
  response = http.request(request)
65
69
  t_ms = ((Time.now - t) * 1000).to_i
66
70
  downloaded = check(response)
67
71
  if downloaded
68
72
  # NOTE: Net::HTTPではgzipが透過的に扱われるため正確なファイルサイズや速度をログに出すのは難しい
69
- log "Downloaded translations (#{t_ms}ms)"
73
+ log("Downloaded translations (#{t_ms}ms)")
70
74
  @downloaded_blurbs = JSON.parse(response.body)
71
75
  @etag = response['ETag']
72
76
  last_download_path.write(JSON.pretty_generate(etag: @etag, downloaded_blurbs: @downloaded_blurbs))
73
77
  else
74
- log "No new translations (#{t_ms}ms)"
78
+ log("No new translations (#{t_ms}ms)")
75
79
  end
76
80
 
77
81
  yield(@downloaded_blurbs) if downloaded || cache_fallback
@@ -83,9 +87,10 @@ module CopyTunerClient
83
87
  # @raise [ConnectionError] if the connection fails
84
88
  def upload(data)
85
89
  connect(host) do |http|
86
- response = http.post(uri('draft_blurbs'), data.to_json, 'Content-Type' => 'application/json', 'User-Agent' => USER_AGENT)
87
- check response
88
- log 'Uploaded missing translations'
90
+ headers = { 'Content-Type' => 'application/json', 'User-Agent' => USER_AGENT }
91
+ response = http.post(uri('draft_blurbs'), data.to_json, headers)
92
+ check(response)
93
+ log('Uploaded missing translations')
89
94
  end
90
95
  end
91
96
 
@@ -94,15 +99,22 @@ module CopyTunerClient
94
99
  def deploy
95
100
  connect(host) do |http|
96
101
  response = http.post(uri('deploys'), '', 'User-Agent' => USER_AGENT)
97
- check response
98
- log 'Deployed'
102
+ check(response)
103
+ log('Deployed')
99
104
  end
100
105
  end
101
106
 
102
107
  private
103
108
 
104
- attr_reader :host, :port, :api_key, :http_read_timeout,
105
- :http_open_timeout, :secure, :logger, :ca_file, :s3_host
109
+ attr_reader :host,
110
+ :port,
111
+ :api_key,
112
+ :http_read_timeout,
113
+ :http_open_timeout,
114
+ :secure,
115
+ :logger,
116
+ :ca_file,
117
+ :s3_host
106
118
 
107
119
  def public?
108
120
  @public
@@ -130,7 +142,7 @@ module CopyTunerClient
130
142
  cache = JSON.parse(pathname.read.to_s).transform_keys(&:to_sym)
131
143
  @etag = cache[:etag]
132
144
  @downloaded_blurbs = cache[:downloaded_blurbs]
133
- log "Loaded cache data from #{pathname}"
145
+ log("Loaded cache data from #{pathname}")
134
146
  rescue JSON::JSONError, Errno::ENOENT, Errno::ENOTDIR
135
147
  nil
136
148
  end
@@ -144,9 +156,9 @@ module CopyTunerClient
144
156
  http.ca_file = ca_file
145
157
 
146
158
  begin
147
- yield http
148
- rescue *HTTP_ERRORS => exception
149
- raise ConnectionError, "#{exception.class.name}: #{exception.message}"
159
+ yield(http)
160
+ rescue *HTTP_ERRORS => e
161
+ raise ConnectionError, "#{e.class.name}: #{e.message}"
150
162
  end
151
163
  end
152
164
 
@@ -12,14 +12,16 @@ module CopyTunerClient
12
12
  # Used to set up and modify settings for the client.
13
13
  class Configuration # rubocop:disable Metrics/ClassLength
14
14
  # These options will be present in the Hash returned by {#to_hash}.
15
- OPTIONS = %i[api_key development_environments environment_name host
16
- http_open_timeout http_read_timeout client_name client_url
17
- client_version port protocol proxy_host proxy_pass
18
- proxy_port proxy_user secure polling_delay sync_interval
19
- sync_interval_staging sync_ignore_path_regex logger
20
- framework middleware disable_middleware disable_test_translation
21
- ca_file local_first_key_regexp s3_host locales ignored_keys ignored_key_handler
22
- download_cache_dir].freeze
15
+ OPTIONS = %i[
16
+ api_key development_environments environment_name host
17
+ http_open_timeout http_read_timeout client_name client_url
18
+ client_version port protocol proxy_host proxy_pass
19
+ proxy_port proxy_user secure polling_delay sync_interval
20
+ sync_interval_staging sync_ignore_path_regex logger
21
+ framework middleware disable_middleware disable_test_translation
22
+ ca_file local_first_key_regexp s3_host locales ignored_keys ignored_key_handler
23
+ download_cache_dir
24
+ ].freeze
23
25
 
24
26
  # NOTE: Rails 標準ロケールで非文字列値(precision: Integer, significant: Boolean,
25
27
  # strip_insignificant_zeros: Boolean)を含むのは number.*.format 配下のみ。store_item が文字列しか
@@ -200,7 +202,7 @@ module CopyTunerClient
200
202
  base_options = { public: public?, upload_disabled: upload_disabled? }
201
203
 
202
204
  OPTIONS.inject(base_options) do |hash, option|
203
- hash.merge option.to_sym => public_send(option)
205
+ hash.merge(option.to_sym => public_send(option))
204
206
  end
205
207
  end
206
208
 
@@ -209,7 +211,7 @@ module CopyTunerClient
209
211
  # @param [Hash] hash A set of configuration options that will take precedence over the defaults
210
212
  # @return [Hash] the merged configuration hash
211
213
  def merge(hash)
212
- to_hash.merge hash
214
+ to_hash.merge(hash)
213
215
  end
214
216
 
215
217
  # Determines if the published or draft content will be used
@@ -222,7 +224,7 @@ module CopyTunerClient
222
224
  # Determines if the content will be editable
223
225
  # @return [Boolean] Returns +true+ if in a development environment, +false+ otherwise.
224
226
  def development?
225
- development_environments.include? environment_name
227
+ development_environments.include?(environment_name)
226
228
  end
227
229
 
228
230
  def enable_middleware?
@@ -252,15 +254,11 @@ module CopyTunerClient
252
254
  # This creates the {I18nBackend} and puts them together.
253
255
  #
254
256
  # When {#test?} returns +false+, the poller will be started.
255
- def apply # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
257
+ def apply # rubocop:disable Metrics/AbcSize
256
258
  # NOTE: project_id は必須。未設定なら apply 時点で明示的に失敗させる
257
259
  validate_project_id!
258
260
 
259
- self.locales ||= self.locales = if defined?(::Rails)
260
- ::Rails.application.config.i18n.available_locales.presence || Array(::Rails.application.config.i18n.default_locale)
261
- else
262
- [:en]
263
- end
261
+ self.locales ||= default_locales
264
262
 
265
263
  self.client ||= Client.new(to_hash)
266
264
  self.cache ||= Cache.new(client, to_hash)
@@ -268,31 +266,12 @@ module CopyTunerClient
268
266
  process_guard = ProcessGuard.new(cache, @poller, to_hash)
269
267
  I18n.backend = I18nBackend.new(cache)
270
268
 
271
- if enable_middleware?
272
- logger.info 'Using copytuner sync middleware'
273
- request_sync_options = { poller: @poller, cache:, interval: sync_interval, ignore_regex: sync_ignore_path_regex }
274
- if middleware_position.is_a?(Hash) && middleware_position[:before]
275
- middleware.insert_before middleware_position[:before], RequestSync, request_sync_options
276
- middleware.insert_before middleware_position[:before], CopyTunerClient::CopyrayMiddleware
277
- elsif middleware_position.is_a?(Hash) && middleware_position[:after]
278
- middleware.insert_after middleware_position[:after], RequestSync, request_sync_options
279
- middleware.insert_after middleware_position[:after], CopyTunerClient::CopyrayMiddleware
280
- else
281
- middleware.use RequestSync, request_sync_options
282
- middleware.use CopyTunerClient::CopyrayMiddleware
283
- end
284
- else
285
- logger.info '[[[Warn]]] Not using copytuner sync middleware' unless middleware
286
- end
269
+ setup_middleware
287
270
 
288
271
  @applied = true
289
- logger.info "Client #{VERSION} ready (s3_download)"
290
- logger.info "Environment Info: #{environment_info}"
291
- logger.info "Available locales: #{self.locales.join(' ')}"
272
+ log_applied
292
273
 
293
- unless test?
294
- process_guard.start
295
- end
274
+ process_guard.start unless test?
296
275
 
297
276
  unless test? && disable_test_translation
298
277
  logger.info 'Download translation now'
@@ -348,7 +327,7 @@ module CopyTunerClient
348
327
  validate_project_id!
349
328
 
350
329
  path = "/projects/#{project_id}"
351
- URI::Generic.build(scheme: self.protocol, host: self.host, port: self.port.to_i, path:).to_s
330
+ URI::Generic.build(scheme: protocol, host:, port: port.to_i, path:).to_s
352
331
  end
353
332
 
354
333
  # locale を除いたキーが local_first_key_regexp にマッチするかを返す。
@@ -369,6 +348,49 @@ module CopyTunerClient
369
348
 
370
349
  private
371
350
 
351
+ def default_locales
352
+ if defined?(::Rails)
353
+ rails_i18n = ::Rails.application.config.i18n
354
+ rails_i18n.available_locales.presence || Array(rails_i18n.default_locale)
355
+ else
356
+ [:en]
357
+ end
358
+ end
359
+
360
+ def setup_middleware
361
+ if enable_middleware?
362
+ logger.info 'Using copytuner sync middleware'
363
+ insert_middleware
364
+ else
365
+ logger.info '[[[Warn]]] Not using copytuner sync middleware' unless middleware
366
+ end
367
+ end
368
+
369
+ def log_applied
370
+ logger.info "Client #{VERSION} ready (s3_download)"
371
+ logger.info "Environment Info: #{environment_info}"
372
+ logger.info "Available locales: #{locales.join(' ')}"
373
+ end
374
+
375
+ def insert_middleware # rubocop:disable Metrics/AbcSize
376
+ request_sync_options = {
377
+ poller: @poller,
378
+ cache:,
379
+ interval: sync_interval,
380
+ ignore_regex: sync_ignore_path_regex,
381
+ }
382
+ if middleware_position.is_a?(Hash) && middleware_position[:before]
383
+ middleware.insert_before(middleware_position[:before], RequestSync, request_sync_options)
384
+ middleware.insert_before(middleware_position[:before], CopyTunerClient::CopyrayMiddleware)
385
+ elsif middleware_position.is_a?(Hash) && middleware_position[:after]
386
+ middleware.insert_after(middleware_position[:after], RequestSync, request_sync_options)
387
+ middleware.insert_after(middleware_position[:after], CopyTunerClient::CopyrayMiddleware)
388
+ else
389
+ middleware.use(RequestSync, request_sync_options)
390
+ middleware.use(CopyTunerClient::CopyrayMiddleware)
391
+ end
392
+ end
393
+
372
394
  # project_id は必須。未設定なら明示的に失敗させる。
373
395
  # apply(起動時の全体検証)と project_url(apply を経ない経路へのセーフネット)の両方から呼ぶ。
374
396
  def validate_project_id!
@@ -38,7 +38,7 @@ module CopyTunerClient
38
38
  # NOTE: 閾値超は Nokogiri を通さず可視トークン除去のみ。skipped=true で編集導線を諦めた旨を伝える。
39
39
  return [strip_markers(scannable), true] if scannable.bytesize > MAX_REWRITE_BYTESIZE
40
40
 
41
- [rewrite_with_nokogiri(scannable, fragment: fragment), false]
41
+ [rewrite_with_nokogiri(scannable, fragment:), false]
42
42
  rescue StandardError => e
43
43
  # NOTE: Copyray は開発支援機能なので、壊れた HTML 等で Nokogiri 処理が落ちても
44
44
  # ページを 500 にしない。data-copyray-key 付与(編集導線)は諦め、最低限可視トークンだけ除去する。
@@ -1,6 +1,7 @@
1
1
  require 'copy_tuner_client/copyray/marker'
2
2
 
3
3
  module CopyTunerClient
4
+ # 翻訳テキストにオーバーレイ編集用のマーカートークンを埋め込む
4
5
  class Copyray
5
6
  # This:
6
7
  # message