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 +4 -4
- data/CHANGELOG.md +59 -0
- data/README.md +58 -5
- data/lib/name_bank/errors.rb +40 -0
- data/lib/name_bank/pool_schema.rb +13 -6
- data/lib/name_bank/pool_store.rb +71 -0
- data/lib/name_bank/version.rb +3 -2
- data/lib/name_bank.rb +66 -15
- metadata +4 -31
- data/lib/name_bank/repository.rb +0 -81
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8bdb30000d395f629f88e6a14d346c067b39049295d999c13556ca36145b1923
|
|
4
|
+
data.tar.gz: a23fc5f83c129d4a1016be83766d4a3e4e496c634254d58ff3b2e8a1df2effd8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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) # => "
|
|
38
|
-
NameBank.last_name(country: "JP", rng: rng) # => "
|
|
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"
|
|
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]
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
19
|
-
when :female then
|
|
20
|
-
else raise
|
|
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
|
data/lib/name_bank/version.rb
CHANGED
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
+
@store.countries
|
|
34
67
|
end
|
|
35
68
|
|
|
36
69
|
def variants(country:)
|
|
37
|
-
|
|
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
|
-
|
|
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
|
|
45
|
-
|
|
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.
|
|
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/
|
|
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:
|
data/lib/name_bank/repository.rb
DELETED
|
@@ -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
|