random_name_generator 4.0.2 → 4.0.4

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: 0e3511685f7df265b945b6254e889eac64d5857309623a5c09f91f309f9fc52d
4
- data.tar.gz: bc756891b8f9adda7c154aed1b6865f3e4746f6d57ca87c2c01357ea9d9912c4
3
+ metadata.gz: d87857214e4e8fc700351d66eaf1c4858b8b3eff7149d00355abdfc7d2388f2a
4
+ data.tar.gz: d8ef37050bbdd23e0be3dd44f708b840033da928a6db157e7e73237cc4d2201c
5
5
  SHA512:
6
- metadata.gz: dced56da054f6c842dc74cb2ac40baae325b2edea5645494fdadf6f3243259137e89fb32924f50052487c14d4d151944efc5d923ed1bef2698effb7f306c6357
7
- data.tar.gz: 25b9619caf21f3e0e04561eed930839d2f599564e77ea194c43f76944a191220732851f8e6e961cf2da8a8568f9f5102b49751c1e866c021a224ed8fb34acd0c
6
+ metadata.gz: 8eafd322e156f87f867941204975a214686cb24fee1dbdd676e2a3b2d3ddb58ba596f04d27b35437eb9d3baf13456a0a0fc164fb34b60037c5cc351b405b0ffe
7
+ data.tar.gz: df1c293f18705b8eac7d46c8f3beac17f7d9fedd312a9fd2ab0f12a57c25d5af5ce9c0cc3b7523999a1072e2e0f6ee2b4e81e94921104e6f9da83ae0a0eef289
data/AI-BOM.md ADDED
@@ -0,0 +1,85 @@
1
+ # AI Bill of Materials — random_name_generator
2
+
3
+ _Last updated: 2026-07-19 · random_name_generator v4.0.2_
4
+
5
+ An inventory of every AI component associated with this repository — development
6
+ tools used to build it, AI-generated content shipped within it, and external AI
7
+ services it integrates with (current or planned). Modeled on the SBOM concept
8
+ applied to AI systems.
9
+
10
+ **AI was first used in the `4.0.0` release.** Everything through the `3.x` line
11
+ was authored without AI assistance.
12
+
13
+ ---
14
+
15
+ ## 1. Development Tools
16
+
17
+ AI tools used to author this codebase. Not part of the shipped gem, but relevant
18
+ to provenance.
19
+
20
+ | Tool | Vendor | Role | Notes |
21
+ |------|--------|------|-------|
22
+ | Claude Code | Anthropic | AI coding assistant | Introduced in 4.0.0; drives the [`lang-gen`](./.claude/skills/lang-gen/SKILL.md) skill and general maintenance |
23
+
24
+ ---
25
+
26
+ ## 2. AI Audits
27
+
28
+ Formal code reviews performed by AI models. Full reports in `docs/`.
29
+
30
+ | Date | Model | Report | Version |
31
+ |------|-------|--------|---------|
32
+ | 2026-07-19 | Claude Fable 5 | [`docs/AUDIT_Fable_5.md`](./docs/AUDIT_Fable_5.md) | v4.0.2 |
33
+
34
+ ---
35
+
36
+ ## 3. AI-Generated Content
37
+
38
+ Syllable data and tooling produced with AI assistance and shipped in the gem.
39
+ All output was human-reviewed before commit.
40
+
41
+ | Artifact | Type | Tool | Notes |
42
+ |----------|------|------|-------|
43
+ | [`lang-gen`](./.claude/skills/lang-gen/SKILL.md) | Claude Code skill | — | Generates a fully integrated language from a plain-English theme |
44
+ | [German Curse](./lib/languages/experimental/german-curse.txt) (`GERMAN_CURSE`) | Language syllable file | `lang-gen` | Experimental; seeded from real roots, extended with invented syllables |
45
+ | [Demonic](./lib/languages/experimental/demonic.txt) (`DEMONIC`) | Language syllable file | `lang-gen` | Experimental; completed from infernal-name source lists |
46
+ | [Portable lang-gen prompt](./docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md) | Text prompt | — | LLM-agnostic version of the skill |
47
+
48
+ ---
49
+
50
+ ## 4. Algorithms Implemented
51
+
52
+ Generative logic built directly into the gem. **No machine-learning models and
53
+ no external ML dependencies** — all name generation is deterministic,
54
+ in-process, and driven by plain syllable-adjacency rules.
55
+
56
+ | Algorithm | Location | Notes |
57
+ |-----------|----------|-------|
58
+ | Probabilistic syllable composition | `RandomNameGenerator::Generator` | Random prefix/middle/suffix assembly honoring `+v`/`+c`/`-v`/`-c` adjacency flags |
59
+ | Syllable parsing / classification | `RandomNameGenerator::Syllable` | Parses each language-file line into a flagged syllable |
60
+
61
+ This is a rules-based random generator, not an AI/ML system. It is listed here
62
+ for completeness so the boundary between "AI-assisted authoring" and "AI at
63
+ runtime" is explicit.
64
+
65
+ ---
66
+
67
+ ## 5. External AI Integrations
68
+
69
+ random_name_generator ships **zero external AI service dependencies**. It makes
70
+ no network calls and requires no API keys. None are planned.
71
+
72
+ | Service | Type | Status |
73
+ |---------|------|--------|
74
+ | — | — | None (current or planned) |
75
+
76
+ ---
77
+
78
+ ## 6. References
79
+
80
+ | Document | Purpose |
81
+ |----------|---------|
82
+ | [`docs/AUDIT_Fable_5.md`](./docs/AUDIT_Fable_5.md) | Full-codebase audit with empirically verified findings |
83
+ | [`.claude/skills/lang-gen/SKILL.md`](./.claude/skills/lang-gen/SKILL.md) | The lang-gen language-generation skill |
84
+ | [`README.md`](./README.md) | Project overview, including the lang-gen workflow |
85
+ | [`docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md`](./docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md) | Paste-into-any-LLM version of the prompt |
data/Gemfile CHANGED
@@ -7,14 +7,13 @@ gemspec
7
7
 
