tater 3.0.2 → 3.0.5
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/README.org +4 -3
- data/lib/tater/aliases.rb +10 -0
- data/lib/tater/hash.rb +19 -0
- data/lib/tater/utils.rb +93 -0
- data/lib/tater/version.rb +4 -0
- data/lib/tater.rb +35 -23
- data/test/tater_test.rb +28 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08a174e27ecb08955ba15997b8b71995aff8a77cbf82bb08514ba5dcd6e404dd'
|
4
|
+
data.tar.gz: c9f78478bc5f1b7a48b038add4f3c05230dc6083ccada5d1de00974b1dbd08f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27b9c2ad757cb5a51bf0ce3a792a92912bc3d8a2f9e82c761621396417480b6c82088222cd9e2c552d2309381d8feb0399466cade27b75fd2fb29d6eab2552b0
|
7
|
+
data.tar.gz: baf324c10b838927b0aedc2ac5babafed538acbdaeb7a4f7a5e6aebf8731d53037ec2dad4870d459a90056c9b9c2442d59ed6efaf5d94dbb74341f6a6ae3c14d
|
data/README.org
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
* Tater
|
2
2
|
|
3
3
|
[[https://badge.fury.io/rb/tater][https://badge.fury.io/rb/tater.svg]]
|
4
|
+
[[https://github.com/evanleck/tater/actions/workflows/main.yml][https://github.com/evanleck/tater/actions/workflows/main.yml/badge.svg]]
|
4
5
|
|
5
6
|
Tater is an internationalization (i18n) and localization (l10n) library designed
|
6
7
|
for simplicity. It doesn't do everything that other libraries do, but that's by
|
@@ -238,7 +239,7 @@ Tater.new.translate('nope', default: 'Yep!') # => 'Yep!'
|
|
238
239
|
** Procs and messages in Ruby
|
239
240
|
|
240
241
|
Ruby files can be used to store messages in addition to YAML, so long as the
|
241
|
-
Ruby file returns a =Hash= when
|
242
|
+
Ruby file returns a =Hash= when evaluated.
|
242
243
|
|
243
244
|
#+begin_src ruby
|
244
245
|
{
|
@@ -281,7 +282,7 @@ Locales will be tried in order and whichever one matches first will be returned.
|
|
281
282
|
|
282
283
|
** Limitations
|
283
284
|
|
284
|
-
- It is not
|
285
|
+
- It is not plug-able, it does what it does and that's it.
|
285
286
|
- It doesn't handle pluralization yet, though it may in the future.
|
286
287
|
|
287
288
|
** Why?
|
@@ -292,6 +293,6 @@ file that handles the basics of lookup and interpolation.
|
|
292
293
|
|
293
294
|
** Trivia
|
294
295
|
|
295
|
-
I was
|
296
|
+
I was originally going to call this library "Translator" but with a [[https://en.wikipedia.org/wiki/Numeronym][numeronym]]
|
296
297
|
like I18n: "t8r". I looked at it for a while but I read it as "tater" instead
|
297
298
|
of "tee-eight-arr" so I figured I'd just name it Tater. Tater the translator.
|
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
|
data/lib/tater/utils.rb
ADDED
@@ -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
@@ -3,8 +3,10 @@ require 'bigdecimal'
|
|
3
3
|
require 'date'
|
4
4
|
require 'time'
|
5
5
|
require 'yaml'
|
6
|
-
|
7
|
-
|
6
|
+
|
7
|
+
require 'tater/hash' unless Hash.method_defined?(:except)
|
8
|
+
require 'tater/utils'
|
9
|
+
require 'tater/version'
|
8
10
|
|
9
11
|
# Tater is a internationalization (i18n) and localization (l10n) library
|
10
12
|
# designed for speed and simplicity.
|
@@ -21,6 +23,11 @@ class Tater
|
|
21
23
|
# Needed for Ruby < 3.
|
22
24
|
using HashExcept unless Hash.method_defined?(:except)
|
23
25
|
|
26
|
+
# An array of the available locale codes found in loaded messages.
|
27
|
+
#
|
28
|
+
# @return [Array<String>]
|
29
|
+
attr_reader :available
|
30
|
+
|
24
31
|
# @return [String]
|
25
32
|
attr_reader :locale
|
26
33
|
|
@@ -36,6 +43,8 @@ class Tater
|
|
36
43
|
# @param path [String]
|
37
44
|
# A path to search for YAML or Ruby files to load messages from.
|
38
45
|
def initialize(cascade: false, locale: nil, messages: nil, path: nil)
|
46
|
+
@available = []
|
47
|
+
@cache = {}
|
39
48
|
@cascade = cascade
|
40
49
|
@locale = locale
|
41
50
|
@messages = {}
|
@@ -44,6 +53,11 @@ class Tater
|
|
44
53
|
load(messages: messages) if messages
|
45
54
|
end
|
46
55
|
|
56
|
+
# @return [String]
|
57
|
+
def inspect
|
58
|
+
%(#<Tater:#{ object_id } @cascade=#{ @cascade } @locale="#{ @locale }" @available=#{ @available }>)
|
59
|
+
end
|
60
|
+
|
47
61
|
# Do lookups cascade by default?
|
48
62
|
#
|
49
63
|
# @return [Boolean]
|
@@ -51,13 +65,6 @@ class Tater
|
|
51
65
|
@cascade
|
52
66
|
end
|
53
67
|
|
54
|
-
# An array of the available locale codes found in loaded messages.
|
55
|
-
#
|
56
|
-
# @return [Array]
|
57
|
-
def available
|
58
|
-
@available ||= messages.keys
|
59
|
-
end
|
60
|
-
|
61
68
|
# Is this locale available in our current set of messages?
|
62
69
|
#
|
63
70
|
# @return [Boolean]
|
@@ -88,12 +95,12 @@ class Tater
|
|
88
95
|
@messages = Utils.deep_merge(@messages, Utils.deep_stringify_keys(messages)) if messages
|
89
96
|
@messages = Utils.deep_freeze(@messages)
|
90
97
|
|
91
|
-
#
|
92
|
-
|
98
|
+
# Update our available locales.
|
99
|
+
@available.replace(@messages.keys.map(&:to_s).sort)
|
93
100
|
|
94
101
|
# Not only does this clear our cache but it establishes the basic structure
|
95
102
|
# that we rely on in other methods.
|
96
|
-
@cache
|
103
|
+
@cache.clear
|
97
104
|
|
98
105
|
@messages.each_key do |key|
|
99
106
|
@cache[key] = { false => {}, true => {} }
|
@@ -105,7 +112,8 @@ class Tater
|
|
105
112
|
# @param locale [String]
|
106
113
|
# The locale code to set as our default.
|
107
114
|
def locale=(locale)
|
108
|
-
|
115
|
+
str = locale.to_s
|
116
|
+
@locale = str if available?(str)
|
109
117
|
end
|
110
118
|
|
111
119
|
# Localize an Array, Date, Time, DateTime, or Numeric object.
|
@@ -209,20 +217,24 @@ class Tater
|
|
209
217
|
#
|
210
218
|
# @return [Boolean]
|
211
219
|
def includes?(key, options = HASH)
|
212
|
-
|
213
|
-
|
214
|
-
|
220
|
+
if options.empty?
|
221
|
+
!lookup(key).nil?
|
222
|
+
else
|
223
|
+
message =
|
224
|
+
if options.key?(:locales)
|
225
|
+
options[:locales].append(@locale) if @locale && !options[:locales].include?(@locale)
|
215
226
|
|
216
|
-
|
217
|
-
|
227
|
+
options[:locales].find do |accept|
|
228
|
+
found = lookup(key, locale: accept, cascade: options[:cascade])
|
218
229
|
|
219
|
-
|
230
|
+
break found unless found.nil?
|
231
|
+
end
|
232
|
+
else
|
233
|
+
lookup(key, locale: options[:locale], cascade: options[:cascade])
|
220
234
|
end
|
221
|
-
else
|
222
|
-
lookup(key, locale: options[:locale], cascade: options[:cascade])
|
223
|
-
end
|
224
235
|
|
225
|
-
|
236
|
+
!message.nil?
|
237
|
+
end
|
226
238
|
end
|
227
239
|
|
228
240
|
# Translate a key path and optional interpolation arguments into a string.
|
data/test/tater_test.rb
CHANGED
@@ -455,4 +455,32 @@ describe Tater do
|
|
455
455
|
refute i18n.includes?('cascade.nope.tacos', cascade: false)
|
456
456
|
end
|
457
457
|
end
|
458
|
+
|
459
|
+
describe '#inspect' do
|
460
|
+
it 'returns a stringified version of the object' do
|
461
|
+
obj = Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
462
|
+
assert_equal %(#<Tater:#{ obj.object_id } @cascade=#{ obj.cascades? } @locale="#{ obj.locale }" @available=#{ obj.available }>), obj.inspect
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
describe '#freeze' do
|
467
|
+
it 'can be frozen' do
|
468
|
+
obj = Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
469
|
+
obj.freeze
|
470
|
+
|
471
|
+
assert obj.includes?('deep')
|
472
|
+
assert_equal 'This key is deeper', obj.translate('deep.key')
|
473
|
+
assert_equal %w[delimiter_only en fr separator_only], obj.available
|
474
|
+
|
475
|
+
assert obj.frozen?
|
476
|
+
end
|
477
|
+
|
478
|
+
it 'throws an error if modified after being frozen' do
|
479
|
+
obj = Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
480
|
+
obj.freeze
|
481
|
+
|
482
|
+
assert_raises(FrozenError) { obj.locale = 'en' }
|
483
|
+
assert_raises(FrozenError) { obj.load(messages: { more: 'Messages' }) }
|
484
|
+
end
|
485
|
+
end
|
458
486
|
end
|
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: 3.0.
|
4
|
+
version: 3.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Lecklider
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -132,6 +132,10 @@ 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
|
138
|
+
- lib/tater/version.rb
|
135
139
|
- test/fixtures/another.yml
|
136
140
|
- test/fixtures/fixtures.yml
|
137
141
|
- test/fixtures/messages/more.yml
|
@@ -159,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
159
163
|
- !ruby/object:Gem::Version
|
160
164
|
version: '2.0'
|
161
165
|
requirements: []
|
162
|
-
rubygems_version: 3.
|
166
|
+
rubygems_version: 3.3.7
|
163
167
|
signing_key:
|
164
168
|
specification_version: 4
|
165
169
|
summary: Minimal internationalization and localization library.
|