singleton-client 0.7.4 → 0.7.7

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 +82 -116
  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 +14 -80
  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 -1
  29. data/lib/sgtn-client/util/cache-util.rb +33 -44
  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 +4 -7
  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 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43d840f37ce5f60b23e16623db0b4393f014eaeaafa0ddf91520225a93bd9c57
4
- data.tar.gz: d3c1e045e6c75f7e09cd2790de733a217516afede51dbfede1224cbabcbd3b53
3
+ metadata.gz: f94c0be66f68113fc69f95dbf7d55dac5c21854acc5cf89381e92e0ce0a6abd1
4
+ data.tar.gz: d70dd2bbc7b19af02a70a4de9be14aa39d50a3e043b310074b8fa84fbeb50f2f
5
5
  SHA512:
6
- metadata.gz: 56593be0f8686d669216a849592b9100bb47f52087bf10938d1fae79fb123b9b4ee68baa10e8e627c2ef263e79f2f056aac777e2e5ebc3d8d7f5fbd15b758ce5
7
- data.tar.gz: 5eca696945202e002be5c489ee37153d63c6498e4cc8639f5ea3ad11666ac63e4fffee59572e29fbeb0ff4c7489ff29b06a77cb57e2f9f077cde0663ca5e115b
6
+ metadata.gz: 3be87ccbb4b86a43e75bdc7b6cf2d3e989d640ee99a4140e0a84f372ab9a4bbd43966f6cbd264892435c02f0ddb1c41c07a6a9e4524bb1222e8eb2a0e206c487
7
+ data.tar.gz: adc2e6eca62d579fd134e3cf8b19e49368ecc79541faa6d5195c812482ab5bcc0b2aff4000d836cf007ef0b0afffb3f2c465f954c95d1a1b16378a51b1e31f99
data/README.md CHANGED
@@ -1,54 +1,53 @@
1
- # Singeleton client for Ruby
1
+ # Singleton Client for Ruby
2
2
 
3
3
  ## Prerequisites
4
4
  - Ruby version: 3.0.0 or above
5
5
  - Bundler version: 2.2.3 or above
6
6
 
7
7
  ## Run Unit Test
8
- rake spec:unit
8
+ `rake spec`
9
9
 
10
10
  ## Usage
11
11
 
12
12
  Basic Usage:
13
13
 
14
14
  ```ruby
15
- require 'singleton-ruby'
16
-
17
- include SgtnClient
18
-
19
- SgtnClient.load(file, mode)
20
-
21
- SgtnClient::Source.loadBundles(locale)
22
-
23
- @Result = SgtnClient::Translation.getString(component, key, locale)
15
+ require 'singleton-client'
24
16
 
17
+ Sgtn.load_config(file, env)
18
+ result = Sgtn.translate(key, component, locale)
25
19
  ```
26
20
  ## API Usage
27
21
 
28
22
  ### Get a string's translation
29
- SgtnClient::Translation.getString(component, key, locale)
30
-
31
- ### Get a string's translation and format it with placeholders
32
- SgtnClient::Translation.getString_f(component, key, args, locale)
33
-
34
- ### Get a component's translations
35
- SgtnClient::Translation.getStrings(component, locale)
36
-
23
+ `result = Sgtn.translate(key, component, locale)`
37
24
 
38
- ## API Usage(with request_store)
39
-
40
- Before call below APIs(without locale and component arguments), it requires to set the locale and component in the initial codes.
41
-
42
- ### Get a string's translation
43
- SgtnClient::T.s(key)
25
+ ### Get a string's translation with default value when no translation
26
+ `result = Sgtn.translate(key, component, locale) { 'default value' }`
44
27
 
45
28
  ### Get a string's translation and format it with placeholders
46
- SgtnClient::T.s_f(key, args)
29
+ `result = Sgtn.translate(key, component, locale, **args)`
47
30
 
48
- ### Get a component's translations
49
- SgtnClient::T.c()
31
+ ### Get pluralized translation
32
+ `result = Sgtn.translate(key, component, locale, **args)`
50
33
 
