singleton-client 0.7.3 → 0.7.6

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -28
  3. data/lib/sgtn-client/api/source.rb +10 -66
  4. data/lib/sgtn-client/api/t.rb +20 -18
  5. data/lib/sgtn-client/api/translation.rb +84 -115
  6. data/lib/sgtn-client/cldr/core_ext.rb +4 -1
  7. data/lib/sgtn-client/cldr/localized_date.rb +4 -1
  8. data/lib/sgtn-client/cldr/localized_datetime.rb +4 -1
  9. data/lib/sgtn-client/cldr/localized_str.rb +4 -1
  10. data/lib/sgtn-client/cldr/localized_time.rb +4 -1
  11. data/lib/sgtn-client/common/data.rb +30 -0
  12. data/lib/sgtn-client/common/single_operation.rb +34 -0
  13. data/lib/sgtn-client/core/cache.rb +15 -82
  14. data/lib/sgtn-client/core/config.rb +42 -3
  15. data/lib/sgtn-client/core/exceptions.rb +3 -0
  16. data/lib/sgtn-client/core/logging.rb +3 -1
  17. data/lib/sgtn-client/exceptions.rb +6 -0
  18. data/lib/sgtn-client/formatters/plurals/plural_formatter.rb +4 -1
  19. data/lib/sgtn-client/loader/cache.rb +71 -0
  20. data/lib/sgtn-client/loader/chain_loader.rb +49 -0
  21. data/lib/sgtn-client/loader/consts.rb +15 -0
  22. data/lib/sgtn-client/loader/loader_factory.rb +33 -0
  23. data/lib/sgtn-client/loader/local_translation.rb +49 -0
  24. data/lib/sgtn-client/loader/server.rb +94 -0
  25. data/lib/sgtn-client/loader/single_loader.rb +48 -0
  26. data/lib/sgtn-client/loader/source.rb +56 -0
  27. data/lib/sgtn-client/loader/source_comparer.rb +58 -0
  28. data/lib/sgtn-client/sgtn-client.rb +11 -2
  29. data/lib/sgtn-client/util/cache-util.rb +33 -41
  30. data/lib/sgtn-client/util/locale-util.rb +67 -28
  31. data/lib/sgtn-client/util/string-util.rb +12 -0
  32. data/lib/sgtn-client/util/validate-util.rb +5 -9
  33. data/lib/singleton-client.rb +15 -0
  34. data/lib/singleton-ruby.rb +5 -0
  35. data/lib/version.rb +2 -0
  36. metadata +43 -147
  37. data/lib/sgtn-client/core/request.rb +0 -21
  38. data/lib/sgtn-client/util/file-util.rb +0 -32
@@ -1,100 +1,33 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
1
4
  require 'date'
2
5
 
3
6
  module SgtnClient::Core
4
7
  class Cache
5
- Entry = Struct.new(:expiry, :value)
8
+ Entry = Struct.new(:expiry, :items)
6
9
 
7
10
  def self.initialize(disabled=false, opts={})
8
11
  @@opts = opts
9
- @mutex = Mutex.new
10
- SgtnClient.logger.debug "Initialize cache......"
11
- if disabled == false
12
- @@data = Hash.new
13
- SgtnClient.logger.debug "Cache is enabled!"
14
- else
15
- @@data = nil
16
- SgtnClient.logger.debug "Cache is disabled!"
17
- end
18
- end
19
-
20
- def self.keys
21
- if @@data == nil
22
- return nil
23
- end
24
- SgtnClient.logger.debug "Get cache keys"
25
- @@data.keys
12
+ SgtnClient.logger.debug "[Cache][initialize] Disable cache? #{disabled}"
13
+ @@data = Hash.new
26
14
  end
27
15
 
28
16
  def self.get(key)
29
- if @@data == nil
30
- return nil, nil
31
- end
32
- SgtnClient.logger.debug "Get cache for key: " + key
33
- invalidate(key)
34
- end
35
-
36
- def self.has(key)
37
- if @@data == nil
38
- return nil
39
- end
40
- SgtnClient.logger.debug "Check if the cache has key: #{(@@data.has_key? key)}"
41
- @@data.has_key? key
17
+ SgtnClient.logger.debug "[Cache][get]get cache for key: " + key
18
+ return @@data&.dig(key)
42
19
  end
