name_bank 0.1.5 → 0.2.0

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: f9a25e48d0f047db7cb393aa8545f0857daac15e5febc9f941b48c440f957930
4
- data.tar.gz: 8ad91e35492cf5e6e703246bbffef2804fa2456ce344bd7425532cb3ef145ae0
3
+ metadata.gz: 8bdb30000d395f629f88e6a14d346c067b39049295d999c13556ca36145b1923
4
+ data.tar.gz: a23fc5f83c129d4a1016be83766d4a3e4e496c634254d58ff3b2e8a1df2effd8
5
5
  SHA512:
6
- metadata.gz: 3ef330f55cd1fdcb87ca7f1391bc3b1c85af3c033e3bb9a4b349bea43651f8eaa4ea9f3ae9c1bac1db5e758ffb331ee403eb6f4639984dbc6246b95a7d78c5e2
7
- data.tar.gz: df0010e081ef9c6314934dc15c06f98af62b61aac7395c7c66b3f02a0e9540065b192112c58c109cbd3fa64c745c3f4daa3012f71f88b131cc6a6a271943c448
6
+ metadata.gz: c93d2d4749bde09cbb3717118e827370acb2f6dcf7227d388e5a84b75273122606b9887b962c1908778f505cac3630a13f40c8054bb02c7837051813c6366020
7
+ data.tar.gz: 1d176535f0208a680571a2f0156e80425e2adfbe6458ac26f956f06b53d25af41bf328f1bd5eb6b1d7e4d22ff8a58395fba4ba1a3ce09842480226780942183e
data/CHANGELOG.md CHANGED
@@ -4,6 +4,65 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.2.0] - 2026-08-08
8
+
9
+ An interface release. Sampling behaviour and the name data are unchanged — the
10
+ same `country:`, `gender:`, `rng:`, `variant:` and `script:` calls return the
11
+ same names as 0.1.6. What changed is what else the interface offers, and how
12
+ failures are named. Three breaking changes are listed below.
13
+
14
+ ### Added
15
+ - `NameBank.first_names` and `NameBank.last_names` return a country's whole
16
+ frequency-ordered pool as a frozen array. They take the same `country:`,
17
+ `gender:`, `variant:` and `script:` options as the samplers.
18
+ - `NameBank` is now a class and can be instantiated: `NameBank.new(data_dir:)`
19
+ reads pools from a directory of your own. The class-level methods delegate to
20
+ a default instance over the shipped data.
21
+
22
+ ### Removed
23
+ - **Breaking:** `NameBank.repository` and the `NameBank::Repository` class. The
24
+ accessor was never documented; everything it offered is on `NameBank` itself,
25
+ with `firstnames`/`lastnames` renamed to `first_names`/`last_names`.
26
+
27
+ - `NameBank::MalformedPool`, raised when a pool file is missing a schema key or
28
+ holds something other than a list. Files are validated once when read.
29
+ - `NameBank::UnknownGender` and `NameBank::EmptyPool`, so every failure the gem
30
+ can produce has a name. The README now lists all six.
31
+
32
+ ### Changed
33
+ - `variants(country:)` is memoized like `countries`, so repeated calls no longer
34
+ hit the filesystem.
35
+ - **Breaking:** `NameBank::Error` is now a module mixed into every error rather
36
+ than their shared superclass. `rescue NameBank::Error` works as before and now
37
+ covers every failure, including the two argument errors that previously
38
+ escaped it; each error also keeps its natural superclass, so
39
+ `rescue ArgumentError` still catches a bad `gender:` or `script:`. Raising or
40
+ instantiating `NameBank::Error` itself is no longer possible.
41
+ - **Breaking:** `NameBank::UnknownScript` now means what its name says — a
42
+ `script:` that is neither `:latin` nor `:native`, previously a bare
43
+ `ArgumentError`. The empty-pool case it used to signal is now
44
+ `NameBank::EmptyPool`.
45
+
46
+ ## [0.1.6] - 2026-07-24
47
+
48
+ Tooling and internals only — no API, data or behaviour change.
49
+
50
+ ### Added
51
+ - RuboCop (with rubocop-rspec, rubocop-rake, rubocop-performance) and
52
+ bundler-audit as development dependencies. The default Rake task now runs
53
+ `spec`, `rubocop` and `bundle:audit:check`.
54
+ - `rake release_check`: the same checks, but refreshing the advisory database
55
+ first. Run before releasing.
56
+
57
+ ### Changed
58
+ - Development dependencies moved from the gemspec to the `:development` group
59
+ in the Gemfile; the gemspec no longer declares any.
60
+ - Internals split for readability: `Repository#names_for_script` extracted from
61
+ `#pool`, and `SplitScripts.split_country` split into `pools`,
62
+ `add_native_pools`, `split_file` and `latin_only`.
63
+ - Specs reorganised — class specs under `spec/name_bank/`, one assertion per
64
+ example.
65
+
7
66
  ## [0.1.5] - 2026-07-24