34
+ ### Get translations of a bundle
35
+ `result = Sgtn.get_translations(component, locale)`
51
36
 
37
+ ### Set locale for a request
38
+ `Sgtn.locale = 'en'`
52
39
 
40
+ ### Get locale of the request
41
+ `result = Sgtn.locale`
53
42
 
43
+ ### Get a string's translation with locale set
44
+ ```ruby
45
+ Sgtn.locale = 'en'
46
+ result = Sgtn.translate(key, component)
47
+ ```
54
48
 
49
+ ### Get translations of a bundle with locale set
50
+ ```ruby
51
+ Sgtn.locale = 'en'
52
+ result = Sgtn.get_translations(component)
53
+ ```
@@ -1,71 +1,15 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
4
+ require 'sgtn-client/loader/source'
1
5
 
2
6
  module SgtnClient
3
-
4
- autoload :CacheUtil, "sgtn-client/util/cache-util"
7
+ autoload :CacheUtil, 'sgtn-client/util/cache-util'
5
8
 
6
9
  class Source
7
-
8
- def self.getSource(component, key, locale)
9
- SgtnClient.logger.debug "[Source][getSource]component=#{component}, key=#{key}, locale=#{locale}"
10
- cache_key = SgtnClient::CacheUtil.get_cachekey(component, locale)
11
- expired, items = SgtnClient::CacheUtil.get_cache(cache_key)
12
- if items.nil?
13
- items = getBundle(component, locale)
14
- SgtnClient::CacheUtil.write_cache(cache_key, items)
15
- else
16
- SgtnClient.logger.debug "[Source][getSource]getting sources from cache with key: " + cache_key
17
- end
18
- s = items.nil?? nil : items[locale][key]
19
- if items.nil? || s.nil?
20
- SgtnClient.logger.debug "[Source][getSource]source not found, return key: " + key
21
- #return key
22
- return nil
23
- else
24
- return s
25
- end
26
- end
27
-
28
- def self.getSources(component, locale)
29
- SgtnClient.logger.debug "[Source][getSources]component=#{component}, locale=#{locale}"
30
- cache_key = SgtnClient::CacheUtil.get_cachekey(component, locale)
31
- expired, items = SgtnClient::CacheUtil.get_cache(cache_key)
32
- if items.nil? || expired
33
- items = getBundle(component, locale)
34
- SgtnClient::CacheUtil.write_cache(cache_key, items)
35
- else
36
- SgtnClient.logger.debug "[Source][getSources]getting sources from cache with key: " + cache_key
37
- end
38
- return items
39
- end
40
-
41
- def self.loadBundles(locale)
42
- SgtnClient.logger.debug "[Source][loadBundles]locale=#{locale}"
43
- env = SgtnClient::Config.default_environment
44
- SgtnClient::Config.configurations.default = locale
45
- source_bundle = SgtnClient::Config.configurations[env]["source_bundle"]
46
- Dir.foreach(source_bundle) do |component|
47
- next if component == '.' || component == '..'
48
- yamlfile = File.join(source_bundle, component + "/" + locale + ".yml")
49
- bundle = SgtnClient::FileUtil.read_yml(yamlfile)
50
- cachekey = SgtnClient::CacheUtil.get_cachekey(component, locale)
51
- SgtnClient::CacheUtil.write_cache(cachekey,bundle)
52
- end
53
- end
54
-
55
- private
56
- def self.getBundle(component, locale)
57
- SgtnClient.logger.debug "[Source][getBundle]component=#{component}, locale=#{locale}"
58
- env = SgtnClient::Config.default_environment
59
- source_bundle = SgtnClient::Config.configurations[env]["source_bundle"]
60
- bundlepath = source_bundle + "/" + component + "/" + locale + ".yml"
61
- begin
62
- bundle = SgtnClient::FileUtil.read_yml(bundlepath)
63
- rescue => exception
64
- SgtnClient.logger.error exception.message
65
- end
66
- return bundle
67
- end
68
-
10
+ def self.loadBundles(locale)
11
+ SgtnClient.logger.debug "[Source][loadBundles]locale=#{locale}"
12
+ SgtnClient::Config.configurations.default = locale
13
+ end
69
14
  end