43
20
 
44
- def self.put(key, value, ttl=nil)
45
- @mutex.synchronize do
46
- if @@data == nil || value == nil
47
- return nil
48
- end
49
- ttl ||= @@opts[:ttl]
50
- # hours from new
51
- SgtnClient.logger.debug "Put cache for key '" + key + "' with expired time at'" + (Time.now + ttl*60).to_s
52
- @@data[key] = Entry.new(Time.now + ttl*60, value)
53
- end
54
- end
55
-
56
- def self.delete(key)
57
- @mutex.synchronize do
58
- if @@data == nil
59
- return nil
60
- end
61
- SgtnClient.logger.debug "Delete cache for key: " + key
62
- @@data.delete key
63
- end
21
+ def self.put(key, items, ttl=nil)
22
+ ttl ||= @@opts[:ttl]
23
+ # hours from new
24
+ SgtnClient.logger.debug "[Cache][put]put cache for key '" + key + "' with expired time at'" + (Time.now + ttl*60).to_s
25
+ @@data[key] = Entry.new(Time.now + ttl*60, items)
64
26
  end
65
27
 
66
28
  def self.clear
67
- @mutex.synchronize do
68
- if @@data == nil
69
- return nil
70
- end
71
- SgtnClient.logger.debug "Clear cache!"
72
- @@data = Hash.new
73
- end
74
- end
75
-
76
- def self.invalidate(key)
77
- @mutex.synchronize do
78
- if @@data == nil
79
- return nil, nil
80
- end
81
- SgtnClient.logger.debug "Invalidating expired cache......"
82
- now = Time.now
83
- if has(key)
84
- v = @@data[key]
85
- expired = false
86
- SgtnClient.logger.debug "Checking cache: key=#{key}, expiredtime=#{v[:expiry]}, now=#{now}, expired=#{(v[:expiry] < now)}"
87
- if v[:expiry] < now
88
- SgtnClient.logger.debug "Before deleting the cache: data=#{@@data}"
89
- @@data.delete(key)
90
- SgtnClient.logger.debug "After deleting the cache: data=#{@@data}"
91
- expired = true
92
- end
93
- return expired, v[:value]
94
- else
95
- return nil, nil
96
- end
97
- end
29
+ SgtnClient.logger.debug "[Cache][clear]clear cache!"
30
+ @@data = Hash.new
98
31
  end
99
32
  end
100
33
 
@@ -1,8 +1,17 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
1
4
  require 'erb'
2
5
  require 'yaml'
6
+ require 'observer'
3
7
 
4
8
  module SgtnClient
5
9
  #include Exceptions
10
+
11
+ module TranslationLoader
12
+ autoload :LoaderFactory, 'sgtn-client/loader/loader_factory'
13
+ end
14
+
6
15
  module Configuration
7
16
 
8
17
  def config
@@ -31,6 +40,7 @@ module SgtnClient
31
40
 
32
41
 
33
42
  class Config
43
+ extend Observable
34
44
 
35
45
  attr_accessor :username, :password, :signature, :app_id, :cert_path,
36
46
  :token, :token_secret, :subject,
@@ -39,7 +49,7 @@ module SgtnClient
39
49
  :mode, :endpoint, :merchant_endpoint, :platform_endpoint, :ipn_endpoint,
40
50
  :rest_endpoint, :rest_token_endpoint, :client_id, :client_secret,
41
51
  :openid_endpoint, :openid_redirect_uri, :openid_client_id, :openid_client_secret,
42
- :verbose_logging, :product_name, :version, :vip_server, :bundle_mode,
52
+ :verbose_logging, :product_name, :version, :vip_server,
43
53
  :translation_bundle, :source_bundle, :cache_expiry_period, :disable_cache, :default_language
44
54
 
45
55
 
@@ -149,6 +159,36 @@ module SgtnClient
149
159
  Logging.logger
150
160
  end
151
161
 