8
67
 
9
68
  Metadata and documentation only — no API, data or behaviour change.
data/README.md CHANGED
@@ -34,18 +34,44 @@ NameBank.full_name(country: "DE", gender: :female, rng: rng)
34
34
  # => { firstname: "Sabine", lastname: "Müller" }
35
35
 
36
36
  # Just a given name or a surname:
37
- NameBank.first_name(country: "IT", gender: :male, rng: rng) # => "Giuseppe"
38
- NameBank.last_name(country: "JP", rng: rng) # => "Tanaka"
37
+ NameBank.first_name(country: "IT", gender: :male, rng: rng) # => "Amor"
38
+ NameBank.last_name(country: "JP", rng: rng) # => "Tsuru"
39
+
40
+ # Latin (default), or the country's native script where it has one:
41
+ NameBank.first_name(country: "JP", gender: :female, rng: rng) # => "Sasahara"
42
+ NameBank.first_name(country: "JP", gender: :female, rng: rng, script: :native) # => "ちゃこ"
43
+
44
+ # Default pool, or an alternate cultural pool where one exists:
45
+ NameBank.first_name(country: "US", gender: :male, rng: rng) # => "Abe"
46
+ NameBank.first_name(country: "US", gender: :male, rng: rng, variant: "african_american") # => "Roosevelt"
39
47
 
40
48
  # List available countries (ISO alpha-2 codes):
41
- NameBank.countries
42
- # => ["AE", "AF", "AL", ..., "ZA"]
49
+ NameBank.countries.size # => 106
50
+ NameBank.countries.first(3) # => ["AE", "AF", "AL"]
43
51
  ```
44
52
 
45
53
  Sampling is uniform over each pool and fully deterministic for a given
46
54
  `rng` — the same seed always yields the same name. `gender:` is `:male`
47
55
  or `:female`.
48
56
 
57
+ ## Whole pools
58
+
59
+ Where sampling one name is not enough — drawing many names without repeats,
60
+ applying your own weighting, or checking what a country actually ships —
61
+ take the pool itself. `first_names` and `last_names` accept the same
62
+ `country:`, `variant:` and `script:` options as the samplers, and return the
63
+ frequency-ordered pool as a frozen array:
64
+
65
+ ```ruby
66
+ NameBank.first_names(country: "DE", gender: :female).size # => 1500
67
+ NameBank.first_names(country: "DE", gender: :female).first(3) # => ["Nicole", "Sandra", "Sabine"]
68
+ NameBank.last_names(country: "JP", script: :native).first(3) # => ["佐藤", "鈴木", "田中"]
69
+ ```
70
+
71
+ Every method shown so far is also available on an instance, which lets you
72
+ point name_bank at your own directory of pool files:
73
+ `NameBank.new(data_dir: "…").first_name(country: "DE", gender: :male, rng: rng)`.
74
+
49
75
  ## Where it fits
50
76
 
51
77
  A factory_bot factory:
@@ -131,9 +157,10 @@ NameBank.first_name(country: "RU", gender: :male, rng: rng) # =
131
157
  NameBank.first_name(country: "RU", gender: :male, rng: rng, script: :native) # => "Алексей"
132
158
 
133
159
  NameBank.scripts(country: "RU") # => [:latin, :native]
134
- NameBank.scripts(country: "DE") # => [:latin] (Latin is Germany's script)
160
+ NameBank.scripts(country: "DE") # => [:latin]
135
161
  ```
