tater 2.0.4 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51644f41d80e87d20ed43b9e8c6b4797fb7aeb4258c94d5d9f9d7a50eb5ee5c6
4
- data.tar.gz: 1fe43b99fbc2f24cdc342ec4278ff7140ee6b9579dd51d236fcc713907f1455d
3
+ metadata.gz: 7176a93dd72c2135f8114c2baed2d4fdda8ffcb7c02312517810d94f9e275dce
4
+ data.tar.gz: 5dea4c80c2a894063790d2cd4ed2232e8566b3cae4a3ab9a73a2be3c45ffd4f5
5
5
  SHA512:
6
- metadata.gz: d4c059fdc696d39c489e61d3e53954fe921feaba78055bd5e62c944d45daf10dfa178b76e4e836d326ba61be0dbf7bbec13b63f52b95e13f23da9ca5be4d3b8c
7
- data.tar.gz: 6b12ffa8f2a1186f2de92c9c16bb5c2e1027f93ab5ee2c877dbad297777274b9f4f38882255490db3c555a0dafb129ed876bd784d3a872e07a4e7e9f6e0e24ec
6
+ metadata.gz: b32716e497cc9a2e81eded3bc277450d143ffec829b4accf3b3396b2c1afb28b4c66bab044695cf13bd6b64ff830d4168b181435565864e977f59ce9fcf6df1e
7
+ data.tar.gz: bd697439d8db3cd1607762bd92adb13e066fb40e16c7db6469a8e2a226774480a4cd1ba19651d45673c6ce819e9f7913af82c0b779c37c5f803e0c72ce3d42ff
data/README.org CHANGED
@@ -212,8 +212,8 @@ i18n.translate('login.special.description', cascade: true) # => 'Normal descript
212
212
  With cascade, the final key stays the same, but pieces of the scope get lopped
213
213
  off. In this case, lookups will be tried in this order:
214
214
 
215
- 1. ='login.special.description'=
216
- 2. ='login.description'=
215
+ 1. =login.special.description=
216
+ 2. =login.description=
217
217
 
218
218
  This can be useful when you want to override some messages but don't want to
219
219
  have to copy all of the other, non-overwritten messages.
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ require 'tater'
3
+
4
+ class Tater # :nodoc:
5
+ # Alias to Tater#localize
6
+ alias l localize
7
+
8
+ # Alias to Tater#translate
9
+ alias t translate
10
+ end
data/lib/tater/hash.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ class Tater
3
+ # Refine Hash and add the #except method from Ruby 3.
4
+ module HashExcept
5
+ refine Hash do
6
+ # Taken from the excellent backports gem written by Marc-André Lafortune.
7
+ # https://github.com/marcandre/backports/blob/master/lib/backports/3.0.0/hash/except.rb
8
+ def except(*keys)
9
+ if keys.size > 4 && size > 4 # index if O(m*n) is big
10
+ h = {}
11
+ keys.each { |key| h[key] = true }
12
+ keys = h
13
+ end
14
+
15
+ reject { |key, _value| keys.include?(key) }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+ class Tater
3
+ # Utility methods that require no state.
4
+ module Utils
5
+ HASH = {}.freeze
6
+ FORMAT_CURLY = '%{'
7
+ FORMAT_NAMED = '%<'
8
+
9
+ # Merge all the way down.
10
+ #
11
+ # @param to [Hash]
12
+ # The target Hash to merge into.
13
+ # @param from [Hash]
14
+ # The Hash to copy values from.
15
+ # @return [Hash]
16
+ def self.deep_merge(to, from)
17
+ to.merge(from) do |_key, left, right|
18
+ if left.is_a?(Hash) && right.is_a?(Hash)
19
+ Utils.deep_merge(left, right)
20
+ else
21
+ right
22
+ end
23
+ end
24
+ end
25
+
26
+ # Transform keys all the way down.
27
+ #
28
+ # @param hash [Hash]
29
+ # The Hash to stringify keys for.
30
+ # @return [Hash]
31
+ def self.deep_stringify_keys(hash)
32
+ hash.transform_keys(&:to_s).transform_values do |value|
33
+ if value.is_a?(Hash)
34
+ Utils.deep_stringify_keys(value)
35
+ else
36
+ value
37
+ end
38
+ end
39
+ end
40
+
41
+ # Freeze all the way down.
42
+ #
43
+ # @param hash [Hash]
44
+ # @return [Hash]
45
+ def self.deep_freeze(hash)
46
+ hash.transform_keys(&:freeze).transform_values do |value|
47
+ if value.is_a?(Hash)
48
+ Utils.deep_freeze(value)
49
+ else
50
+ value.freeze
51
+ end
52
+ end.freeze
53
+ end
54
+
55
+ # Format values into a string if appropriate.
56
+ #
57
+ # @param string [String]
58
+ # The target string to interpolate into.
59
+ # @param options [Hash]
60
+ # The values to interpolate into the target string.
61
+ #
62
+ # @return [String]
63
+ def self.interpolate(string, options = HASH)
64
+ return string if options.empty?
65
+ return string unless interpolation_string?(string)
66
+
67
+ format(string, options)
68
+ end
69
+
70
+ # Determine whether a string includes any interpolation placeholders e.g.
71
+ # "%{" or "%<"
72
+ #
73
+ # @param string [String]
74
+ # @return [Boolean]
75
+ def self.interpolation_string?(string)
76
+ string.include?(FORMAT_CURLY) || string.include?(FORMAT_NAMED)
77
+ end
78
+
79
+ # Convert a Numeric to a string, particularly formatting BigDecimals to a
80
+ # Float-like string representation.
81
+ #
82
+ # @param numeric [Numeric]
83
+ #
84
+ # @return [String]
85
+ def self.string_from_numeric(numeric)
86
+ if numeric.is_a?(BigDecimal)
87
+ numeric.to_s('F')
88
+ else
89
+ numeric.to_s
90
+ end
91
+ end
92
+ end
93
+ end
data/lib/tater.rb CHANGED
@@ -4,96 +4,24 @@ require 'date'
4
4
  require 'time'