8
8
  gem "pry"
9
9
  gem "rake", "~> 13.4"
10
- gem "slop", "~> 4.10.1"
11
10
 
12
11
  group :development do
12
+ gem "logger" # logger leaves the default gems in Ruby 4.0; Reek (via dry-core) requires it implicitly.
13
13
  gem "reek"
14
14
  gem "rspec", "~> 3.12.0"
15
15
  gem "rubocop"
16
16
  gem "rubocop-rake", require: false
17
17
  gem "rubocop-rspec", require: false
18
- # tsort left the default gems in Ruby 4.1; RuboCop requires it implicitly.
19
- gem "tsort"
18
+ gem "tsort" # tsort left the default gems in Ruby 4.1; RuboCop requires it implicitly.
20
19
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- random_name_generator (4.0.2)
4
+ random_name_generator (4.0.4)
5
+ slop (~> 4.10)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
@@ -40,6 +41,7 @@ GEM
40
41
  zeitwerk (~> 2.6)
41
42
  json (2.7.2)
42
43
  language_server-protocol (3.17.0.3)
44
+ logger (1.7.0)
43
45
  method_source (1.1.0)
44
46
  parallel (1.26.3)
45
47
  parser (3.3.5.0)
@@ -100,6 +102,7 @@ PLATFORMS
100
102
  x86_64-linux
101
103
 
102
104
  DEPENDENCIES
105
+ logger
103
106
  pry
104
107
  rake (~> 13.4)
105
108
  random_name_generator!
@@ -108,7 +111,6 @@ DEPENDENCIES
108
111
  rubocop
109
112
  rubocop-rake
110
113
  rubocop-rspec
111
- slop (~> 4.10.1)
112
114
  tsort
113
115
 
114
116
  BUNDLED WITH
data/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![Build and Test](https://github.com/folkengine/random_name_generator/actions/workflows/ruby.yml/badge.svg)](https://github.com/folkengine/random_name_generator/actions/workflows/ruby.yml)
5
5
  [![Code Climate](https://codeclimate.com/github/folkengine/random_name_generator/badges/gpa.svg)](https://codeclimate.com/github/folkengine/random_name_generator)
6
6
  [![Inline docs](http://inch-ci.org/github/folkengine/random_name_generator.svg?branch=master)](http://inch-ci.org/github/folkengine/random_name_generator)
7
+ [![AI BOM](https://img.shields.io/badge/AI--BOM-tracked-blueviolet)](./AI-BOM.md)
7
8
 
8
9
  Ruby port of
9
10
  [java-random-name-generator](https://github.com/folkengine/java-random-name-generator),
@@ -40,7 +41,7 @@ Or install it yourself as:
40
41
  generate a first and last name for you:
41
42
 
42
43
  ```shell
43
- $> exe/random_name_generator [-egrkfcxdß?]
44
+ $> exe/random_name_generator [-egrkbfcxdß?]
44
45
  ```
45
46
 
46
47
  You can also install it so that it's instantly available to you:
@@ -58,12 +59,14 @@ RandomNameGenerator comes with several styles of syllable files:
58
59
  [Fantasy](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/fantasy.txt),
59
60
  [Goblin](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/goblin.txt),
60
61
  [Roman](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/roman.txt),
62
+ [Klingon](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/klingon.txt),
63
+ [Welsh](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/welsh.txt),
61
64
  and
62
- [Klingon](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/klingon.txt).
65
+ [Belter](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/belter.txt).
63
66
  By default it uses Fantasy. Instantiate RandomNameGenerator and then
64
67
  call compose on the object to generate a random name. If you don't pass
65
68
  in the number of syllables you want for your name to compose, it will
66
- randomly pick between 3 and 6.
69
+ randomly pick between 2 and 5.
67
70
 
68
71
  ```ruby
69
72
  require 'random_name_generator'
@@ -152,9 +155,9 @@ https://github.com/folkengine/random_name_generator.
152
155
  This ships with a skill designed to work with [Claude Code](https://claude.com/claude-code)
153
156
  and other LLMs under `.claude/skills/`.
154
157
 
155
- - [lang-gen](.claude/skills/lang-gen/SKILL.md) Generate a new random_name_generator language from a free-text theme (e.g. "german curse words") assembles flagged pre/mid/sur syllable collections, registers the File constant, adds a smoke spec and README entry, and samples names to verify.
158
+ - [lang-gen](.claude/skills/lang-gen/SKILL.md): Generate a new random_name_generator language from a free-text theme (e.g. "german curse words"), assembles flagged pre/mid/sur syllable collections, registers the File constant, adds a smoke spec and README entry, and samples names to verify.
156
159
 
157
- There is also [a text version](docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md) of the prompt that can be pasted into an LLM.
160
+ There is also [a text version](docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md) of the prompt that can be pasted into an LLM.
158
161
 
159
162
  ### lang-gen
160
163
 
@@ -0,0 +1,151 @@
1
+ # random_name_generator Repository Audit — Claude Fable 5
2
+
3
+ _Date:_ 2026-07-19
4
+ _Repo:_ `random_name_generator` v4.0.2 (working tree, including the in-flight Demonic language and CLI additions)
5
+ _Model:_ Claude Fable 5 (`claude-fable-5`)
6
+ _Audit basis:_ Full codebase read + empirical verification — every defect below was reproduced with a live repro before being reported.
7
+
8
+ > **Remediation:** all nine findings were fixed on 2026-07-19, immediately following
9
+ > this audit, with regression specs added for each (suite grew from 61 to 168
10
+ > examples). The findings below describe the codebase as audited.
11
+
12
+ ---
13
+
14
+ ## Executive Summary
15
+
16
+ `random_name_generator` is a small, clean, dependency-light gem that does what it says: green test suite (61 examples, 0 failures), RuboCop clean at repo settings (11 files, no offenses), no runtime gem dependencies, and a simple two-class core (`Generator`, `Syllable`) that is easy to hold in your head.
17
+
18
+ The audit found **3 high-severity defects** — one packaging, two runtime — and a cluster of medium/low issues concentrated in `Syllable`'s letter classification and error paths. None affect the happy path exercised by the specs, which is precisely why they have survived: the suite tests that names compose, not what happens at the edges.
19
+
20
+ | # | Finding | Severity | Effort to fix |
21
+ |---|---------|----------|---------------|
22
+ | 1 | CLI requires `slop`, but the gemspec declares no runtime dependency on it | High | Low |
23
+ | 2 | `compose(1)` crashes (`String#map`) despite documented single-syllable support | High | Low |
24
+ | 3 | Unsatisfiable adjacency flags hang the process in an infinite loop | High | Medium |
25
+ | 4 | Blank line in a language file crashes `Generator#refresh` | Medium | Low |
26
+ | 5 | Documented `Syllable` clone constructor crashes | Medium | Low |
27
+ | 6 | Cyrillic letters are in neither letter set — all flags in the four `-ru` files are silent no-ops | Medium | Medium |
28
+ | 7 | Empty-syllable guard is doubly broken (unreachable + malformed `raise`) | Low | Low |
29
+ | 8 | `y` classified as both vowel and consonant | Low | Low |
30
+ | 9 | Documentation drift on syllable-count range (README vs code vs spec) | Low | Low |
31
+
32
+ ---
33
+
34
+ ## Scope and Method
35
+
36
+ **Verified by direct read (complete files):**
37
+
38
+ - `lib/random_name_generator.rb`
39
+ - `lib/random_name_generator/syllable.rb`
40
+ - `lib/random_name_generator/version.rb`
41
+ - `exe/random_name_generator`
42
+ - `spec/random_name_generator_spec.rb` and both spec fixture languages
43
+ - `random_name_generator.gemspec`, `Gemfile`, `Rakefile`
44
+ - `.github/workflows/ruby.yml`
45
+ - All 13 language files (curated, `-ru`, and experimental)
46
+
47
+ **Verified empirically:** every High/Medium finding was reproduced by executing the failing call under the bundle, including a `Timeout`-guarded repro for the infinite loop and a crafted two-line language file for the blank-line crash (which was also hit organically during this session, completing the WIP `demonic.txt`).
48
+
49
+ **Health signals:**
50
+
51
+ - `bundle exec rspec`: **61 examples, 0 failures**
52
+ - `bundle exec rubocop`: **11 files inspected, no offenses**
53
+ - `bundle exec rake` (default: spec + rubocop): green
54
+ - Runtime dependencies: none declared (see Finding 1 for the catch)
55
+
56
+ ---
57
+
58
+ ## Findings
59
+
60
+ ### 1. Missing runtime dependency: the installed CLI cannot start (High)
61
+
62
+ `exe/random_name_generator` line 6 does `require "slop"`, but `slop` is declared only in the **Gemfile** (`gem "slop", "~> 4.10.1"`), which end users never see. The gemspec's dependency section is an empty template comment.
63
+
64
+ Consequence: after `gem install random_name_generator`, running `random_name_generator` raises `LoadError: cannot load such file -- slop` on any machine that doesn't coincidentally have slop installed. Every CI run and local dev invocation goes through Bundler, so this is invisible to the test suite by construction.
65
+
66
+ **Fix (low effort):** add `spec.add_dependency "slop", "~> 4.10"` to the gemspec and remove it from the Gemfile.
67
+
68
+ ### 2. `compose(1)` crashes despite documented support (High)
69
+
70
+ `syllable.rb`'s header documentation states: *"In case of 1 syllable, name will be chosen from amongst the prefixes."* The code disagrees:
71
+
72
+ - `Generator#compose_array` (`lib/random_name_generator.rb:89`) returns a **String** (`@pre.to_s.capitalize`) when `count < 2`, an Array otherwise.
73
+ - `Generator#compose` (`lib/random_name_generator.rb:97`) unconditionally calls `.map` on that result.
74
+
75
+ Repro: `RandomNameGenerator.new(ELVEN).compose(1)` → `NoMethodError: undefined method 'map' for an instance of String`.
76
+
77
+ The type-splitting return also makes `compose_array(1)` a misnomer — callers get a String from a method named `_array`. **Fix:** have `compose_array` return `[@pre]` for `count < 2`; both bugs disappear and the return type becomes honest.
78
+
79
+ ### 3. Unsatisfiable flag combinations hang the process (High)
80
+
81
+ `Generator#determine_next_syllable` (`lib/random_name_generator.rb:126-133`) is a bare `loop` that resamples until it draws a compatible syllable. If **no** syllable in the target bucket is compatible, it spins forever.
82
+
83
+ Repro (hangs, killed by timeout): a language whose only prefix is `-ba +v` (next must start with a vowel) and whose only suffix is `+kar`. Since the README explicitly invites users to supply their own syllable files, this is reachable input, not just an authoring mistake — one bad file freezes the host application with no diagnostic.
84
+
85
+ **Fix (medium effort):** track attempts (or pre-filter the bucket by compatibility) and raise a descriptive error when no compatible syllable exists.
86
+
87
+ ### 4. Blank lines in language files crash loading (Medium)
88
+
89
+ `Generator#refresh` (`lib/random_name_generator.rb:138`) guards with `line.empty?`, but lines from `readlines` retain `"\n"`, so the guard never fires; `Syllable#parse_args` then splits the whitespace-only string into `[]` and calls `nil.empty?` (`syllable.rb:137`) → `NoMethodError`.
90
+
91
+ This is not hypothetical: the committed WIP `demonic.txt` used blank lines as bucket separators and crashed on load — likely part of why that file sat unfinished. **Fix:** `line.strip.empty?` in `refresh` (one word), or skip whitespace-only lines in `Syllable`.
92
+
93
+ ### 5. Documented `Syllable` clone constructor crashes (Medium)
94
+
95
+ The `Syllable` header advertises `Syllable.new(syllable)` as a cloning idiom, and `initialize` has an explicit `args.is_a?(Syllable)` branch for it (`syllable.rb:56-60`). But line 49 — `@raw = args.strip` — executes first, and `Syllable` defines no `strip`.
96
+
97
+ Repro: `Syllable.new(Syllable.new("-foo +c"))` → `NoMethodError: undefined method 'strip' for an instance of RandomNameGenerator::Syllable`. The clone path is dead code; nothing in the gem or specs exercises it. **Fix:** `@raw = args.to_s.strip` (Syllable already implements `to_s`), plus a spec.
98
+
99
+ ### 6. Adjacency flags are silent no-ops for Cyrillic (Medium)
100
+
101
+ `Syllable::VOWELS` and `CONSONANTS` (`syllable.rb:45-46`) contain only Latin/IPA characters. For a Cyrillic syllable, `vowel_first?`, `consonant_first?`, `vowel_last?`, and `consonant_last?` are all **false** (verified: `-анг` reports neither vowel- nor consonant-final).
102
+
103
+ Consequence: in `next_incompatible?`/`previous_incompatible?`, nothing can ever be incompatible, so every `+v/+c/-v/-c` flag in the four `-ru` language files — and all four carry them — is dead weight. Names still generate; they just ignore constraints the language authors wrote, silently. **Fix:** add Cyrillic vowels (а е ё и о у ы э ю я) and consonants to the sets, or classify by Unicode-aware rules.
104
+
105
+ ### 7. The empty-syllable guard is doubly broken (Low)
106
+
107
+ `syllable.rb:137`: `raise ArgumentError "Empty String is not allowed." if syll.empty?`
108
+
109
+ - **Unreachable as intended:** an empty or whitespace input produces `args[0] == nil`, so `syll.empty?` raises `NoMethodError` on `nil` before the guard can act (this is the crash surfacing in Findings 4 and the `Syllable.new("")` repro).
110
+ - **Malformed if it were reached:** the missing comma means `ArgumentError "…"` is parsed as a method call, which would itself raise `NoMethodError`, not the intended `ArgumentError`.
111
+
112
+ **Fix:** `raise ArgumentError, "…" if syll.nil? || syll.empty?`.
113
+
114
+ ### 8. `y` is both a vowel and a consonant (Low)
115
+
116
+ `y` appears in both letter sets, so `+v` and `+c` flags each accept y-initial syllables. Linguistically defensible for a semivowel, but it's undocumented and asymmetric (no other letter is dual-classified). Worth a comment if intentional.
117
+
118
+ ### 9. Documentation drift on syllable counts (Low)
119
+
120
+ Three sources disagree with the code (`pick_number_of_syllables` samples from a weighted 2–5 distribution):
121
+
122
+ - README: "it will randomly pick between 3 and 6"
123
+ - Module doc in `random_name_generator.rb`: "between 2 and 5" ✓ (correct)
124
+ - Spec title: "returns an integer between 1 and 6" (assertion actually checks 1..5)
125
+
126
+ Also cosmetic: the CLI help text spells "syllable" as "eyllable" in four option descriptions.
127
+
128
+ ---
129
+
130
+ ## Test Coverage Assessment
131
+
132
+ The suite is a smoke suite: it proves every bundled language loads and composes, and that syllable counts come out right. It does **not** cover:
133
+
134
+ - Adjacency-flag semantics (no spec would catch Finding 6 — the flags could be deleted entirely and the suite stays green)
135
+ - Any error path (Findings 2, 4, 5, 7 are all crashes in paths with zero coverage)
136
+ - The CLI (`exe/` has no test at all; Finding 1 ships silently)
137
+
138
+ The fixture languages (`test-micro.txt`, `test-tiny.txt`) are well-designed for flag testing — `test-tiny.txt` deliberately exercises every flag combination — but no assertion actually inspects flag behavior.
139
+
140
+ ---
141
+
142
+ ## Recommendations (priority order)
143
+
144
+ 1. **Gemspec:** declare the `slop` runtime dependency. One line; unbreaks the installed CLI.
145
+ 2. **`compose_array`:** return `[@pre]` for `count < 2`. Fixes `compose(1)` and the return-type lie.
146
+ 3. **`refresh`:** strip lines before the empty check. Fixes blank-line crashes and makes hand-authored files forgiving.
147
+ 4. **`determine_next_syllable`:** bound the retry loop with a clear error. Turns a hang into a diagnosis.
148
+ 5. **Letter sets:** add Cyrillic ranges so the `-ru` flags do what their authors intended; add a flag-behavior spec pinned to `test-tiny.txt`.
149
+ 6. **Cleanups:** fix the `raise` syntax and clone constructor, correct the README/spec-title ranges, fix "eyllable".
150
+
151
+ None of these threaten the gem's architecture, which is sound for its size. The pattern across all findings is the same: the edges were never walked. A dozen targeted specs would lock in every fix above.
@@ -8,10 +8,11 @@ require "slop"
8
8
  lang = RandomNameGenerator::FANTASY
9
9
 
10
10
  opts = Slop.parse do |o|
11
- o.bool "-e", "--elven", "Use Elven eyllable file"
12
- o.bool "-g", "--goblin", "Use Goblin eyllable file"
13
- o.bool "-r", "--roman", "Use Roman eyllable file"
14
- o.bool "-k", "--klingon", "Use Klingon eyllable file"
11
+ o.bool "-e", "--elven", "Use Elven syllable file"
12
+ o.bool "-g", "--goblin", "Use Goblin syllable file"
13
+ o.bool "-r", "--roman", "Use Roman syllable file"
14
+ o.bool "-k", "--klingon", "Use Klingon syllable file"
15
+ o.bool "-b", "--belter", "Use Belter syllable file"
15
16
  o.bool "-f", "--flipmode", "Flip mode in effect"
16
17
  o.bool "-c", "--cyrillic", "Use Cyrillic mode"
17
18
  o.bool "-x", "--xrated", "Generate Curse words [NEEDS WORK]"
@@ -30,6 +31,7 @@ else
30
31
  lang = RandomNameGenerator::GOBLIN if opts.goblin?
31
32
  lang = RandomNameGenerator::ROMAN if opts.roman?
32
33
  lang = RandomNameGenerator::KLINGON if opts.klingon?
34
+ lang = RandomNameGenerator::BELTER if opts.belter?
33
35
  lang = RandomNameGenerator::CURSE if opts.xrated?
34
36
  lang = RandomNameGenerator::DEMONIC if opts.demonic?
35
37
  lang = RandomNameGenerator::GERMAN_CURSE if opts.german_curse?
@@ -0,0 +1,102 @@
1
+ -bel
2
+ -ber
3
+ -bos
4
+ -che
5
+ -da +c
6
+ -du
7
+ -fel
8
+ -gut
9
+ -im
10
+ -ka
11
+ -kep
12
+ -ko
13
+ -kop
14
+ -kow
15
+ -mal
16
+ -mi +c
17
+ -nak
18
+ -oye
19
+ -pash
20
+ -pel
21
+ -pom
22
+ -sa +c
23
+ -sab
24
+ -sha
25
+ -shu
26
+ -ta
27
+ -teng
28
+ -tich
29
+ -to
30
+ -tu
31
+ -wel
32
+ -xa
33
+ -xi
34
+ -ya +c
35
+ -ze
36
+ al
37
+ ang
38
+ at
39
+ be
40
+ da
41
+ deng
42
+ do
43
+ eng
44
+ ere -c
45
+ ga
46
+ im -c
47
+ ka
48
+ ken
49
+ la
50
+ lom
51
+ low
52
+ lu
53
+ ma
54
+ mang
55
+ mi
56
+ na
57
+ nga
58
+ ok
59
+ ol
60
+ pa
61
+ ra
62
+ sa
63
+ se
64
+ sha
65
+ shi
66
+ ta
67
+ teng
68
+ ti
69
+ to
70
+ tu
71
+ un
72
+ wa
73
+ we
74
+ xa
75
+ ya
76
+ +a -c
77
+ +ang
78
+ +atna
79
+ +da
80
+ +deng
81
+ +eng -c
82
+ +im -c
83
+ +ing
84
+ +kang
85
+ +lowda
86
+ +mang
87
+ +na
88
+ +nya
89
+ +owda -c
90
+ +pa
91
+ +ra
92
+ +sa
93
+ +shang
94
+ +ta
95
+ +teng
96
+ +tim
97
+ +to
98
+ +tu
99
+ +wa
100
+ +wala
101
+ +xa
102
+ +ya
@@ -0,0 +1,91 @@
1
+ -ang +c
2
+ -bled
3
+ -bran
4
+ -bryn
5
+ -cad
6
+ -caer
7
+ -car
8
+ -cyn
9
+ -del
10
+ -dyf
11
+ -dyl
12
+ -eir
13
+ -em +c
14
+ -ffion
15
+ -gar
16
+ -ger
17
+ -gla +c
18
+ -glyn
19
+ -gru +c
20
+ -gwen
21
+ -gwyn
22
+ -hedd
23
+ -idr +v
24
+ -llan
25
+ -llew
26
+ -mab
27
+ -mor
28
+ -myr +c
29
+ -ol +c
30
+ -ow +v
31
+ -rhi +c
32
+ -rhos
33
+ -rhys
34
+ -tal
35
+ -tre +c
36
+ a -c
37
+ ad
38
+ ael
39
+ af
40
+ an
41
+ ar
42
+ aw
43
+ del
44
+ dwr
45
+ ed
46
+ el
47
+ ell
48
+ en
49
+ er
50
+ es
51
+ gar
52
+ goll
53
+ gwa
54
+ ha
55
+ i -c
56
+ ian
57
+ lan
58
+ lod
59
+ lyn
60
+ mai
61
+ o -c
62
+ on
63
+ rhy
64
+ ron
65
+ si
66
+ wal
67
+ wy
68
+ +ain -c
69
+ +aint -c
70
+ +an
71
+ +ddyn -v
72
+ +dwr
73
+ +dydd
74
+ +dys
75
+ +ed -c
76
+ +ell -c
77
+ +eth
78
+ +in
79
+ +ion
80
+ +lys
81
+ +og -c
82
+ +on
83
+ +rad
84
+ +ri
85
+ +rys
86
+ +ur -c
87
+ +wen
88
+ +wyn
89
+ +wyr
90
+ +ydd -c
91
+ +ys
@@ -42,10 +42,15 @@ module RandomNameGenerator
42
42
  class Syllable
43
43
  attr_reader :raw, :syllable, :next_syllable_requirement, :previous_syllable_requirement
44
44
 
45
- VOWELS = %w[i y ɨ ʉ ɯ u ɪ ʏ ʊ ɯ ʊ e ø ɘ ɵ ɤ o ø ə ɵ ɤ o ɛ œ ɜ ɞ ʌ ɔ æ ɐ ɞ a ɶ ä ɒ ɑ].freeze
46
- CONSONANTS = %w[b ɓ ʙ β c d ɗ ɖ ð f g h j k l ł m ɱ n ɳ p q r s t v w x y z].freeze
45
+ # "y" appears in both sets deliberately: as a semivowel it satisfies
46
+ # either a +v or a +c adjacency requirement.
47
+ VOWELS = %w[i y ɨ ʉ ɯ u ɪ ʏ ʊ ɯ ʊ e ø ɘ ɵ ɤ o ø ə ɵ ɤ o ɛ œ ɜ ɞ ʌ ɔ æ ɐ ɞ a ɶ ä ɒ ɑ ö ü
48
+ а е ё и о у ы э ю я].freeze
49
+ CONSONANTS = %w[b ɓ ʙ β c d ɗ ɖ ð f g h j k l ł m ɱ n ɳ p q r s t v w x y z ß
50
+ б в г д ж з й к л м н п р с т ф х ц ч ш щ ь].freeze
47
51
 
48
52
  def initialize(args)
53
+ args = args.raw if args.is_a?(Syllable)
49
54
  @raw = args.strip
50
55
  @syllable = ""
51
56
  @is_prefix = false
@@ -53,11 +58,7 @@ module RandomNameGenerator
53
58
  @next_syllable_requirement = :letter
54
59
  @previous_syllable_requirement = :letter
55
60
 
56
- if args.is_a?(Syllable)
57
- parse_args(args.raw)
58
- else
59
- parse_args(args)
60
- end
61
+ parse_args(args)
61
62
  end
62
63
 
63
64
  def incompatible?(next_syllable)
@@ -134,7 +135,7 @@ module RandomNameGenerator
134
135
  end
135
136
 
136
137
  def parse_syllable(syll)
137
- raise ArgumentError "Empty String is not allowed." if syll.empty?
138
+ raise ArgumentError, "Empty String is not allowed." if syll.to_s.empty?
138
139
 
139
140
  captures = /([+-]?)(.+)/.match(syll).captures
140
141
  parse_prefix(captures[0])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RandomNameGenerator
4
- VERSION = "4.0.2"
4
+ VERSION = "4.0.4"
5
5
  end
@@ -31,7 +31,9 @@ module RandomNameGenerator
31
31
  GOBLIN_RU = File.new("#{dirname}/languages/goblin-ru.txt")
32
32
  ROMAN_RU = File.new("#{dirname}/languages/roman-ru.txt")
33
33
 
34
+ BELTER = File.new("#{dirname}/languages/belter.txt")
34
35
  KLINGON = File.new("#{dirname}/languages/klingon.txt")
36
+ WELSH = File.new("#{dirname}/languages/welsh.txt")
35
37
 
36
38
  # Experimental
37
39
  CURSE = File.new("#{dirname}/languages/experimental/curse.txt")
@@ -87,7 +89,7 @@ module RandomNameGenerator
87
89
  # Returns the composed name as an array of Syllables.
88
90
  def compose_array(count = RandomNameGenerator.pick_number_of_syllables)
89
91
  @pre = pre_syllables.sample(random: @rnd)
90
- return @pre.to_s.capitalize if count < 2
92
+ return [@pre] if count < 2
91
93
 
92
94
  name = determine_middle_syllables(count - 2, @pre)
93
95
  name << determine_last_syllable(name.last)
@@ -125,18 +127,18 @@ module RandomNameGenerator
125
127
  end
126
128
 
127
129
  def determine_next_syllable(this_syllable, sampler)
128
- next_syllable = ""
129
- loop do
130
- next_syllable = sampler.sample(random: @rnd)
131
- break unless this_syllable.incompatible?(next_syllable)
130
+ candidates = sampler.select { |syllable| this_syllable.compatible?(syllable) }
131
+ if candidates.empty?
132
+ raise ArgumentError,
133
+ "No syllable in #{@language.path} is compatible with \"#{this_syllable}\" — check its +v/+c/-v/-c flags"
132
134
  end
133
- next_syllable
135
+ candidates.sample(random: @rnd)
134
136
  end
135
137
 
136
138
  # Loops through the language file, and pushes each syllable into the correct array.
137
139
  def refresh
138
140
  @language.readlines.each do |line|
139
- push(RandomNameGenerator::Syllable.new(line)) unless line.empty?
141
+ push(RandomNameGenerator::Syllable.new(line)) unless line.strip.empty?
140
142
  end
141
143
  @language.rewind
142
144
  end
@@ -28,8 +28,9 @@ Gem::Specification.new do |spec|
28
28
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ["lib"]
30
30
 
31
- # Uncomment to register a new dependency of your gem
32
- # spec.add_dependency "example-gem", "~> 1.0"
31
+ # The CLI in exe/ parses its options with slop, so it must ship as a
32
+ # runtime dependency a Gemfile entry is invisible to installed gems.
33
+ spec.add_dependency "slop", "~> 4.10"
33
34
 
34
35
  # For more information and examples about making a new gem, checkout our
35
36
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: random_name_generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.2
4
+ version: 4.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - folkengine
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2026-07-19 00:00:00.000000000 Z
12
- dependencies: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: slop
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '4.10'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '4.10'
13
26
  description: Generates random names based upon custom collections of syllables. Styles
14
27
  include Elvish, Fantasy, Goblin, and Roman.
15
28
  email:
@@ -29,6 +42,7 @@ files:
29
42
  - ".rspec"
30
43
  - ".rubocop.yml"
31
44
  - ".tool-versions"
45
+ - AI-BOM.md
32
46
  - CHANGELOG.md
33
47
  - Gemfile
34
48
  - Gemfile.lock
@@ -39,10 +53,12 @@ files:
39
53
  - bin/run
40
54
  - bin/setup
41
55
  - config.reek
56
+ - docs/AUDIT_Fable_5.md
42
57
  - docs/superpowers/plans/2026-07-18-lang-gen-skill.md
43
58
  - docs/superpowers/specs/2026-07-18-lang-gen-skill-design.md
44
59
  - docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md
45
60
  - exe/random_name_generator
61
+ - lib/languages/belter.txt
46
62
  - lib/languages/elven-ru.txt
47
63
  - lib/languages/elven.txt
48
64
  - lib/languages/experimental/curse.txt
@@ -55,6 +71,7 @@ files:
55
71
  - lib/languages/klingon.txt
56
72
  - lib/languages/roman-ru.txt
57
73
  - lib/languages/roman.txt
74
+ - lib/languages/welsh.txt
58
75
  - lib/random_name_generator.rb
59
76
  - lib/random_name_generator/syllable.rb
60
77
  - lib/random_name_generator/version.rb
@@ -67,7 +84,6 @@ metadata:
67
84
  homepage_uri: https://github.com/folkengine/random_name_generator
68
85
  source_code_uri: https://github.com/folkengine/random_name_generator
69
86
  changelog_uri: https://github.com/folkengine/random_name_generator/CHANGELOG.md
70
- post_install_message:
71
87
  rdoc_options: []
72
88
  require_paths:
73
89
  - lib
@@ -82,8 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
82
98
  - !ruby/object:Gem::Version
83
99
  version: '0'
84
100
  requirements: []
85
- rubygems_version: 3.4.19
86
- signing_key:
101
+ rubygems_version: 3.6.9
87
102
  specification_version: 4
88
103
  summary: Random Name Generator
89
104
  test_files: []