70
-
71
- end
15
+ end
@@ -1,22 +1,24 @@
1
- module SgtnClient
2
- class T < Translation
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
3
 
4
- def self.s(key)
5
- locale = RequestStore.store[:locale]
6
- component = RequestStore.store[:component]
7
- return getString(component, key, locale)
8
- end
4
+ module SgtnClient
5
+ class T
6
+ def self.s(key)
7
+ locale = RequestStore.store[:locale]
8
+ component = RequestStore.store[:component]
9
+ Translation.getString(component, key, locale)
10
+ end
9
11
 
10
- def self.s_f(key, args)
11
- locale = RequestStore.store[:locale]
12
- component = RequestStore.store[:component]
13
- return getString_f(component, key, args, locale)
14
- end
12
+ def self.s_f(key, args)
13
+ locale = RequestStore.store[:locale]
14
+ component = RequestStore.store[:component]
15
+ Translation.getString_f(component, key, args, locale)
16
+ end
15
17
 
16
- def self.c()
17
- locale = RequestStore.store[:locale]
18
- component = RequestStore.store[:component]
19
- return getStrings(component, locale)
20
- end
18
+ def self.c
19
+ locale = RequestStore.store[:locale]
20
+ component = RequestStore.store[:component]
21
+ Translation.getStrings(component, locale)
21
22
  end
22
- end
23
+ end
24
+ end
@@ -1,147 +1,113 @@
1
- require 'multi_json'
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
2
3
 
3
- module SgtnClient
4
-
5
- module Core
6
- autoload :Request, "sgtn-client/core/request"
7
- autoload :Cache, "sgtn-client/core/cache"
8
- autoload :CacheUtil, "sgtn-client/util/cache-util"
9
- autoload :LocaleUtil, "sgtn-client/util/locale-util"
10
- end
11
-
12
- class Translation
4
+ require 'request_store'
13
5
 
14
- def self.getString(component, key, locale)
6
+ module SgtnClient
7
+ module Translation
8
+ module Implementation
9
+ # <b>DEPRECATED:</b> Please use <tt>Sgtn:translate</tt> instead.
10
+ def getString(component, key, locale)
15
11
  SgtnClient.logger.debug "[Translation.getString]component: #{component}, key: #{key}, locale: #{locale}"
16
- str = getTranslation(component, key, locale)
17
- if str.nil?
18
- str = SgtnClient::Source.getSource(component, key, SgtnClient::Config.configurations.default)
19
- if str.nil?
20
- SgtnClient.logger.debug "[Translation][getString] Missing source string with key: #{key}, component: #{component}, locale: #{locale}"
21
- end
22
- end
23
- str
12
+ translate(key, component, locale) { nil }
24
13
  end
25
14
 
26
- def self.getString_p(component, key, plural_args, locale)
15
+ # <b>DEPRECATED:</b> Please use <tt>Sgtn:translate</tt> instead.
16
+ def getString_p(component, key, plural_args, locale)
27
17
  SgtnClient.logger.debug "[Translation][getString_p]component=#{component}, key=#{key}, locale=#{locale}"
28
- str = getTranslation(component, key, locale)
29
- if str.nil?
30
- str = SgtnClient::Source.getSource(component, key, SgtnClient::Config.configurations.default)
31
- if str.nil?
32
- SgtnClient.logger.debug "[Translation][getString_p] Missing source string with key: #{key}, component: #{component}, locale: #{locale}"
33
- return nil
34
- end
35
- str.to_plural_s(:en, plural_args)
36
- else
37
- str.to_plural_s(locale, plural_args)
38
- end
18
+ translate(key, component, locale, **plural_args) { nil }
39
19
  end