5
5
  require 'yaml'
6
6
 
7
+ require 'tater/utils'
8
+ require 'tater/hash' unless Hash.method_defined?(:except)
9
+
7
10
  # Tater is a internationalization (i18n) and localization (l10n) library
8
11
  # designed for speed and simplicity.
9
12
  class Tater
10
13
  class MissingLocalizationFormat < ArgumentError; end
11
-
12
14
  class UnLocalizableObject < ArgumentError; end
13
15
 
14
- module Utils # :nodoc:
15
- # Merge all the way down.
16
- #
17
- # @param to [Hash]
18
- # The target Hash to merge into.
19
- # @param from [Hash]
20
- # The Hash to copy values from.
21
- # @return [Hash]
22
- def self.deep_merge(to, from)
23
- to.merge(from) do |_key, left, right|
24
- if left.is_a?(Hash) && right.is_a?(Hash)
25
- Utils.deep_merge(left, right)
26
- else
27
- right
28
- end
29
- end
30
- end
31
-
32
- # Transform keys all the way down.
33
- #
34
- # @param hash [Hash]
35
- # The Hash to stringify keys for.
36
- # @return [Hash]
37
- def self.deep_stringify_keys(hash)
38
- hash.transform_keys(&:to_s).transform_values do |value|
39
- if value.is_a?(Hash)
40
- Utils.deep_stringify_keys(value)
41
- else
42
- value
43
- end
44
- end
45
- end
46
-
47
- # Freeze all the way down.
48
- #
49
- # @param hash [Hash]
50
- # @return [Hash]
51
- def self.deep_freeze(hash)
52
- hash.transform_keys(&:freeze).transform_values do |value|
53
- if value.is_a?(Hash)
54
- Utils.deep_freeze(value)
55
- else
56
- value.freeze
57
- end
58
- end.freeze
59
- end
60
-
61
- # Try to interpolate these things, if one of them is a string.
62
- #
63
- # @param string [String]
64
- # The target string to interpolate into.
65
- # @param options [Hash]
66
- # The values to interpolate into the target string.
67
- #
68
- # @return [String]
69
- def self.interpolate(string, options = HASH)
70
- return string unless string.is_a?(String)
71
- return string if options.empty?
72
-
73
- format(string, options)
74
- end
75
-
76
- # Convert a Numeric to a string, particularly formatting BigDecimals to a
77
- # Float-like string representation.
78
- #
79
- # @param numeric [Numeric]
80
- #
81
- # @return [String]
82
- def self.string_from_numeric(numeric)
83
- if numeric.is_a?(BigDecimal)
84
- numeric.to_s('F')
85
- else
86
- numeric.to_s
87
- end
88
- end
89
- end
90
-
91
16
  DEFAULT = 'default'
