text-gen 0.3.0 → 0.4.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/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 +2 -0
- data/lib/text/gen/runner.rb +37 -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: e32773381d2c8aa30b9c027bf9c5a00f4b76045eff5e6fd36626d94fa33de652
|
|
4
|
+
data.tar.gz: 97e3c3b49b367939e270582fb3c6b2e940d623bfec93fab341af27fe1e3902d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 68f26c344a0feb1b1a730d9247e41c8dc93e27f1a52f22f7d16bdb0c1d719ba3ff03b294475f36e1c404c816192a995c9c3341ff845af0b29d6ad0a37e0e5028
|
|
7
|
+
data.tar.gz: 3021702f504892d11d449c2de96ad98d5b5b1d7d514f4bc8f32e91b172e74383a3338faf1256fc03c57dcba34fb6442ccc560ad004e7ddc948c2e709562ce684
|
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)
|
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,15 @@ module Text
|
|
|
99
111
|
end
|
|
100
112
|
|
|
101
113
|
def run_item(item, depth)
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
locale_item = Filter.replace_locale(item, locale)
|
|
115
|
+
item = locale_item if locale_item
|
|
104
116
|
|
|
105
117
|
results = item["segments"].map { |seg| run_segment(seg, depth) }
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
result = Result.merge(results,
|
|
119
|
+
value: item["value"],
|
|
120
|
+
multiplier: item["multiplier"],
|
|
121
|
+
filters: item["filters"],
|
|
122
|
+
meta: item["meta"])
|
|
109
123
|
result = apply_result_function(result, item["filters"])
|
|
110
124
|
apply_result_filters(result, item["filters"])
|
|
111
125
|
end
|
|
@@ -123,16 +137,22 @@ module Text
|
|
|
123
137
|
end
|
|
124
138
|
end
|
|
125
139
|
|
|
140
|
+
def random_from_dice(text)
|
|
141
|
+
rolled = DiceNomShim.roll(text)
|
|
142
|
+
parsed = JSON.parse(rolled).first["lhs"]
|
|
143
|
+
count = parsed["values"].size
|
|
144
|
+
total = parsed["total"]
|
|
145
|
+
[total, count]
|
|
146
|
+
end
|
|
147
|
+
|
|
126
148
|
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)
|
|
149
|
+
total, = random_from_dice(seg["text"])
|
|
150
|
+
Result.new(text: total.to_s, multiplier: total, type: :dice)
|
|
131
151
|
end
|
|
132
152
|
|
|
133
153
|
def run_number_segment(seg)
|
|
134
154
|
num = seg["text"].to_i
|
|
135
|
-
Result.new(text: num.to_s,
|
|
155
|
+
Result.new(text: num.to_s, multiplier: num, type: :number)
|
|
136
156
|
end
|
|
137
157
|
|
|
138
158
|
def run_reference_segment(seg, depth)
|
|
@@ -162,13 +182,6 @@ module Text
|
|
|
162
182
|
items
|
|
163
183
|
end
|
|
164
184
|
|
|
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
185
|
def to_h
|
|
173
186
|
{
|
|
174
187
|
"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.0
|
|
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
|