40
20
 
41
- def self.getString_f(component, key, args, locale, *optionals)
42
- SgtnClient.logger.debug "[Translation][getString_f]component=#{component}, key=#{key}, locale=#{locale}"
43
- s = getString(component, key, locale, *optionals)
44
- if s.nil?
45
- return nil
46
- end
47
- if args.is_a?(Hash)
21
+ # <b>DEPRECATED:</b> Please use <tt>Sgtn:translate</tt> instead.
22
+ def getString_f(component, key, args, locale, *_optionals)
23
+ SgtnClient.logger.debug "[Translation][getString_f]component=#{component}, key=#{key}, locale=#{locale}"
24
+ s = translate(key, component, locale) { nil }
25
+ return nil if s.nil?
26
+
27
+ if args.is_a?(Hash)
48
28
  args.each do |source, arg|
49
29
  s.gsub! "{#{source}}", arg
50
30
  end
51
- elsif args.is_a?(Array)
52
- s = sprintf s % args
53
- end
54
- return s
31
+ elsif args.is_a?(Array)
32
+ s = s % args
33
+ end
34
+ s
55
35
  end
56
36
 
57
- def self.getStrings(component, locale)
58
- SgtnClient.logger.debug "[Translation][getStrings]component=#{component}, locale=#{locale}"
59
- items = get_cs(component, locale)
60
- default = SgtnClient::Config.configurations.default
61
- if items.nil? || items["messages"] == nil
62
- items = {}
63
- s = SgtnClient::Source.getSources(component, default)
64
- if s.nil?
65
- SgtnClient.logger.error "[Translation][getStrings] Missing component: #{component}, locale: #{locale}"
66
- else
67
- default_component, value = s.first
68
- items["component"] = component
69
- items["messages"] = value
70
- items["locale"] = 'source'
71
- end
37
+ # <b>DEPRECATED:</b> Please use <tt>Sgtn:get_translations</tt> instead.
38
+ def getStrings(component, locale)
39
+ get_translations(component, locale)
40
+ end
41
+
42
+ # raise error when translation is not found
43
+ def translate(key, component, locale = nil, **kwargs)
44
+ SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}] key: #{key}, component: #{component}, locale: #{locale}, args: #{kwargs}"
45
+
46
+ locale = locale.nil? ? self.locale : SgtnClient::LocaleUtil.get_best_locale(locale)
47
+
48
+ result = get_bundle(component, locale)&.fetch(key, nil)
49
+ if result.nil? && !LocaleUtil.is_source_locale(locale)
50
+ locale = LocaleUtil.get_source_locale
51
+ result = get_bundle(component, locale)&.fetch(key, nil)
72
52
  end
73
- return items
74
- end
75
53
 
54
+ if result.nil?
55
+ return key unless block_given?
76
56
 
77
- private
57
+ result = yield
58
+ return if result.nil?
59
+ end
78
60
 
79
- def self.getTranslation(component, key, locale)
80
- items = get_cs(component, locale)
81
- if items.nil? || items["messages"] == nil
82
- nil
61
+ if kwargs.empty?
62
+ result
83
63
  else
84
- items["messages"][key]
64
+ locale = result.locale if result.is_a?(SgtnClient::StringUtil)
65
+ result.localize(locale) % kwargs
85
66
  end
86
67
  end
68
+ alias t translate
87
69
 