92
17
  DELIMITING_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/.freeze
93
18
  HASH = {}.freeze
94
19
  SEPARATOR = '.'
95
20
  SUBSTITUTION_REGEX = /%(|\^)[aAbBpP]/.freeze
96
21
 
22
+ # Needed for Ruby < 3.
23
+ using HashExcept unless Hash.method_defined?(:except)
24
+
97
25
  # @return [String]
98
26
  attr_reader :locale
99
27
 
@@ -154,7 +82,7 @@ class Tater
154
82
  end
155
83
 
156
84
  Dir.glob(File.join(path, '**', '*.rb')).each do |file|
157
- @messages = Utils.deep_merge(@messages, Utils.deep_stringify_keys(eval(IO.read(file), binding, file))) # rubocop:disable Security/Eval
85
+ @messages = Utils.deep_merge(@messages, Utils.deep_stringify_keys(eval(File.read(file), binding, file))) # rubocop:disable Security/Eval
158
86
  end
159
87
  end
160
88
 
@@ -220,7 +148,6 @@ class Tater
220
148
  raise(UnLocalizableObject, "The object class #{ object.class } cannot be localized by Tater.")
221
149
  end
222
150
  end
223
- alias l localize
224
151
 
225
152
  # Lookup a key in the messages hash, using the current locale or an override.
226
153
  #
@@ -325,25 +252,39 @@ class Tater
325
252
  # The translated and interpreted string, if found, or any data at the
326
253
  # defined key.
327
254
  def translate(key, options = HASH)
328
- message =
329
- if options.key?(:locales)
330
- options[:locales].append(@locale) if @locale && !options[:locales].include?(@locale)
255
+ if options.empty?
256
+ message = lookup(key)
331
257
 
332
- options[:locales].find do |accept|
333
- found = lookup(key, locale: accept, cascade: options[:cascade])
334
-
335
- break found unless found.nil?
336
- end
258
+ if message.is_a?(Proc) # rubocop:disable Style/CaseLikeIf
259
+ message.call(key)
260
+ elsif message.is_a?(String)
261
+ message
337
262
  else
338
- lookup(key, locale: options[:locale], cascade: options[:cascade])
263
+ "Tater lookup failed: #{ locale }.#{ key }"
339
264
  end
265
+ else
266
+ message =
267
+ if options.key?(:locales)
268
+ options[:locales].append(@locale) if @locale && !options[:locales].include?(@locale)
340
269
 
341
- # Call procs that should return a string.
342
- message = message.call(key, options) if message.is_a?(Proc)
270
+ options[:locales].find do |accept|
271
+ found = lookup(key, locale: accept, cascade: options[:cascade])
272
+
273
+ break found unless found.nil?
274
+ end
275
+ else
276
+ lookup(key, locale: options[:locale], cascade: options[:cascade])
277
+ end
343
278
 
344
- Utils.interpolate(message, options) || options[:default] || "Tater lookup failed: #{ options[:locale] || options[:locales] || locale }.#{ key }"
279
+ if message.is_a?(Proc) # rubocop:disable Style/CaseLikeIf
280
+ message.call(key, options.except(:cascade, :default, :locale, :locales))
281
+ elsif message.is_a?(String)
282
+ Utils.interpolate(message, options.except(:cascade, :default, :locale, :locales))
283
+ else
284
+ options[:default] || "Tater lookup failed: #{ options[:locale] || options[:locales] || locale }.#{ key }"
285
+ end
286
+ end
345
287
  end
346
- alias t translate
347
288
 
348
289
  private
349
290
 
data/test/tater_test.rb CHANGED
@@ -61,6 +61,20 @@ describe Tater do
61
61
  assert_equal '1.0', Tater::Utils.string_from_numeric(BigDecimal('1'))
62
62
  end
63
63
  end
64
+
65
+ describe '#interpolation_string?' do
66
+ def is?(arg)
67
+ Tater::Utils.interpolation_string?(arg)
68
+ end
69
+
70
+ it 'checks whether a string contains interpolation placeholders' do
71
+ assert is?('Hey %{there}!')
72
+ assert is?('Hey %<there>s!')
73
+ refute is?('Nah, this is fine')
74
+ refute is?("<b>HTML shouldn't count")
75
+ refute is?("A single % shouldn't count")
76
+ end
77
+ end
64
78
  end