162
+
163
+ def loader
164
+ @loader ||= begin
165
+ config = SgtnClient::Config.configurations[SgtnClient::Config.default_environment]
166
+ SgtnClient::TranslationLoader::LoaderFactory.create(config)
167
+ end
168
+ end
169
+
170
+ def available_bundles
171
+ loader.available_bundles
172
+ rescue StandardError => e
173
+ SgtnClient.logger.error 'failed to get available bundles'
174
+ SgtnClient.logger.error e
175
+ Set.new
176
+ end
177
+
178
+ def available_locales
179
+ bundles = available_bundles
180
+ return Set.new if bundles.nil? || bundles.empty?
181
+
182
+ unless bundles.respond_to?(:locales)
183
+ def bundles.locales
184
+ @locales ||= reduce(Set.new) { |locales, id| locales << id.locale }
185
+ end
186
+ changed
187
+ notify_observers(:available_locales)
188
+ end
189
+ bundles.locales
190
+ end
191
+
152
192
  private
153
193
  # Read configurations from the given file name
154
194
  # === Arguments
@@ -158,9 +198,8 @@ module SgtnClient
158
198
  erb.filename = file_name
159
199
  YAML.load(erb.result)
160
200
  end
161
-
162
201
 
163
202
  end
164
203
  end
165
204
 
166
- end
205
+ end
@@ -1,3 +1,6 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
1
4
  require 'multi_json'
2
5
  require 'pp'
3
6
 
@@ -1,3 +1,6 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
1
4
  require 'logger'
2
5
 
3
6
  module SgtnClient
@@ -47,4 +50,3 @@ module SgtnClient
47
50
  end
48
51
 
49
52
  end
50
-
@@ -0,0 +1,6 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
4
+ module SgtnClient
5
+ class SingletonError < StandardError; end
6
+ end
@@ -1,3 +1,6 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
1
4
  require 'json'
2
5
 
3
6
  module SgtnClient
@@ -35,4 +38,4 @@ module SgtnClient
35
38
  end
36
39
  end
37
40
  end