88
- def self.get_cs(component, locale)
89
- flocale = SgtnClient::LocaleUtil.fallback(locale)
90
- cache_key = SgtnClient::CacheUtil.get_cachekey(component, flocale)
91
- SgtnClient.logger.debug "[Translation][get_cs]cache_key=#{cache_key}"
92
- expired, items = SgtnClient::CacheUtil.get_cache(cache_key)
93
- if items.nil? || expired
94
- items = load(component, flocale)
95
- if items.nil?
96
- items = SgtnClient::Source.getSources(component, SgtnClient::Config.configurations.default)
97
- SgtnClient::Core::Cache.put(cache_key, items, 60)
98
- else
99
- SgtnClient::CacheUtil.write_cache(cache_key, items)
100
- end
101
- else
102
- SgtnClient.logger.debug "[Translation]get translations from cache with key: " + cache_key
70
+ def get_translations(component, locale = nil)
71
+ SgtnClient.logger.debug "[#{method(__callee__).owner}.#{__callee__}] component: #{component}, locale: #{locale}"
72
+
73
+ locale = locale.nil? ? self.locale : SgtnClient::LocaleUtil.get_best_locale(locale)
74
+ items = get_bundle(component, locale)
75
+ if items.nil? && !LocaleUtil.is_source_locale(locale)
76
+ items = get_bundle(component, LocaleUtil.get_source_locale)
77
+ locale = LocaleUtil.get_source_locale
103
78
  end
104
79
 
105
- return items
106
- end
80
+ { 'component' => component, 'locale' => locale, 'messages' => items || {} } if items
81
+ end
107
82
 
108
- def self.load(component, locale)
109
- env = SgtnClient::Config.default_environment
110
- mode = SgtnClient::Config.configurations[env]["bundle_mode"]
111
- SgtnClient.logger.debug "[Translation][load]mode=#{mode}"
112
- if mode == 'offline'
113
- return load_o(component, locale)
114
- else
115
- return load_s(component, locale)
116
- end
83
+ def locale
84
+ RequestStore.store[:locale] ||= SgtnClient::LocaleUtil.get_fallback_locale
117
85
  end
118
86
 
119
- def self.load_o(component, locale)
120
- env = SgtnClient::Config.default_environment
121
- product_name = SgtnClient::Config.configurations[env]["product_name"]
122
- version = SgtnClient::Config.configurations[env]["version"].to_s
123
- translation_bundle = SgtnClient::Config.configurations[env]["translation_bundle"]
124
- bundlepath = translation_bundle + "/" + product_name + "/" + version + "/" + component + "/messages_" + locale + ".json"
125
- SgtnClient::FileUtil.read_json(bundlepath)
87
+ def locale=(value)
88
+ RequestStore.store[:locale] = SgtnClient::LocaleUtil.get_best_locale(value)
126
89
  end
127
90
 
128
- def self.load_s(component, locale)
129
- env = SgtnClient::Config.default_environment
130
- product_name = SgtnClient::Config.configurations[env]["product_name"]
131
- vip_server = SgtnClient::Config.configurations[env]["vip_server"]
132
- version = SgtnClient::Config.configurations[env]["version"].to_s
133
- url = vip_server + "/i18n/api/v2/translation/products/" + product_name + "/versions/" + version + "/locales/" + locale + "/components/" + component+ "?checkTranslationStatus=false&machineTranslation=false&pseudo=false"
134
- begin
135
- obj = SgtnClient::Core::Request.get(url)
136
- rescue => exception
137
- SgtnClient.logger.error exception.message
138
- end
139
- if obj != nil
140
- obj = obj["data"]
141
- end
142
- return obj
91
+ private
92
+
93
+ def get_bundle(component, locale)
94
+ get_bundle!(component, locale)
95
+ rescue StandardError => e
96
+ SgtnClient.logger.error "[#{method(__callee__).owner}.#{__callee__}] failed to get a bundle. component: #{component}, locale: #{locale}"
97
+ SgtnClient.logger.error e
98
+ nil
143
99
  end
144
100
 
145
- end
101
+ def get_bundle!(component, locale)
102
+ id = SgtnClient::Common::BundleID.new(component, locale)
103
+ bundles = SgtnClient::Config.available_bundles
104
+ unless bundles.nil? || bundles.empty? || bundles.include?(id)
105
+ raise SgtnClient::SingletonError, 'bundle is unavailable.'
106
+ end
146
107
 
147
- end
108
+ SgtnClient::Config.loader.get_bundle(component, locale)
109
+ end
110
+ end
111
+ extend Implementation
112
+ end
113
+ end
@@ -1,4 +1,7 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
1
4
  require 'sgtn-client/cldr/localized_datetime'