136
162
 
163
+ Germany's script is Latin, so `DE` reports `:latin` only.
137
164
  `:latin` and `:native` sample from independent pools. For Latin-script countries
138
165
  `:native` returns the same (Latin) pool. Requesting a script with no names
139
166
  raises `NameBank::UnknownScript`.
@@ -151,6 +178,32 @@ NameBank.variants(country: "US") # => ["african_american"]
151
178
  NameBank.variants(country: "DE") # => []
152
179
  ```
153
180
 
181
+ ## Errors
182
+
183
+ Every error name_bank raises carries `NameBank::Error`, so one `rescue` covers
184
+ the lot. Each also keeps its natural Ruby superclass, so `rescue ArgumentError`
185
+ still catches the two that are genuinely caller mistakes:
186
+
187
+ | Error | Superclass | Raised when |
188
+ | --- | --- | --- |
189
+ | `NameBank::UnknownGender` | `ArgumentError` | `gender:` is neither `:male` nor `:female` |
190
+ | `NameBank::UnknownScript` | `ArgumentError` | `script:` is neither `:latin` nor `:native` |
191
+ | `NameBank::UnknownCountry` | `StandardError` | no pool file for that country code |
192
+ | `NameBank::UnknownVariant` | `StandardError` | no pool file for that variant |
193
+ | `NameBank::EmptyPool` | `StandardError` | the pool exists but holds no names |
194
+ | `NameBank::MalformedPool` | `StandardError` | a pool file is missing a key, or holds something other than a list |
195
+
196
+ The last two cannot occur with the shipped data; they matter when you point
197
+ `NameBank.new(data_dir:)` at pool files of your own.
198
+
199
+ ```ruby
200
+ begin
201
+ NameBank.first_name(country: "ZZ", gender: :male, rng: rng)
202
+ rescue NameBank::UnknownCountry => e
203
+ warn "no pool for #{e.message}"
204
+ end
205
+ ```
206
+
154
207
  ## Supported countries
155
208
 
156
209
  106 countries: Afghanistan, Albania, Algeria, Angola, Argentina, Austria,
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Declared as a class, not a module: NameBank itself is one (see lib/name_bank.rb).
4
+ class NameBank
5
+ # Mixed into every error this gem raises, so `rescue NameBank::Error` catches
6
+ # all of them while each error keeps its natural superclass. Errors whose
7
+ # valid values are a fixed, countable set are ArgumentErrors; errors that
8
+ # depend on what a data dir happens to hold are not.
9
+ module Error; end
10
+
11
+ # No pool file for this country code.
12
+ class UnknownCountry < StandardError
13
+ include Error
14
+ end
15
+
16
+ # No pool file for this variant of this country.
17
+ class UnknownVariant < StandardError
18
+ include Error
19
+ end
20
+
21
+ # A pool file is missing a schema key, or holds something other than a list.
22
+ class MalformedPool < StandardError
23
+ include Error
24
+ end
25
+
26
+ # The requested pool exists but holds no names.
27
+ class EmptyPool < StandardError
28
+ include Error
29
+ end
30
+
31
+ # script: was neither :latin nor :native.
32
+ class UnknownScript < ArgumentError
33
+ include Error
34
+ end
35
+
36
+ # gender: was neither :male nor :female.
37
+ class UnknownGender < ArgumentError
38
+ include Error
39
+ end
40
+ end
@@ -1,11 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module NameBank
3
+ require_relative "errors"
4
+
5
+ # Declared as a class, not a module: NameBank itself is one (see lib/name_bank.rb).
6
+ class NameBank
4
7
  # The pool-key schema: the base YAML keys, how native-script keys are named,
5
- # and how a gender maps to its key. Shared by the runtime Repository and the
8
+ # and how a gender maps to its key. Shared by the runtime NameBank and the
6
9
  # build-time SplitScripts tool so the key names live in one place.
7
10
  module PoolSchema
8
- KEYS = %w[firstnames_male firstnames_female lastnames].freeze
11
+ GIVEN_MALE = "firstnames_male"
12
+ GIVEN_FEMALE = "firstnames_female"
13
+ SURNAMES = "lastnames"
14
+
15
+ KEYS = [GIVEN_MALE, GIVEN_FEMALE, SURNAMES].freeze
9
16
 
10
17
  module_function
11
18
 
@@ -15,9 +22,9 @@ module NameBank
15
22
 
16
23
  def gender_key(gender)
17
24
  case gender
18
- when :male then "firstnames_male"
19
- when :female then "firstnames_female"
20
- else raise ArgumentError, "gender must be :male or :female, got #{gender.inspect}"
25
+ when :male then GIVEN_MALE
26
+ when :female then GIVEN_FEMALE
27
+ else raise UnknownGender, "gender must be :male or :female, got #{gender.inspect}"
21
28
  end
22
29
  end
23
30
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ require_relative "errors"
6
+ require_relative "pool_schema"
7
+
8
+ # Declared as a class, not a module: NameBank itself is one (see lib/name_bank.rb).
9
+ class NameBank
10
+ # Everything that touches the filesystem: reading pool files, checking their
11
+ # shape against PoolSchema, memoizing them, and listing what a data dir holds.
12
+ # NameBank itself never opens a file. Files are read once per instance, and
13
+ # validated once when read rather than on every pool access.
14
+ class PoolStore
15
+ def initialize(data_dir)
16
+ @data_dir = data_dir
17
+ @country_pools = {}
18
+ @variant_pools = {}
19
+ @variant_names = {}
20
+ end
21
+
22
+ def pools(country, variant)
23
+ variant ? variant_pools(country, variant) : country_pools(country)
24
+ end
25
+
26
+ def countries
27
+ @countries ||= yml_basenames(File.join(@data_dir, "countries"))
28
+ end
29
+
30
+ def variants(country)
31
+ @variant_names[country] ||= yml_basenames(File.join(@data_dir, "variants", country))
32
+ end
33
+
34
+ private
35
+
36
+ def country_pools(country)
37
+ @country_pools[country] ||= begin
38
+ path = File.join(@data_dir, "countries", "#{country}.yml")
39
+ raise UnknownCountry, country unless File.exist?(path)
40
+
41
+ read(path)
42
+ end
43
+ end
44
+
45
+ def variant_pools(country, variant)
46
+ @variant_pools[[country, variant]] ||= begin
47
+ path = File.join(@data_dir, "variants", country, "#{variant}.yml")
48
+ raise UnknownVariant, "#{country}/#{variant}" unless File.exist?(path)
49
+
50
+ read(path)
51
+ end
52
+ end
53
+
54
+ def read(path)
55
+ data = YAML.safe_load_file(path)
56
+ PoolSchema::KEYS.each do |key|
57
+ raise MalformedPool, "#{path}: #{key} must be a list of names" unless data[key].is_a?(Array)
58
+ end
59
+ data
60
+ end
61
+
62
+ def yml_basenames(dir)
63
+ return [] unless Dir.exist?(dir)
64
+
65
+ Dir.children(dir).select { |f| f.end_with?(".yml") }
66
+ .map { |f| File.basename(f, ".yml") }.sort
67
+ end
68
+ end
69
+
70
+ private_constant :PoolStore
71
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module NameBank
4
- VERSION = "0.1.5"
3
+ # Declared as a class, not a module: NameBank itself is one (see lib/name_bank.rb).
4
+ class NameBank
5
+ VERSION = "0.2.0"
5
6
  end
data/lib/name_bank.rb CHANGED
@@ -1,25 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "name_bank/version"
4
+ require_relative "name_bank/errors"
4
5
  require_relative "name_bank/pool_schema"
5
- require_relative "name_bank/repository"
6
-
7
- module NameBank
8
- Error = Class.new(StandardError)
9
- UnknownCountry = Class.new(Error)
10
- UnknownVariant = Class.new(Error)
11
- UnknownScript = Class.new(Error)
6
+ require_relative "name_bank/pool_store"
12
7
 
8
+ # Authentic, gender-matched given names and surnames for 106 countries,
9
+ # addressed by ISO alpha-2 country code. Sampling is uniform and deterministic
10
+ # from a caller-supplied RNG — no global locale or random state.
11
+ #
12
+ # The class-level methods delegate to a default instance over the shipped data
13
+ # dir; instantiate with another data_dir to read pools from elsewhere. Country
14
+ # and variant files are read lazily and memoized per instance.
15
+ #
16
+ # Every error raised here is a NameBank::Error; see the README for the list.
17
+ class NameBank
13
18
  DATA_DIR = File.expand_path("../data", __dir__)
14
19
 
15
- module_function
20
+ # Every class-level method forwards to the default instance, so the signatures
21
+ # live once — on the instance methods below.
22
+ class << self
23
+ def default
24
+ @default ||= new
25
+ end
26
+
27
+ def first_name(...) = default.first_name(...)
28
+ def last_name(...) = default.last_name(...)
29
+ def full_name(...) = default.full_name(...)
30
+ def first_names(...) = default.first_names(...)
31
+ def last_names(...) = default.last_names(...)
32
+ def countries(...) = default.countries(...)
33
+ def variants(...) = default.variants(...)
34
+ def scripts(...) = default.scripts(...)
35
+ end
36
+
37
+ def initialize(data_dir: DATA_DIR)
38
+ @store = PoolStore.new(data_dir)
39
+ end
16
40
 
17
41
  def first_name(country:, gender:, rng:, variant: nil, script: :latin)
18
- repository.firstnames(country: country, gender: gender, variant: variant, script: script).sample(random: rng)
42
+ first_names(country: country, gender: gender, variant: variant, script: script).sample(random: rng)
19
43
  end
20
44
 
21
45
  def last_name(country:, rng:, variant: nil, script: :latin)
22
- repository.lastnames(country: country, variant: variant, script: script).sample(random: rng)
46
+ last_names(country: country, variant: variant, script: script).sample(random: rng)
23
47
  end
24
48
 
25
49
  def full_name(country:, gender:, rng:, variant: nil, script: :latin)
@@ -29,19 +53,46 @@ module NameBank
29
53
  }
30
54
  end
31
55
 
56
+ # The whole frequency-ordered pool, frozen. The name strings stay mutable.
57
+ def first_names(country:, gender:, variant: nil, script: :latin)
58
+ pool(country, variant, PoolSchema.gender_key(gender), script)
59
+ end
60
+
61
+ def last_names(country:, variant: nil, script: :latin)
62
+ pool(country, variant, PoolSchema::SURNAMES, script)
63
+ end
64
+
32
65
  def countries
33
- repository.countries
66
+ @store.countries
34
67
  end
35
68
 
36
69
  def variants(country:)
37
- repository.variants(country: country)
70
+ @store.variants(country)
38
71
  end
39
72
 
73
+ # The script forms this country offers, not writing systems — see CONTEXT.md.
40
74
  def scripts(country:)
41
- repository.scripts(country: country)
75
+ data = @store.pools(country, nil)
76
+ PoolSchema::KEYS.any? { |k| data[PoolSchema.native_key(k)]&.any? } ? %i[latin native] : %i[latin]
77
+ end
78
+
79
+ private
80
+
81
+ def pool(country, variant, key, script)
82
+ names = names_for_script(@store.pools(country, variant), key, script)
83
+ raise EmptyPool, "#{country}/#{script}" if names.empty?
84
+
85
+ names.freeze
42
86
  end
43
87
 
44
- def repository
45
- @repository ||= Repository.new(data_dir: DATA_DIR)
88
+ def names_for_script(data, key, script)
89
+ case script
90
+ when :latin then data.fetch(key)
91
+ when :native
92
+ native = data[PoolSchema.native_key(key)]
93
+ native && !native.empty? ? native : data.fetch(key)
94
+ else
95
+ raise UnknownScript, "script must be :latin or :native, got #{script.inspect}"
96
+ end
46
97
  end
47
98
  end
metadata CHANGED
@@ -1,42 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: name_bank
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Bartels
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: rspec
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - "~>"
17
- - !ruby/object:Gem::Version
18
- version: '3.13'
19
- type: :development
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - "~>"
24
- - !ruby/object:Gem::Version
25
- version: '3.13'
26
- - !ruby/object:Gem::Dependency
27
- name: rake
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '13.0'
33
- type: :development
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '13.0'
11
+ dependencies: []
40
12
  description: 'NameBank generates authentic, gender-matched given names and surnames
41
13
  for 106 countries, addressed by ISO alpha-2 country code, including native-script
42
14
  pools (Cyrillic, Arabic, Han, Hangul, Kana, Greek, Hebrew and more) for 35 of them.
@@ -164,8 +136,9 @@ files:
164
136
  - data/variants/US/african_american.yml
165
137
  - docs/name-counts.md
166
138
  - lib/name_bank.rb
139
+ - lib/name_bank/errors.rb
167
140
  - lib/name_bank/pool_schema.rb
168
- - lib/name_bank/repository.rb
141
+ - lib/name_bank/pool_store.rb
169
142
  - lib/name_bank/version.rb
170
143
  homepage: https://github.com/roughneck/name_bank
171
144
  licenses:
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "yaml"
4
-
5
- module NameBank
6
- # Lazy, memoized loader of per-country name pools stored as YAML in data_dir.
7
- class Repository
8
- def initialize(data_dir:)
9
- @data_dir = data_dir
10
- @countries_cache = {}
11
- @variants_cache = {}
12
- end
13
-
14
- def firstnames(country:, gender:, variant: nil, script: :latin)
15
- pool(load(country, variant), PoolSchema.gender_key(gender), script, country)
16
- end
17
-
18
- def lastnames(country:, variant: nil, script: :latin)
19
- pool(load(country, variant), "lastnames", script, country)
20
- end
21
-
22
- def scripts(country:)
23
- data = load(country, nil)
24
- PoolSchema::KEYS.any? { |k| data[PoolSchema.native_key(k)]&.any? } ? %i[latin native] : %i[latin]
25
- end
26
-
27
- def countries
28
- @countries ||= yml_basenames(File.join(@data_dir, "countries"))
29
- end
30
-
31
- def variants(country:)
32
- yml_basenames(File.join(@data_dir, "variants", country))
33
- end
34
-
35
- private
36
-
37
- def pool(data, key, script, country)
38
- names =
39
- case script
40
- when :latin then data.fetch(key)
41
- when :native
42
- native = data[PoolSchema.native_key(key)]
43
- native && !native.empty? ? native : data.fetch(key)
44
- else
45
- raise ArgumentError, "script must be :latin or :native, got #{script.inspect}"
46
- end
47
- raise UnknownScript, "#{country}/#{script}" if names.nil? || names.empty?
48
-
49
- names
50
- end
51
-
52
- def load(country, variant)
53
- variant ? load_variant(country, variant) : load_country(country)
54
- end
55
-
56
- def load_country(country)
57
- @countries_cache[country] ||= begin
58
- path = File.join(@data_dir, "countries", "#{country}.yml")
59
- raise UnknownCountry, country unless File.exist?(path)
60
-
61
- YAML.safe_load_file(path)
62
- end
63
- end
64
-
65
- def load_variant(country, variant)
66
- @variants_cache[[country, variant]] ||= begin
67
- path = File.join(@data_dir, "variants", country, "#{variant}.yml")
68
- raise UnknownVariant, "#{country}/#{variant}" unless File.exist?(path)
69
-
70
- YAML.safe_load_file(path)
71
- end
72
- end
73
-
74
- def yml_basenames(dir)
75
- return [] unless Dir.exist?(dir)
76
-
77
- Dir.children(dir).select { |f| f.end_with?(".yml") }
78
- .map { |f| File.basename(f, ".yml") }.sort
79
- end
80
- end
81
- end