38
- end
41
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2022 VMware, Inc.
4
+ # SPDX-License-Identifier: EPL-2.0
5
+
6
+ module SgtnClient
7
+ autoload :CacheUtil, 'sgtn-client/util/cache-util'
8
+
9
+ module TranslationLoader
10
+ autoload :CONSTS, 'sgtn-client/loader/consts'
11
+
12
+ module Cache # :nodoc:
13
+ # get from cache, return expired data immediately
14
+ def get_bundle(component, locale)
15
+ SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}] component=#{component}, locale=#{locale}"
16
+
17
+ key = SgtnClient::CacheUtil.get_cachekey(component, locale)
18
+ cache_item = SgtnClient::CacheUtil.get_cache(key)
19
+ if cache_item
20
+ if SgtnClient::CacheUtil.is_expired(cache_item)
21
+ Thread.new do # TODO: Use one thread # refresh in background
22
+ begin
23
+ load_bundle(component, locale)
24
+ rescue StandardError => e
25
+ SgtnClient.logger.error "an error occured while loading bundle: component=#{component}, locale=#{locale}"
26
+ SgtnClient.logger.error e
27
+ end
28
+ end
29
+ end
30
+ return cache_item.dig(:items)
31
+ end
32
+
33
+ load_bundle(component, locale) # refresh synchronously if not in cache
34
+ end
35
+
36
+ # load and save to cache
37
+ def load_bundle(component, locale)
38
+ SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}] component=#{component}, locale=#{locale}"
39
+
40
+ key = SgtnClient::CacheUtil.get_cachekey(component, locale)
41
+ item = super
42
+ SgtnClient::CacheUtil.write_cache(key, item) if item
43
+ item
44
+ end
45
+
46
+ def available_bundles
47
+ SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}]"
48
+
49
+ cache_item = SgtnClient::CacheUtil.get_cache(CONSTS::AVAILABLE_BUNDLES_KEY)
50
+ if cache_item
51
+ if SgtnClient::CacheUtil.is_expired(cache_item)
52
+ Thread.new do # TODO: Use one thread
53
+ begin
54
+ item = super
55
+ SgtnClient::CacheUtil.write_cache(CONSTS::AVAILABLE_BUNDLES_KEY, item) if item
56
+ rescue StandardError => e
57
+ SgtnClient.logger.error 'an error occured while loading available bundles.'
58
+ SgtnClient.logger.error e
59
+ end
60
+ end
61
+ end
62
+ return cache_item.dig(:items)
63
+ end
64
+
65
+ item = super
66
+ SgtnClient::CacheUtil.write_cache(CONSTS::AVAILABLE_BUNDLES_KEY, item) if item
67
+ item
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,49 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
4
+ module SgtnClient
5
+ module TranslationLoader
6
+ class Chain
7
+ attr_accessor :loaders
8
+
9
+ def initialize(*loaders)
10
+ self.loaders = loaders
11
+ end
12
+
13
+ def load_bundle(component, locale)
14
+ exception = nil
15
+
16
+ loaders.each do |loader|
17
+ begin
18
+ bundle = loader.load_bundle(component, locale)
19
+ return bundle if bundle
20
+ rescue StandardError => e
21
+ exception = e
22
+ SgtnClient.logger.error "[#{__FILE__}][#{__callee__}] {component: #{component},locale: #{locale}}, failed on #{loader.class}: #{e}"
23
+ end
24
+ end
25
+
26
+ raise exception || SgtnClient::SingletonError.new("can't load component: #{component}, locale: #{locale}")
27
+ end
28
+
29
+ def available_bundles
30
+ exception = nil
31
+ total_data = Set.new
32
+
33
+ loaders.each do |loader|
34
+ begin
35
+ item = loader.available_bundles
36
+ total_data += item
37
+ rescue StandardError => e
38
+ exception = e
39
+ SgtnClient.logger.error "[#{__FILE__}][#{__callee__}] failed on #{loader.class}: #{e}"
40
+ end
41
+ end
42
+
43
+ raise exception if total_data.empty? && exception
44
+
45
+ total_data
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2022 VMware, Inc.
4
+ # SPDX-License-Identifier: EPL-2.0
5
+
6
+ module SgtnClient
7
+ module TranslationLoader
8
+ module CONSTS
9
+ OLD_SOURCE_LOCALE = 'old_source'
10
+ REAL_SOURCE_LOCALE = 'latest'
11
+
12
+ AVAILABLE_BUNDLES_KEY = 'available_bundles'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
4
+ module SgtnClient
5
+ module TranslationLoader
6
+ autoload :Source, 'sgtn-client/loader/source'
7
+ autoload :SgtnServer, 'sgtn-client/loader/server'
8
+ autoload :LocalTranslation, 'sgtn-client/loader/local_translation'
9
+ autoload :Chain, 'sgtn-client/loader/chain_loader'
10
+ autoload :SourceComparer, 'sgtn-client/loader/source_comparer'
11
+ autoload :SingleLoader, 'sgtn-client/loader/single_loader'
12
+ autoload :Cache, 'sgtn-client/loader/cache'
13
+
14
+ module LoaderFactory
15
+ def self.create(config)
16
+ SgtnClient.logger.info "[#{method(__callee__).owner}.#{__callee__}] config=#{config}"
17
+
18
+ loaders = []
19
+ loaders << Source.new(config) if config['source_bundle']
20
+ loaders << SgtnServer.new(config) if config['vip_server']
21
+ loaders << LocalTranslation.new(config) if config['translation_bundle']
22
+ raise SgtnClient::SingletonError, 'no translation is available!' if loaders.empty?
23
+
24
+ chain_loader = Class.new(Chain)
25
+ chain_loader.include SourceComparer
26
+ chain_loader.include SingleLoader
27
+ chain_loader.include Cache
28
+
29
+ chain_loader.new(*loaders)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
4
+ require 'json'
5
+ require 'pathname'
6
+
7
+ module SgtnClient
8
+ module Common
9
+ autoload :BundleID, 'sgtn-client/common/data'
10
+ end
11
+
12
+ module TranslationLoader
13
+ autoload :CONSTS, 'sgtn-client/loader/consts'
14
+
15
+ class LocalTranslation
16
+ BUNDLE_PREFIX = 'messages_'.freeze
17
+ BUNDLE_SUFFIX = '.json'.freeze
18
+
19
+ def initialize(config)
20
+ @base_path = Pathname.new(config['translation_bundle']) + config['product_name'] + config['version'].to_s
21
+ end
22
+
23
+ def load_bundle(component, locale)
24
+ SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}] component=#{component}, locale=#{locale}"
25
+
26
+ file_name = BUNDLE_PREFIX + locale + BUNDLE_SUFFIX
27
+ file_path = @base_path + component + file_name
28
+
29
+ bundle_data = JSON.parse(File.read(file_path))
30
+ messages = bundle_data['messages']
31
+
32
+ raise SgtnClient::SingletonError, "no messages in local bundle file: #{file_path}." unless messages
33
+
34
+ messages
35
+ end
36
+
37
+ def available_bundles
38
+ SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}]"
39
+
40
+ @available_bundles ||= begin
41
+ @base_path.glob('*/*.json').reduce(Set.new) do |bundles, f|
42
+ locale = f.basename.to_s.sub(/\A#{BUNDLE_PREFIX}/i, '').sub(/#{BUNDLE_SUFFIX}\z/i, '')
43
+ bundles.add Common::BundleID.new(f.parent.basename.to_s, locale)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2022 VMware, Inc.
4
+ # SPDX-License-Identifier: EPL-2.0
5
+
6
+ require 'faraday'
7
+ require 'faraday_middleware'
8
+
9
+ module SgtnClient
10
+ module Common
11
+ autoload :BundleID, 'sgtn-client/common/data'
12
+ end
13
+
14
+ module TranslationLoader
15
+ autoload :CONSTS, 'sgtn-client/loader/consts'
16
+
17
+ class SgtnServer
18
+ ERROR_ILLEGAL_DATA = 'server returned illegal data.'
19
+ ERROR_BUSINESS_ERROR = 'server returned business error.'
20
+
21
+ REQUEST_ARGUMENTS = { timeout: 10 }.freeze
22
+
23
+ def initialize(config)
24
+ @server_url = config['vip_server']
25
+
26
+ product_root = format('/i18n/api/v2/translation/products/%s/versions/%s', config['product_name'], config['version'])
27
+
28
+ @bundle_url = "#{product_root}/locales/%s/components/%s"
29
+ @locales_url = "#{product_root}/localelist"
30
+ @components_url = "#{product_root}/componentlist"
31
+ end
32
+
33
+ def load_bundle(component, locale)
34
+ SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}] component=#{component}, locale=#{locale}"
35
+
36
+ messages = query_server(format(@bundle_url, locale, component), ['messages'])
37
+ messages
38
+ end
39
+
40
+ def available_bundles
41
+ SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}]"
42
+
43
+ components_thread = Thread.new { available_components }
44
+ available_locales.reduce(Set.new) do |bundles, locale|
45
+ components_thread.value.reduce(bundles) do |inner_bundles, component|
46
+ inner_bundles << Common::BundleID.new(component, locale)
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def available_locales
54
+ query_server(@locales_url, ['locales'])
55
+ end
56
+
57
+ def available_components
58
+ query_server(@components_url, ['components'])
59
+ end
60
+
61
+ def query_server(url, path_to_data = [], queries = nil, headers = nil)
62
+ conn = Faraday.new(@server_url, request: REQUEST_ARGUMENTS) do |f|
63
+ f.response :json # decode response bodies as JSON
64
+ f.use :gzip
65
+ f.response :raise_error
66
+ f.response :logger, SgtnClient.logger, { log_level: :debug }
67
+ end
68
+ resp = conn.get(url, queries, headers)
69
+
70
+ process_business_error(resp.body)
71
+ extract_data(resp.body, path_to_data)
72
+ end
73
+
74
+ def extract_data(parsedbody, path_to_data)
75
+ data = parsedbody.dig('data', *path_to_data)
76
+ raise SgtnClient::SingletonError, "no expected data in response. Body is: #{parsedbody}" unless data
77
+
78
+ data
79
+ end
80
+
81
+ def process_business_error(parsedbody)
82
+ b_code = parsedbody.dig('response', 'code')
83
+ unless b_code >= 200 && b_code < 300 || b_code >= 600 && b_code < 700
84
+ raise SgtnClient::SingletonError, "#{ERROR_BUSINESS_ERROR} #{parsedbody['response']}"
85
+ end
86
+
87
+ # 600 means a successful response, 6xx means partial successful.
88
+ SgtnClient.logger.warn "#{ERROR_BUSINESS_ERROR} #{parsedbody['response']}" if b_code > 600
89
+ rescue TypeError, ArgumentError, NoMethodError => e
90
+ raise SgtnClient::SingletonError, "#{ERROR_ILLEGAL_DATA} #{e}. Body is: #{parsedbody}"
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,48 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
4
+ module SgtnClient
5
+ autoload :SingleOperation, 'sgtn-client/common/single_operation'
6
+ autoload :CacheUtil, 'sgtn-client/util/cache-util'
7
+
8
+ module TranslationLoader
9
+ autoload :CONSTS, 'sgtn-client/loader/consts'
10
+
11
+ module SingleLoader
12
+ def load_bundle(component, locale)
13
+ SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}] component=#{component}, locale=#{locale}"
14
+
15
+ @single_bundle_loader ||= single_loader { |c, l| super(c, l) }
16
+ id = CacheUtil.get_cachekey(component, locale)
17
+ @single_bundle_loader.operate(id, component, locale)&.value
18
+ end
19
+
20
+ def available_bundles
21
+ SgtnClient.logger.debug "[#{__FILE__}][#{__callee__}]"
22
+
23
+ @single_available_bundles_loader ||= single_loader { super }
24
+ @single_available_bundles_loader.operate(CONSTS::AVAILABLE_BUNDLES_KEY)&.value
25
+ end
26
+
27
+ private
28
+
29
+ def single_loader(&block)
30
+ loader = nil
31
+ none_alive = proc { |_, thread| thread.nil? }
32
+ creator = proc do |id, _, *args|
33
+ Thread.new do
34
+ SgtnClient.logger.debug "start single loading #{id}"
35
+ begin
36
+ block.call(*args)
37
+ ensure
38
+ # delete thread from hash after finish
39
+ loader.remove_object(id)
40
+ end
41
+ end
42
+ end
43
+
44
+ loader = SgtnClient::SingleOperation.new(none_alive, &creator)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
4
+ require 'pathname'
5
+ require 'yaml'
6
+
7
+ module SgtnClient
8
+ module Common
9
+ autoload :BundleID, 'sgtn-client/common/data'
10
+ end
11
+
12
+ module TranslationLoader
13
+ autoload :CONSTS, 'sgtn-client/loader/consts'
14
+
15
+ class Source
16
+ def initialize(config)
17
+ @source_bundle_path = Pathname.new(config['source_bundle'])
18
+ end
19
+
20
+ def load_bundle(component, locale = nil)
21
+ return if locale && locale != CONSTS::REAL_SOURCE_LOCALE # return when NOT querying source
22
+
23
+ SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}] component=#{component}"
24
+
25
+ total_messages = {}
26
+
27
+ (@source_bundle_path + component).glob('**/*.{yml, yaml}') do |f|
28
+ bundle = YAML.load(File.read(f))
29
+ messages = bundle&.first&.last # TODO: Warn about inconsistent source locale
30
+ if messages.is_a?(Hash)
31
+ total_messages.merge!(messages)
32
+ else
33
+ SgtnClient.logger.error "[#{method(__callee__).owner}.#{__callee__}] invalid bundle data in #{f}"
34
+ end
35
+ end
36
+
37
+ raise SgtnClient::SingletonError, "no local source messages for component #{component}" if total_messages.empty?
38
+
39
+ total_messages
40
+ end
41
+
42
+ def available_bundles
43
+ SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}]"
44
+
45
+ @available_bundles ||= begin
46
+ @source_bundle_path.children.select(&:directory?).reduce(Set.new) do |bundles, component|
47
+ component.glob('**/*.{yml, yaml}') do |_|
48
+ bundles << Common::BundleID.new(component.basename.to_s, SgtnClient::LocaleUtil.get_source_locale)
49
+ break bundles
50
+ end || bundles
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end