2
5
  require 'sgtn-client/cldr/localized_date'
3
6
  require 'sgtn-client/cldr/localized_time'
4
- require 'sgtn-client/cldr/localized_str'
7
+ require 'sgtn-client/cldr/localized_str'
@@ -1,3 +1,6 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
1
4
  require 'date'
2
5
  require 'time'
3
6
 
@@ -24,4 +27,4 @@ Date.class_eval <<-LOCALIZE, __FILE__, __LINE__ + 1
24
27
  def l_short_s(locale = TwitterCldr.locale)
25
28
  self.to_datetime().localize(locale).to_date().to_short_s
26
29
  end
27
- LOCALIZE
30
+ LOCALIZE
@@ -1,3 +1,6 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
1
4
  require 'date'
2
5
  require 'time'
3
6
 
@@ -60,4 +63,4 @@ DateTime.class_eval <<-LOCALIZE, __FILE__, __LINE__ + 1
60
63
  tz.display_name_for(self, display_name)
61
64
  end
62
65
  end
63
- LOCALIZE
66
+ LOCALIZE
@@ -1,5 +1,8 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
1
3
 
2
4
  String.class_eval <<-LOCALIZE, __FILE__, __LINE__ + 1
5
+ # <b>DEPRECATED:</b> Please use <tt>Sgtn:translate</tt> instead.
3
6
  def to_plural_s(locale, arg)
4
7
  num_str = SgtnClient::Formatters::PluralFormatter.new(locale).num_s(self, arg)
5
8
  if num_str.nil? || num_str.empty?
@@ -8,4 +11,4 @@ String.class_eval <<-LOCALIZE, __FILE__, __LINE__ + 1
8
11
  num_str
9
12
  end
10
13
  end
11
- LOCALIZE
14
+ LOCALIZE
@@ -1,3 +1,6 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
1
4
  require 'date'
2
5
  require 'time'
3
6
 
@@ -24,4 +27,4 @@ Time.class_eval <<-LOCALIZE, __FILE__, __LINE__ + 1
24
27
  def l_short_s(locale = TwitterCldr.locale)
25
28
  self.localize(locale).to_short_s
26
29
  end
27
- LOCALIZE
30
+ LOCALIZE
@@ -0,0 +1,30 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
4
+ module SgtnClient
5
+ module Common
6
+ class BundleID
7
+ attr_reader :locale, :component
8
+
9
+ def initialize(component, locale)
10
+ @locale = locale
11
+ @component = component
12
+ @key = [@component, @locale].hash
13
+ end
14
+
15
+ def hash
16
+ @key
17
+ end
18
+
19
+ def ==(other)
20
+ (other.is_a? self.class) && @locale == other.locale && @component == other.component
21
+ end
22
+
23
+ alias eql? ==
24
+
25
+ def to_s
26
+ "locale=#{@locale}, component=#{@component}}"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright 2022 VMware, Inc.
2
+ # SPDX-License-Identifier: EPL-2.0
3
+
4
+ module SgtnClient
5
+ class SingleOperation
6
+ def initialize(*conditions, &block)
7
+ raise 'no way to create a new obj' unless block
8
+
9
+ @lock = Mutex.new
10
+ @hash = {}
11
+
12
+ @conditions = conditions
13
+ @creator = block
14
+ end
15
+
16
+ # return new created object
17
+ def operate(id, *args)
18
+ @lock.synchronize do
19
+ obj = @hash[id]
20
+ @conditions.each do |con|
21
+ return obj unless con.call(id, obj, *args)
22
+ end
23
+ # TODO: whatif returning nil
24
+ @hash[id] = @creator.call(id, obj, *args)
25
+ end
26
+ end
27
+
28
+ def remove_object(id)
29
+ @lock.synchronize do
30
+ @hash.delete(id)
31
+ end
32
+ end
33
+ end
34
+ end