65
79
 
66
80
  describe '#available?' do
@@ -143,6 +157,10 @@ describe Tater do
143
157
  assert_nil i18n.lookup('nope')
144
158
  end
145
159
 
160
+ it 'returns arbitrary data at keys' do
161
+ assert_equal({ 'key' => 'This key is deeper' }, i18n.lookup('deep'))
162
+ end
163
+
146
164
  it 'cascades' do
147
165
  assert_equal 'Delicious', i18n.lookup('cascade.nope.tacos', cascade: true)
148
166
  assert_equal 'Whoaa', i18n.lookup('cascade.another.nope.crazy', cascade: true)
@@ -164,8 +182,8 @@ describe Tater do
164
182
  assert_equal 'This key is deeper', i18n.translate('deep.key')
165
183
  end
166
184
 
167
- it 'returns a hash for nested keys' do
168
- assert_equal({ 'key' => 'This key is deeper' }, i18n.translate('deep'))
185
+ it 'does not return a hash for nested keys' do
186
+ assert_equal 'Tater lookup failed: en.deep', i18n.translate('deep')
169
187
  end
170
188
 
171
189
  it 'interpolates additional variables' do
@@ -182,10 +200,6 @@ describe Tater do
182
200
  assert_equal 'Tater lookup failed: en.nope', i18n.translate('nope')
183
201
  end
184
202
 
185
- it 'is aliased as t' do
186
- assert_equal 'This is a title', i18n.t('title')
187
- end
188
-
189
203
  it 'cascades lookups' do
190
204
  assert_equal 'Tater lookup failed: en.cascade.another.nope.crazy', i18n.translate('cascade.another.nope.crazy', cascade: false)
191
205
  assert_equal 'Tater lookup failed: en.cascade.nope.tacos', i18n.translate('cascade.nope.tacos')
@@ -203,9 +217,12 @@ describe Tater do
203
217
  assert_equal 'Tater lookup failed: ["fr", "en"].neither', i18n.translate('neither', locales: %w[fr en])
204
218
  end
205
219
 
206
- it 'finds Ruby files as well' do
220
+ it 'finds Ruby files' do
207
221
  assert_equal 'Hey ruby!', i18n.translate('ruby')
208
- assert_equal 'Hey options!', i18n.translate('options', options: 'options')
222
+ end
223
+
224
+ it 'does not interpolate messages returned by procs' do
225
+ assert_equal 'Hey %{options}!', i18n.translate('options', options: 'options')
209
226
  end
210
227
  end
211
228
 
@@ -316,10 +333,6 @@ describe Tater do
316
333
  end
317
334
  end
318
335
 
319
- it 'is aliased l' do
320
- assert_equal '1970/1/1', i18n.l(Date.new(1970, 1, 1))
321
- end
322
-
323
336
  describe 'month, day, and AM/PM names' do
324
337
  let :i18n do
325
338
  Tater.new(path: File.expand_path('test/fixtures'), locale: 'fr')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tater
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.4
4
+ version: 3.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Lecklider
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-12 00:00:00.000000000 Z
11
+ date: 2021-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -132,6 +132,9 @@ files:
132
132
  - LICENSE.txt
133
133
  - README.org
134
134
  - lib/tater.rb
135
+ - lib/tater/aliases.rb
136
+ - lib/tater/hash.rb
137
+ - lib/tater/utils.rb
135
138
  - test/fixtures/another.yml
136
139
  - test/fixtures/fixtures.yml
137
140
  - test/fixtures/messages/more.yml
@@ -142,8 +145,9 @@ licenses:
142
145
  - MIT
143
146
  metadata:
144
147
  bug_tracker_uri: https://github.com/evanleck/tater/issues
148
+ rubygems_mfa_required: 'true'
145
149
  source_code_uri: https://github.com/evanleck/tater
146
- post_install_message:
150
+ post_install_message:
147
151
  rdoc_options: []
148
152
  require_paths:
149
153
  - lib
@@ -158,8 +162,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
162
  - !ruby/object:Gem::Version
159
163
  version: '2.0'
160
164
  requirements: []
161
- rubygems_version: 3.2.22
162
- signing_key:
165
+ rubygems_version: 3.2.32
166
+ signing_key:
163
167
  specification_version: 4
164
168
  summary: Minimal internationalization and localization library.
165
169
  test_files: