This gem is a text generator; a cross between mad-libs and Faker. The focus is on generating text that is appropriate for games, stories, and other creative works.
Why does this exist? In the age of generatve text using LLMs, there's probably not a good reason other than it's more understandable and possibly easier to fine tune.
Why Eunomia? Eunomia is a minor Greek goddess of law and order and since the generator is set up using a series of rules, it seemed appropriate.
Install the gem and add to the application's Gemfile by executing:
$ bundle add eunomia_gen
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install eunomia_gen
The gem is used by loading JSON (or ruby hashes) into a store, building a request context, and then selecting a generator to use.
There are three main concepts:
- Segments are elements in a sequence that can be combined to form a string.
Segments in square brackets have a special meaning. Dice notation (e.g.
[d4]
) generates a random number. A segment that is a key (e.g.[thing]
) will be replaced with a value chosen by the generator. - Items are selected at random from the generator that they are nested in. Items can have metadata about the generated string such as value (an arbitrary integer), ruby hash, or a weight (the likihood of being selected)
- Generators are the top level of the JSON. They contain a list of items that are selected at random.
require 'eunomia_gen'
data = [
{
key: "random-fruit",
items: [
functions: %w[pluralize capitalize],
segments: "[d4] [fruit]"
]
},
{
key: "fruit",
items: %w[apple banana orange pear kiwi]
}
]
Eunomia.add(data)
p Eunomia.generate("random-fruit").to_s # => "3 Bananas"
A request may have a key to translate a generated text into a different language. A translation hash may also be added to the request itself. If a translation is set on the item, then it needs a key to look up the translation. On the request any matching string is replaced with the value.
require 'eunomia_gen'
data = [
{ key: "fruit", items: %w[apple banana orange pear kiwi], alts: { "apple" => { "es" => "manzana" } } }
]
Eunomia.add(data)
request = Eunomia::Request.new("fruit", alt_key: "es", unique: true, alts: { "kiwi" => "a small flightless bird" })
arr = []
5.times { arr << request.generate.to_s }
p arr.join(", ") # => "manzana, banana, orange, pear, a small flightless bird"
A function is a method that can be called on the generated segments in a sequence. There are few built-in functions, but it is possible to add custom functions by passing in a proc that takes an array of strings and returns an array of strings.
Functions are called in the order that they are listed in the request. Functions defined on the request are applied to the final generated string. Functions defined on a generator or item are applied to the segments generated by that entity.
require 'eunomia_gen'
data = [
{ key: "fruit", items: %w[apple banana orange pear kiwi] },
{ key: "random-fruit", items: [{ segments: "[d4] [fruit]" }] }
]
Eunomia.add(data)
Eunomia.add_function(:reverse, proc { |arr| arr.map(&:reverse) })
request = Eunomia::Request.new("random-fruit", functions: %w[pluralize reverse])
p request.generate.to_s # => "3 sananab"
request = Eunomia::Request.new("random-fruit", functions: %w[reverse pluralize])
p request.generate.to_s # => "3 ikiks"
Items can have a weight that determines the likelihood of being selected. The default weight is 1.
A request has a unique flag, if set to true the request will add all generated strings to an internal set and will discard a string if it is already in the set. It will make up to 100 attempts to generate a unique string before raising an error.
Items can have a value that is an arbitrary integer. This is multiplied by the first segment in the generated string if it a number (e.g. a constant or randomly generated from a dice segment). If the first segment is not a number the multiplier defaults to 1.
A constant hash can be added to the request. A string that matches the hash key will be replaced by the constant value.
Tags can be used to filter items. For example, some plant names are also used as names. So a list of
plants could be filtered to only those that are also names to generate a character name. Tags assigned
to an item are also added as metadat to the result if the tag is in key:value
format.
data = [
{ key: "plant", items: %w[[flower] [tree]] },
{ key: "flower", items: [
{ segments: "rose", tags: %w[name:plant] },
{ segments: "hydrangea" }
]
},
{ key: "tree", items: [
{ segments: "ash", tags: %w[name:plant] },
{ segments: "oak" }
]
}
]
Eunomia.add(data)
val = Eunomia.generate("plant", tags: %w[name:plant], functions: ["capitalize"])
p val.to_s # => "Rose" or "Ash"
p val.meta # => { "tag" => "name:plant" }
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow
you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a
new version, update the version number in version.rb
, and then run bundle exec rake release
,
which will create a git tag for the version, push git commits and the created tag, and push
the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/palmergs/eunomia_gen. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the EunomiaGen project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.