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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b543d8ed6a28b2f4059b2c1fc63593bc2f7ced7b2cc9d62e786c78263b97ec35
4
- data.tar.gz: 0042276af4078a436e7f34e1f50500e5dd92649ae060b24921a98f5dfc859789
3
+ metadata.gz: '08a174e27ecb08955ba15997b8b71995aff8a77cbf82bb08514ba5dcd6e404dd'
4
+ data.tar.gz: c9f78478bc5f1b7a48b038add4f3c05230dc6083ccada5d1de00974b1dbd08f8
5
5
  SHA512:
6
- metadata.gz: cd927d5425f5a8b7356fafb72db449d57494042369eb9ba0018a2ba7d68893fe24c0d633d75a93ab8ff1d3e8f2367d148e87c5bc068ec1717afa27d1168cec8c
7
- data.tar.gz: 4f0a917429aef2c443288f9c338a0471f86fb608585e29e5fdc8b3f04b8ecfe654ecdc7e09daed29e99b19ddb3bf1a4175057d6db77c64b495b25a232f2c6eb7
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 evalled.
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 pluggable, it does what it does and that's it.
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 orininally going to call this library "Translator" but with a [[https://en.wikipedia.org/wiki/Numeronym][numeronym]]
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.
@@ -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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ class Tater
3
+ VERSION = '3.0.5'
4
+ 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
- require_relative 'tater/utils'
7
- require_relative 'tater/hash' unless Hash.method_defined?(:except)
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
- # Gotta recalculate available locales after updating.
92
- remove_instance_variable(:@available) if instance_variable_defined?(:@available)
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
- @locale = locale.to_s if available?(locale)
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
- message =
213
- if options.key?(:locales)
214
- options[:locales].append(@locale) if @locale && !options[:locales].include?(@locale)
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
- options[:locales].find do |accept|
217
- found = lookup(key, locale: accept, cascade: options[:cascade])
227
+ options[:locales].find do |accept|
228
+ found = lookup(key, locale: accept, cascade: options[:cascade])
218
229
 
219
- break found unless found.nil?
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
- !message.nil?
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.2
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: 2021-12-19 00:00:00.000000000 Z
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.2.32
166
+ rubygems_version: 3.3.7
163
167
  signing_key:
164
168
  specification_version: 4
165
169
  summary: Minimal internationalization and localization library.