text-gen 0.3.0 → 0.4.1
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/lib/text/gen/filter.rb +34 -14
- data/lib/text/gen/filter_error.rb +2 -0
- data/lib/text/gen/function/pluralizer.rb +68 -0
- data/lib/text/gen/function/titleizer.rb +24 -0
- data/lib/text/gen/max_attempts_error.rb +2 -0
- data/lib/text/gen/max_recursion_error.rb +2 -0
- data/lib/text/gen/result.rb +7 -4
- data/lib/text/gen/result_accumulator.rb +4 -2
- data/lib/text/gen/runner.rb +39 -24
- data/lib/text/gen/segment/constant.rb +2 -0
- data/lib/text/gen/segment/dice.rb +2 -0
- data/lib/text/gen/segment/number.rb +5 -1
- data/lib/text/gen/segment/parser.rb +2 -0
- data/lib/text/gen/segment/reference.rb +2 -0
- data/lib/text/gen/version.rb +1 -1
- data/lib/text/gen.rb +2 -1
- metadata +3 -2
- data/lib/text/gen/titleizer.rb +0 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a407f025ce7e1835fe342ecdd09831a83fc8384c3e5bf5cebe9d63b7c9105689
|
|
4
|
+
data.tar.gz: '08254c7fc3f59de3c959d34f469813a1af7ca2114c0678641c38093e67ddc421'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5faa23b1eb072b7f559269dea94a4e93096296ea8d6a197d86f3ea038b2f6d37d513afb18070174a7508f6119281e1f7f584893816b56515259287e25c32a428
|
|
7
|
+
data.tar.gz: dfec5829429cd2f2bba0f709bda7b0fd7b3402f181336755c7c05768ef79e57e41c77d4d181de070409b4be5030fc36b654e3269a055752385a54f3f9cac32bf
|
data/lib/text/gen/filter.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Text
|
|
2
4
|
module Gen
|
|
3
5
|
class Filter
|
|
@@ -15,7 +17,9 @@ module Text
|
|
|
15
17
|
when "upcase"
|
|
16
18
|
result = Result.from(text: result.text.upcase, type: :function, result:)
|
|
17
19
|
when "titleize"
|
|
18
|
-
result = Result.from(text: Titleizer.titleize(result.text), type: :function, result:)
|
|
20
|
+
result = Result.from(text: Function::Titleizer.titleize(result.text), type: :function, result:)
|
|
21
|
+
when "pluralize"
|
|
22
|
+
result = Result.from(text: Function::Pluralizer.pluralize(result.text), type: :function, result:)
|
|
19
23
|
when "clear"
|
|
20
24
|
result = Result.from(text: result.text, type: :function, result:)
|
|
21
25
|
result.clear_meta(filter["key"], filter["value"])
|
|
@@ -66,33 +70,47 @@ module Text
|
|
|
66
70
|
|
|
67
71
|
# Create a builder that always returns a constant value
|
|
68
72
|
def constant_builder(key, str)
|
|
69
|
-
# TODO: the string should be parsed into segments
|
|
70
|
-
segments = Segment::Parser.parse(str)
|
|
71
73
|
{
|
|
72
74
|
"key" => key,
|
|
73
|
-
"items" => [
|
|
74
|
-
{
|
|
75
|
-
"segments" => segments,
|
|
76
|
-
"value" => 0,
|
|
77
|
-
"multiplier" => 1,
|
|
78
|
-
"filters" => [],
|
|
79
|
-
"meta" => {}
|
|
80
|
-
}
|
|
81
|
-
],
|
|
75
|
+
"items" => [constant_item(str)],
|
|
82
76
|
"meta" => {},
|
|
83
77
|
"filters" => []
|
|
84
78
|
}
|
|
85
79
|
end
|
|
86
80
|
|
|
81
|
+
def constant_item(str, item = nil)
|
|
82
|
+
segments = Segment::Parser.parse(str)
|
|
83
|
+
citem = { "segments" => segments }
|
|
84
|
+
if item
|
|
85
|
+
citem["value"] = item["value"] if item["value"]
|
|
86
|
+
citem["multiplier"] = item["multiplier"] if item["multiplier"]
|
|
87
|
+
citem["filters"] = item["filters"] if item["filters"]
|
|
88
|
+
citem["meta"] = item["meta"] if item["meta"]
|
|
89
|
+
end
|
|
90
|
+
citem
|
|
91
|
+
end
|
|
92
|
+
|
|
87
93
|
# Replace locale is used on an item; if the item is selected and has
|
|
88
94
|
# associated meta value of "locale" and the request is using that locale value
|
|
89
95
|
# then replace the item text with the value of the meta. If there is
|
|
90
96
|
# more than one value, select at random.
|
|
91
|
-
|
|
97
|
+
# Returns a new item with locale text and preserved value/multiplier, or nil
|
|
98
|
+
def replace_locale(item, locale)
|
|
92
99
|
return if locale.nil?
|
|
100
|
+
return if item.nil?
|
|
101
|
+
|
|
102
|
+
meta = item["meta"]
|
|
93
103
|
return if meta.nil? || meta.empty?
|
|
94
104
|
|
|
95
|
-
meta[locale.downcase]&.sample
|
|
105
|
+
locale_text = meta[locale.downcase]&.sample
|
|
106
|
+
return unless locale_text
|
|
107
|
+
|
|
108
|
+
# Create new item with locale text, preserving value and multiplier
|
|
109
|
+
# new_item = constant_item(locale_text)
|
|
110
|
+
# new_item["value"] = item["value"] if item["value"]
|
|
111
|
+
# new_item["multiplier"] = item["multiplier"] if item["multiplier"]
|
|
112
|
+
# new_item
|
|
113
|
+
constant_item(locale_text, item)
|
|
96
114
|
end
|
|
97
115
|
|
|
98
116
|
def pass_select?(meta, key, value)
|
|
@@ -110,6 +128,8 @@ module Text
|
|
|
110
128
|
end
|
|
111
129
|
|
|
112
130
|
def separator(filters)
|
|
131
|
+
return "" unless filters
|
|
132
|
+
|
|
113
133
|
separator_filter = filter_by_type(filters, "separator")
|
|
114
134
|
return "" unless separator_filter
|
|
115
135
|
return separator_filter["value"] if separator_filter["key"] == "string"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Text
|
|
4
|
+
module Gen
|
|
5
|
+
module Function
|
|
6
|
+
class Pluralizer
|
|
7
|
+
EXCEPTIONS = {
|
|
8
|
+
"foot" => "feet",
|
|
9
|
+
"axis" => "axes",
|
|
10
|
+
"child" => "children",
|
|
11
|
+
"codex" => "codices",
|
|
12
|
+
"die" => "dice",
|
|
13
|
+
"dwarf" => "dwarves",
|
|
14
|
+
"goose" => "geese",
|
|
15
|
+
"elf" => "elves",
|
|
16
|
+
"man" => "men",
|
|
17
|
+
"ox" => "oxen",
|
|
18
|
+
"thief" => "thieves",
|
|
19
|
+
"tooth" => "teeth",
|
|
20
|
+
"wolf" => "wolves",
|
|
21
|
+
"woman" => "women"
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
SINGLE = Set.new(%w[a an the this that my your his her its our their])
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def pluralize(str)
|
|
28
|
+
return str if str.empty?
|
|
29
|
+
|
|
30
|
+
arr = str.split(/\s+/)
|
|
31
|
+
return str if arr.length < 2
|
|
32
|
+
|
|
33
|
+
num = to_num(arr[0])
|
|
34
|
+
if num && num > 1
|
|
35
|
+
dc = arr[-1].downcase
|
|
36
|
+
arr[-1] = exceptions(dc) || others(dc) || ends_with_y(dc) || simple(dc)
|
|
37
|
+
end
|
|
38
|
+
arr.join(" ")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_num(str)
|
|
42
|
+
return 1 if SINGLE.include?(str.downcase)
|
|
43
|
+
|
|
44
|
+
str =~ /\d+/ ? str.to_i : nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def exceptions(str)
|
|
48
|
+
EXCEPTIONS[str]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def others(str)
|
|
52
|
+
if str.end_with?("s") || str.end_with?("x") || str.end_with?("z") || str.end_with?("ch") || str.end_with?("sh")
|
|
53
|
+
"#{str}es"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def ends_with_y(str)
|
|
58
|
+
"#{str[0..-2]}ies" if str.end_with?("y")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def simple(str)
|
|
62
|
+
"#{str}s"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Text
|
|
4
|
+
module Gen
|
|
5
|
+
module Function
|
|
6
|
+
class Titleizer
|
|
7
|
+
SKIP_WORDS = Set.new(%w[a an and as at but by for if in of on or the to v via vs])
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def titleize(str)
|
|
11
|
+
arr = str.split(/\s+/)
|
|
12
|
+
idx = 0
|
|
13
|
+
len = arr.length
|
|
14
|
+
while idx < len
|
|
15
|
+
arr[idx] = arr[idx].capitalize if idx.zero? || !SKIP_WORDS.include?(arr[idx])
|
|
16
|
+
idx += 1
|
|
17
|
+
end
|
|
18
|
+
arr.join(" ")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/text/gen/result.rb
CHANGED
|
@@ -73,12 +73,15 @@ module Text
|
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
def merge(results,
|
|
76
|
+
def merge(results, value: nil, multiplier: nil, filters: [], meta: {}, type: :sequence)
|
|
77
|
+
sep = Filter.separator(filters)
|
|
78
|
+
mul = multiplier || results.reduce(1) { |acc, r| acc * r.multiplier }
|
|
79
|
+
val = value || results.sum(&:value)
|
|
77
80
|
new(
|
|
78
|
-
text: results.map(&:text).join(
|
|
81
|
+
text: results.map(&:text).join(sep),
|
|
79
82
|
type: type,
|
|
80
|
-
value:
|
|
81
|
-
multiplier:
|
|
83
|
+
value: val * mul,
|
|
84
|
+
multiplier: 1
|
|
82
85
|
).tap do |result|
|
|
83
86
|
result.components.append(*results)
|
|
84
87
|
result.merge_all_meta(results)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Text
|
|
2
4
|
module Gen
|
|
3
5
|
# ResultAccumulator handles accumulating results with attempt tracking
|
|
@@ -10,8 +12,8 @@ module Text
|
|
|
10
12
|
|
|
11
13
|
def initialize(unique:, count:, max_attempts:)
|
|
12
14
|
@unique = unique
|
|
13
|
-
@count = count
|
|
14
|
-
@max_attempts = max_attempts
|
|
15
|
+
@count = [count, 1].max
|
|
16
|
+
@max_attempts = [max_attempts, 1].max
|
|
15
17
|
@results = unique ? {} : []
|
|
16
18
|
@attempts = max_attempts * count
|
|
17
19
|
end
|
data/lib/text/gen/runner.rb
CHANGED
|
@@ -49,7 +49,7 @@ module Text
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# A builder is hash with a key field, items, filters, and meta
|
|
52
|
-
def run_builder(
|
|
52
|
+
def run_builder(_key, builder, filters, depth)
|
|
53
53
|
depth += 1
|
|
54
54
|
raise MaxRecursionError if depth > max_recursion
|
|
55
55
|
|
|
@@ -65,24 +65,36 @@ module Text
|
|
|
65
65
|
def run_items(strategy, items, filters, meta, depth)
|
|
66
66
|
case strategy
|
|
67
67
|
when "sequence"
|
|
68
|
-
|
|
69
|
-
run_item_sequence(items, sep, meta, depth)
|
|
68
|
+
run_item_sequence(items, filters, meta, depth)
|
|
70
69
|
when "weighted"
|
|
71
70
|
run_weighted_items(items, meta, depth)
|
|
72
71
|
else
|
|
73
|
-
run_random_item(items, meta, depth)
|
|
72
|
+
run_random_item(strategy, items, meta, depth)
|
|
74
73
|
end
|
|
75
74
|
end
|
|
76
75
|
|
|
77
|
-
def
|
|
78
|
-
|
|
76
|
+
def random_item(strategy, items)
|
|
77
|
+
if strategy.nil? || strategy.empty? || strategy == "random"
|
|
78
|
+
items.sample
|
|
79
|
+
else
|
|
80
|
+
total, count = random_from_dice(strategy)
|
|
81
|
+
total -= count # make the roll 0-indexed
|
|
82
|
+
items[total]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def run_random_item(strategy, items, meta, depth)
|
|
87
|
+
item = random_item(strategy, items)
|
|
88
|
+
return unless item
|
|
89
|
+
|
|
90
|
+
result = run_item(item, depth)
|
|
79
91
|
result.merge_meta(meta)
|
|
80
92
|
result
|
|
81
93
|
end
|
|
82
94
|
|
|
83
95
|
def run_item_sequence(items, filters, meta, depth)
|
|
84
96
|
results = items.map { |item| run_item(item, depth) }
|
|
85
|
-
Result.merge(results, filters
|
|
97
|
+
Result.merge(results, filters:, meta:, type: :sequence)
|
|
86
98
|
end
|
|
87
99
|
|
|
88
100
|
def run_weighted_items(items, meta, depth)
|
|
@@ -99,13 +111,17 @@ module Text
|
|
|
99
111
|
end
|
|
100
112
|
|
|
101
113
|
def run_item(item, depth)
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
return unless item
|
|
115
|
+
|
|
116
|
+
locale_item = Filter.replace_locale(item, locale)
|
|
117
|
+
item = locale_item || item
|
|
104
118
|
|
|
105
119
|
results = item["segments"].map { |seg| run_segment(seg, depth) }
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
result = Result.merge(results,
|
|
121
|
+
value: item["value"],
|
|
122
|
+
multiplier: item["multiplier"],
|
|
123
|
+
filters: item["filters"],
|
|
124
|
+
meta: item["meta"])
|
|
109
125
|
result = apply_result_function(result, item["filters"])
|
|
110
126
|
apply_result_filters(result, item["filters"])
|
|
111
127
|
end
|
|
@@ -123,16 +139,22 @@ module Text
|
|
|
123
139
|
end
|
|
124
140
|
end
|
|
125
141
|
|
|
142
|
+
def random_from_dice(text)
|
|
143
|
+
rolled = DiceNomShim.roll(text)
|
|
144
|
+
parsed = JSON.parse(rolled).first["lhs"]
|
|
145
|
+
count = parsed["values"].size
|
|
146
|
+
total = parsed["total"]
|
|
147
|
+
[total, count]
|
|
148
|
+
end
|
|
149
|
+
|
|
126
150
|
def run_dice_segment(seg)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
result = parsed[0].dig("lhs", "total")
|
|
130
|
-
Result.new(text: result.to_s, multiplier: result, type: :dice)
|
|
151
|
+
total, = random_from_dice(seg["text"])
|
|
152
|
+
Result.new(text: total.to_s, multiplier: total, type: :dice)
|
|
131
153
|
end
|
|
132
154
|
|
|
133
155
|
def run_number_segment(seg)
|
|
134
156
|
num = seg["text"].to_i
|
|
135
|
-
Result.new(text: num.to_s,
|
|
157
|
+
Result.new(text: num.to_s, multiplier: num, type: :number)
|
|
136
158
|
end
|
|
137
159
|
|
|
138
160
|
def run_reference_segment(seg, depth)
|
|
@@ -162,13 +184,6 @@ module Text
|
|
|
162
184
|
items
|
|
163
185
|
end
|
|
164
186
|
|
|
165
|
-
def locale_result_for(item)
|
|
166
|
-
text = Filter.replace_locale(item["meta"], locale)
|
|
167
|
-
return nil unless text
|
|
168
|
-
|
|
169
|
-
Result.new(text:, type: :locale, value: item["value"], multiplier: item["multiplier"])
|
|
170
|
-
end
|
|
171
|
-
|
|
172
187
|
def to_h
|
|
173
188
|
{
|
|
174
189
|
"key" => key,
|
data/lib/text/gen/version.rb
CHANGED
data/lib/text/gen.rb
CHANGED
|
@@ -6,8 +6,9 @@ require_relative "gen/lookup_error"
|
|
|
6
6
|
require_relative "gen/max_attempts_error"
|
|
7
7
|
require_relative "gen/max_recursion_error"
|
|
8
8
|
|
|
9
|
+
require_relative "gen/function/titleizer"
|
|
10
|
+
require_relative "gen/function/pluralizer"
|
|
9
11
|
require_relative "gen/result_accumulator"
|
|
10
|
-
require_relative "gen/titleizer"
|
|
11
12
|
require_relative "gen/result"
|
|
12
13
|
require_relative "gen/segment/parser"
|
|
13
14
|
require_relative "gen/filter"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: text-gen
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- G Palmer
|
|
@@ -42,6 +42,8 @@ files:
|
|
|
42
42
|
- lib/text/gen.rb
|
|
43
43
|
- lib/text/gen/filter.rb
|
|
44
44
|
- lib/text/gen/filter_error.rb
|
|
45
|
+
- lib/text/gen/function/pluralizer.rb
|
|
46
|
+
- lib/text/gen/function/titleizer.rb
|
|
45
47
|
- lib/text/gen/lookup_error.rb
|
|
46
48
|
- lib/text/gen/max_attempts_error.rb
|
|
47
49
|
- lib/text/gen/max_recursion_error.rb
|
|
@@ -54,7 +56,6 @@ files:
|
|
|
54
56
|
- lib/text/gen/segment/parser.rb
|
|
55
57
|
- lib/text/gen/segment/reference.rb
|
|
56
58
|
- lib/text/gen/store.rb
|
|
57
|
-
- lib/text/gen/titleizer.rb
|
|
58
59
|
- lib/text/gen/version.rb
|
|
59
60
|
- sig/text/gen.rbs
|
|
60
61
|
homepage: https://github.com/palmergs/text-gen
|
data/lib/text/gen/titleizer.rb
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
module Text
|
|
2
|
-
module Gen
|
|
3
|
-
class Titleizer
|
|
4
|
-
SKIP_WORDS = Set.new(%w[a an and as at but by for if in of on or the to v via vs])
|
|
5
|
-
|
|
6
|
-
class << self
|
|
7
|
-
def titleize(str)
|
|
8
|
-
arr = str.split(/\s+/)
|
|
9
|
-
idx = 0
|
|
10
|
-
len = arr.length
|
|
11
|
-
while idx < len
|
|
12
|
-
arr[idx] = arr[idx].capitalize if idx == 0 || !SKIP_WORDS.include?(arr[idx])
|
|
13
|
-
idx += 1
|
|
14
|
-
end
|
|
15
|
-
arr.join(" ")
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|