text-gen 0.11.3 → 0.12.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/context.rb +119 -0
- data/lib/text/gen/filter/base.rb +16 -13
- data/lib/text/gen/filter/capitalize.rb +19 -0
- data/lib/text/gen/filter/censor.rb +52 -0
- data/lib/text/gen/filter/clear.rb +19 -0
- data/lib/text/gen/filter/distinct.rb +19 -0
- data/lib/text/gen/filter/downcase.rb +19 -0
- data/lib/text/gen/filter/exclude.rb +19 -0
- data/lib/text/gen/filter/locale.rb +44 -0
- data/lib/text/gen/filter/match.rb +19 -0
- data/lib/text/gen/filter/meta.rb +35 -0
- data/lib/text/gen/filter/pluralize.rb +96 -0
- data/lib/text/gen/filter/reject.rb +24 -0
- data/lib/text/gen/filter/remember.rb +18 -0
- data/lib/text/gen/filter/replace.rb +15 -0
- data/lib/text/gen/filter/select.rb +24 -0
- data/lib/text/gen/filter/swap.rb +50 -0
- data/lib/text/gen/filter/titleize.rb +29 -0
- data/lib/text/gen/filter/upcase.rb +19 -0
- data/lib/text/gen/filter.rb +10 -168
- data/lib/text/gen/investigator.rb +38 -0
- data/lib/text/gen/meta.rb +46 -0
- data/lib/text/gen/result.rb +7 -22
- data/lib/text/gen/result_accumulator.rb +6 -11
- data/lib/text/gen/runner.rb +113 -174
- data/lib/text/gen/store.rb +29 -1
- data/lib/text/gen/version.rb +1 -1
- data/lib/text/gen.rb +4 -0
- metadata +21 -17
- data/lib/text/gen/filter/item/locale.rb +0 -29
- data/lib/text/gen/filter/item/reject.rb +0 -28
- data/lib/text/gen/filter/item/select.rb +0 -28
- data/lib/text/gen/filter/item_filter.rb +0 -17
- data/lib/text/gen/filter/result/capitalize.rb +0 -17
- data/lib/text/gen/filter/result/censor.rb +0 -55
- data/lib/text/gen/filter/result/clear.rb +0 -19
- data/lib/text/gen/filter/result/downcase.rb +0 -17
- data/lib/text/gen/filter/result/exclude.rb +0 -22
- data/lib/text/gen/filter/result/match.rb +0 -22
- data/lib/text/gen/filter/result/meta.rb +0 -31
- data/lib/text/gen/filter/result/pluralize.rb +0 -91
- data/lib/text/gen/filter/result/swap.rb +0 -48
- data/lib/text/gen/filter/result/titleize.rb +0 -28
- data/lib/text/gen/filter/result/upcase.rb +0 -17
- data/lib/text/gen/filter/result_filter.rb +0 -35
data/lib/text/gen/runner.rb
CHANGED
|
@@ -7,149 +7,90 @@ module Text
|
|
|
7
7
|
module Gen
|
|
8
8
|
# Runner generates results from the builder that is found with the given key.
|
|
9
9
|
class Runner
|
|
10
|
-
attr_reader :key, :lookup, :max_attempts, :max_recursion
|
|
11
|
-
attr_accessor :unique, :store
|
|
10
|
+
attr_reader :key, :lookup, :max_attempts, :max_recursion, :filters, :meta
|
|
12
11
|
|
|
13
|
-
def initialize(key:, lookup:, max_recursion: 10, max_attempts: 10,
|
|
12
|
+
def initialize(key:, lookup:, max_recursion: 10, max_attempts: 10, filters: [], meta: {})
|
|
14
13
|
@key = key.to_s.downcase
|
|
15
14
|
@lookup = lookup
|
|
16
|
-
@
|
|
17
|
-
@
|
|
18
|
-
@request_filters = request_filters
|
|
15
|
+
@filters = filters
|
|
16
|
+
@meta = meta
|
|
19
17
|
@max_attempts = max_attempts
|
|
20
18
|
@max_recursion = max_recursion
|
|
21
|
-
populate_replace
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def unique?
|
|
25
|
-
@unique
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def populate_replace
|
|
29
|
-
@request_filters.each do |f|
|
|
30
|
-
store.add(f["key"], Filter.constant_builder(f["key"], f["value"])) if f["type"] == "replace"
|
|
31
|
-
end
|
|
32
19
|
end
|
|
33
20
|
|
|
34
21
|
def run(count: 1)
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
store = Store.new(lookup)
|
|
23
|
+
builder = store.fetch(key)
|
|
24
|
+
raise NoBuilderMatched, "{builder `#{key}` not found}" unless builder
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
context = Context.new(store:, filters:, meta:, max_recursion:)
|
|
27
|
+
ResultAccumulator.accumulate(count:, max_attempts:) do
|
|
28
|
+
run_builder(context, builder)
|
|
40
29
|
end
|
|
41
30
|
end
|
|
42
31
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
# A builder is hash with a key field, items, filters, and meta
|
|
33
|
+
def run_builder(context, builder)
|
|
34
|
+
context.descend!(builder)
|
|
35
|
+
context.with_filters(builder["filters"]) do
|
|
36
|
+
builder = context.apply_builder_filters(builder)
|
|
37
|
+
return unless builder
|
|
46
38
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
# Filters without parameters will have an empty array.
|
|
50
|
-
def available_filters
|
|
51
|
-
Filter.available_filters
|
|
52
|
-
end
|
|
39
|
+
# Filter mapping of builder to result is allowed
|
|
40
|
+
return builder if builder.is_a?(Result)
|
|
53
41
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
raise MaxRecursionError if depth > max_recursion
|
|
57
|
-
|
|
58
|
-
builder = fetch_builder(key)
|
|
59
|
-
keys = [key]
|
|
60
|
-
builder["items"].each do |item|
|
|
61
|
-
item["segments"].each do |segment|
|
|
62
|
-
if segment["type"] == "reference"
|
|
63
|
-
keys << segment["text"]
|
|
64
|
-
keys += visit_and_recurse(segment["text"], depth)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
keys
|
|
69
|
-
rescue => e
|
|
70
|
-
[key]
|
|
71
|
-
end
|
|
42
|
+
result = builder.is_a?(Result) ? builder : run_items(context, builder["items"])
|
|
43
|
+
return unless result
|
|
72
44
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
builder
|
|
77
|
-
end
|
|
45
|
+
result.merge_meta(builder["meta"])
|
|
46
|
+
result = context.apply_result_filters(result)
|
|
47
|
+
return unless result
|
|
78
48
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
current_filters = merge_filters(builder, filters)
|
|
85
|
-
current_items = apply_item_filters(builder["items"], current_filters)
|
|
86
|
-
result = run_items(key, builder, current_items, current_filters, builder["meta"], depth)
|
|
87
|
-
return unless result
|
|
88
|
-
|
|
89
|
-
result.merge_meta(builder["meta"])
|
|
90
|
-
result = apply_result_function(result, current_filters)
|
|
91
|
-
apply_result_filters(result, current_filters)
|
|
49
|
+
context.remember(result)
|
|
50
|
+
end
|
|
51
|
+
ensure
|
|
52
|
+
context.ascend!
|
|
92
53
|
end
|
|
93
54
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
|
|
55
|
+
def run_items(context, items)
|
|
56
|
+
items = context.apply_item_list_filters(items)
|
|
57
|
+
return if items.empty?
|
|
97
58
|
|
|
98
|
-
|
|
99
|
-
strategy, modifier = (builder["strategy"] || "random").split(":")
|
|
100
|
-
case strategy
|
|
59
|
+
case context.current_strategy
|
|
101
60
|
when "sequence"
|
|
102
|
-
run_item_sequence(
|
|
61
|
+
run_item_sequence(context, items)
|
|
103
62
|
when "weighted"
|
|
104
|
-
run_weighted_items(
|
|
63
|
+
run_weighted_items(context, items)
|
|
105
64
|
else
|
|
106
|
-
run_random_item(
|
|
65
|
+
run_random_item(context, items)
|
|
107
66
|
end
|
|
108
|
-
rescue StandardError => e
|
|
109
|
-
error_result("{#{e.class.name}:#{e.message}}")
|
|
110
67
|
end
|
|
111
68
|
|
|
112
|
-
def
|
|
113
|
-
return items.sample if dice.nil? || dice.empty? || dice == "*"
|
|
114
|
-
|
|
115
|
-
total, count = random_from_dice(dice)
|
|
116
|
-
index = total - count # convert to 0-indexed
|
|
117
|
-
raise NoItemMatched("roll #{total} exceeds #{items.length}") if index >= items.length
|
|
118
|
-
|
|
119
|
-
items[index]
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def run_random_item(key, dice, items, meta, depth)
|
|
123
|
-
item = random_item(dice, items)
|
|
124
|
-
result = run_item(key, item, depth)
|
|
125
|
-
return unless result
|
|
126
|
-
|
|
127
|
-
result.merge_meta(meta)
|
|
128
|
-
result
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def run_item_sequence(key, separator, items, filters, meta, depth)
|
|
69
|
+
def run_item_sequence(context, items)
|
|
132
70
|
results = items.map do |item|
|
|
133
71
|
weight = item["weight"]&.to_i || 100
|
|
134
72
|
next unless rand(100) < weight.clamp(1, 100)
|
|
135
73
|
|
|
136
|
-
|
|
74
|
+
ResultAccumulator.accumulate(count: 1, max_attempts: @max_attempts) do
|
|
75
|
+
run_item(context, item)
|
|
76
|
+
end.first
|
|
137
77
|
end
|
|
138
78
|
|
|
139
79
|
results = results.compact
|
|
140
80
|
return if results.empty?
|
|
141
81
|
|
|
142
|
-
Result.merge(results, separator:
|
|
82
|
+
Result.merge(results, separator: context.current_modifier, type: :sequence)
|
|
143
83
|
end
|
|
144
84
|
|
|
145
|
-
def run_weighted_items(
|
|
85
|
+
def run_weighted_items(context, items)
|
|
146
86
|
total_weight = items.sum { |item| [item.fetch("weight", 1).to_i, 1].max }
|
|
87
|
+
dice = context.current_modifier
|
|
147
88
|
rand_weight = if dice.nil? || dice.empty? || dice == "*"
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
89
|
+
rand(total_weight)
|
|
90
|
+
else
|
|
91
|
+
total, count = random_from_dice(dice)
|
|
92
|
+
total - count # convert to 0-indexed
|
|
93
|
+
end
|
|
153
94
|
return if rand_weight > total_weight
|
|
154
95
|
|
|
155
96
|
current_weight = 0
|
|
@@ -157,47 +98,75 @@ module Text
|
|
|
157
98
|
current_weight += [item.fetch("weight", 1).to_i, 1].max
|
|
158
99
|
current_weight > rand_weight
|
|
159
100
|
end
|
|
160
|
-
result = run_item(key, item, depth)
|
|
161
|
-
return unless result
|
|
162
101
|
|
|
163
|
-
|
|
164
|
-
|
|
102
|
+
run_item(context, item)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def run_random_item(context, items)
|
|
106
|
+
item = random_item(items, dice: context.current_modifier)
|
|
107
|
+
return unless item
|
|
108
|
+
|
|
109
|
+
run_item(context, item)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def random_item(items, dice: nil)
|
|
113
|
+
return items.sample if dice.nil? || dice.empty? || dice == "*"
|
|
114
|
+
|
|
115
|
+
total, count = random_from_dice(dice)
|
|
116
|
+
index = total - count # convert to 0-indexed
|
|
117
|
+
raise NoItemMatched("roll #{total} exceeds #{items.length}") if index >= items.length
|
|
118
|
+
|
|
119
|
+
items[index]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def run_item(context, item)
|
|
123
|
+
item = context.apply_item_filters(item)
|
|
124
|
+
return unless item
|
|
125
|
+
|
|
126
|
+
# Filter mapping of item to result is allowed
|
|
127
|
+
return item if item.is_a?(Result)
|
|
128
|
+
|
|
129
|
+
context.with_filters(item["filters"]) do
|
|
130
|
+
results = item["segments"].map { |seg| run_segment(context, seg) }.compact
|
|
131
|
+
return if results.empty?
|
|
132
|
+
|
|
133
|
+
Result.merge(results,
|
|
134
|
+
value: item["value"],
|
|
135
|
+
multiplier: item["multiplier"],
|
|
136
|
+
meta: item["meta"],
|
|
137
|
+
type: context.current_key)
|
|
138
|
+
end
|
|
165
139
|
end
|
|
166
140
|
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
141
|
+
def run_segment(context, seg)
|
|
142
|
+
# Note, a filter may expand a segment into multiple segments
|
|
143
|
+
segments = context.apply_segment_filters(seg)
|
|
144
|
+
return if segments.nil?
|
|
145
|
+
|
|
146
|
+
# Filter mapping of segment to result is allowed
|
|
147
|
+
return segments if segments.is_a?(Result)
|
|
148
|
+
|
|
149
|
+
results = segments.map { |seg| run_simple_segment(context, seg) }.compact
|
|
150
|
+
return if results.empty?
|
|
151
|
+
|
|
152
|
+
return results[0] if results.length == 1
|
|
153
|
+
|
|
154
|
+
Result.merge(results, type: "segment expansion")
|
|
176
155
|
end
|
|
177
156
|
|
|
178
|
-
def
|
|
157
|
+
def run_simple_segment(context, seg)
|
|
179
158
|
case seg["type"]
|
|
180
159
|
when "dice"
|
|
181
160
|
run_dice_segment(seg)
|
|
182
161
|
when "number"
|
|
183
162
|
run_number_segment(seg)
|
|
184
163
|
when "reference"
|
|
185
|
-
run_reference_segment(
|
|
164
|
+
run_reference_segment(context, seg)
|
|
186
165
|
else
|
|
187
166
|
Result.new(text: seg["text"], value: seg["value"], type: :constant)
|
|
188
167
|
end
|
|
189
168
|
end
|
|
190
169
|
|
|
191
|
-
def random_from_dice(text)
|
|
192
|
-
rolled = DiceNomShim.roll(text)
|
|
193
|
-
parsed = JSON.parse(rolled).first["lhs"]
|
|
194
|
-
|
|
195
|
-
# Keep tracks the dice that weren't discarded
|
|
196
|
-
count = parsed["values"].select {|v| v["keep"] }.count
|
|
197
|
-
total = parsed["total"]
|
|
198
|
-
[total, count]
|
|
199
|
-
end
|
|
200
|
-
|
|
201
170
|
def run_dice_segment(seg)
|
|
202
171
|
total, = random_from_dice(seg["text"])
|
|
203
172
|
Result.new(text: total.to_s, multiplier: total, type: :dice)
|
|
@@ -208,56 +177,26 @@ module Text
|
|
|
208
177
|
Result.new(text: num.to_s, multiplier: num, type: :number)
|
|
209
178
|
end
|
|
210
179
|
|
|
211
|
-
def run_reference_segment(
|
|
180
|
+
def run_reference_segment(context, seg)
|
|
212
181
|
key = seg["text"]
|
|
213
|
-
run_builder(key, fetch_builder(key), seg.fetch("filters", []), depth)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
def apply_result_filters(result, filters)
|
|
217
|
-
result = Filter.result_select(result, filters)
|
|
218
|
-
result = Filter.result_reject(result, filters) if result
|
|
219
|
-
result
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
def apply_result_function(result, filters)
|
|
223
|
-
Filter.functions(result, filters, lookup: lookup)
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
def merge_filters(builder, filters)
|
|
227
|
-
builder.key?("filters") ? builder["filters"] + filters : filters
|
|
228
|
-
end
|
|
229
182
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
items = Filter.filter_select(items, filters)
|
|
233
|
-
items = Filter.filter_reject(items, filters)
|
|
234
|
-
raise FilterError if items.empty?
|
|
235
|
-
|
|
236
|
-
items
|
|
237
|
-
end
|
|
183
|
+
builder = context.store.fetch(key)
|
|
184
|
+
return unless builder
|
|
238
185
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
"request_meta" => request_meta,
|
|
244
|
-
"store" => store.to_h
|
|
245
|
-
}
|
|
186
|
+
# Offset by one to ensure the segment filters are applied to the builder
|
|
187
|
+
context.with_filters(seg["filters"], offset: 1) do
|
|
188
|
+
run_builder(context, builder)
|
|
189
|
+
end
|
|
246
190
|
end
|
|
247
191
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
key: hash["key"],
|
|
252
|
-
request_filters: hash.fetch("request_filters", [])
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
hash.fetch("store", {}).each do |k, b|
|
|
256
|
-
runner.store.add(k, b)
|
|
257
|
-
end
|
|
192
|
+
def random_from_dice(text)
|
|
193
|
+
rolled = DiceNomShim.roll(text)
|
|
194
|
+
parsed = JSON.parse(rolled).first["lhs"]
|
|
258
195
|
|
|
259
|
-
|
|
260
|
-
|
|
196
|
+
# Keep tracks the dice that weren't discarded
|
|
197
|
+
count = parsed["values"].select { |v| v["keep"] }.count
|
|
198
|
+
total = parsed["total"]
|
|
199
|
+
[total, count]
|
|
261
200
|
end
|
|
262
201
|
end
|
|
263
202
|
end
|
data/lib/text/gen/store.rb
CHANGED
|
@@ -5,7 +5,14 @@ module Text
|
|
|
5
5
|
# Store is a local cache of a builder that is persistent per store
|
|
6
6
|
# to save time on database lookups or transformations.
|
|
7
7
|
class Store
|
|
8
|
-
|
|
8
|
+
NOT_FOUND_BUILDER = {
|
|
9
|
+
"filters" => [],
|
|
10
|
+
"meta" => {},
|
|
11
|
+
"items" => []
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
def initialize(lookup)
|
|
15
|
+
@lookup = lookup
|
|
9
16
|
@store = {}
|
|
10
17
|
end
|
|
11
18
|
|
|
@@ -15,6 +22,27 @@ module Text
|
|
|
15
22
|
|
|
16
23
|
def add(key, builder)
|
|
17
24
|
@store[key.to_s.downcase] = builder
|
|
25
|
+
builder
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def clear(key)
|
|
29
|
+
@store.delete(key.to_s.downcase)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def fetch(key)
|
|
33
|
+
builder = find(key)
|
|
34
|
+
return builder if builder
|
|
35
|
+
|
|
36
|
+
builder = @lookup.call(key)
|
|
37
|
+
return not_found(key) unless builder
|
|
38
|
+
|
|
39
|
+
add(key, builder.merge("key" => key.to_s.downcase))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def not_found(key)
|
|
43
|
+
hsh = NOT_FOUND_BUILDER.dup
|
|
44
|
+
hsh["key"] = key
|
|
45
|
+
hsh
|
|
18
46
|
end
|
|
19
47
|
|
|
20
48
|
def to_h
|
data/lib/text/gen/version.rb
CHANGED
data/lib/text/gen.rb
CHANGED
|
@@ -6,13 +6,17 @@ require_relative "gen/result_accumulator"
|
|
|
6
6
|
require_relative "gen/result"
|
|
7
7
|
require_relative "gen/segment/parser"
|
|
8
8
|
require_relative "gen/filter"
|
|
9
|
+
require_relative "gen/context"
|
|
10
|
+
require_relative "gen/meta"
|
|
9
11
|
require_relative "gen/store"
|
|
10
12
|
require_relative "gen/runner"
|
|
13
|
+
require_relative "gen/investigator"
|
|
11
14
|
|
|
12
15
|
module Text
|
|
13
16
|
module Gen
|
|
14
17
|
class MaxRecursionError < StandardError; end
|
|
15
18
|
class MaxAttemptsError < StandardError; end
|
|
19
|
+
class NoBuilderMatched < StandardError; end
|
|
16
20
|
class NoItemMatched < StandardError; end
|
|
17
21
|
class FilterError < StandardError; end
|
|
18
22
|
class LookupError < StandardError; end
|
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.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- G Palmer
|
|
@@ -41,24 +41,28 @@ files:
|
|
|
41
41
|
- README.md
|
|
42
42
|
- Rakefile
|
|
43
43
|
- lib/text/gen.rb
|
|
44
|
+
- lib/text/gen/context.rb
|
|
44
45
|
- lib/text/gen/filter.rb
|
|
45
46
|
- lib/text/gen/filter/base.rb
|
|
46
|
-
- lib/text/gen/filter/
|
|
47
|
-
- lib/text/gen/filter/
|
|
48
|
-
- lib/text/gen/filter/
|
|
49
|
-
- lib/text/gen/filter/
|
|
50
|
-
- lib/text/gen/filter/
|
|
51
|
-
- lib/text/gen/filter/
|
|
52
|
-
- lib/text/gen/filter/
|
|
53
|
-
- lib/text/gen/filter/
|
|
54
|
-
- lib/text/gen/filter/
|
|
55
|
-
- lib/text/gen/filter/
|
|
56
|
-
- lib/text/gen/filter/
|
|
57
|
-
- lib/text/gen/filter/
|
|
58
|
-
- lib/text/gen/filter/
|
|
59
|
-
- lib/text/gen/filter/
|
|
60
|
-
- lib/text/gen/filter/
|
|
61
|
-
- lib/text/gen/filter/
|
|
47
|
+
- lib/text/gen/filter/capitalize.rb
|
|
48
|
+
- lib/text/gen/filter/censor.rb
|
|
49
|
+
- lib/text/gen/filter/clear.rb
|
|
50
|
+
- lib/text/gen/filter/distinct.rb
|
|
51
|
+
- lib/text/gen/filter/downcase.rb
|
|
52
|
+
- lib/text/gen/filter/exclude.rb
|
|
53
|
+
- lib/text/gen/filter/locale.rb
|
|
54
|
+
- lib/text/gen/filter/match.rb
|
|
55
|
+
- lib/text/gen/filter/meta.rb
|
|
56
|
+
- lib/text/gen/filter/pluralize.rb
|
|
57
|
+
- lib/text/gen/filter/reject.rb
|
|
58
|
+
- lib/text/gen/filter/remember.rb
|
|
59
|
+
- lib/text/gen/filter/replace.rb
|
|
60
|
+
- lib/text/gen/filter/select.rb
|
|
61
|
+
- lib/text/gen/filter/swap.rb
|
|
62
|
+
- lib/text/gen/filter/titleize.rb
|
|
63
|
+
- lib/text/gen/filter/upcase.rb
|
|
64
|
+
- lib/text/gen/investigator.rb
|
|
65
|
+
- lib/text/gen/meta.rb
|
|
62
66
|
- lib/text/gen/result.rb
|
|
63
67
|
- lib/text/gen/result_accumulator.rb
|
|
64
68
|
- lib/text/gen/runner.rb
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "../item_filter"
|
|
4
|
-
|
|
5
|
-
module Text
|
|
6
|
-
module Gen
|
|
7
|
-
module Filter
|
|
8
|
-
module Item
|
|
9
|
-
class Locale < ItemFilter
|
|
10
|
-
def apply(items)
|
|
11
|
-
items.map { |item| locale_item(item) || item }
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
def locale_item(item)
|
|
17
|
-
meta = item["meta"]
|
|
18
|
-
return if meta.nil? || meta.empty?
|
|
19
|
-
|
|
20
|
-
locale_text = meta[key.downcase]&.sample
|
|
21
|
-
return unless locale_text
|
|
22
|
-
|
|
23
|
-
Filter.constant_item(locale_text, item)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "../item_filter"
|
|
4
|
-
|
|
5
|
-
module Text
|
|
6
|
-
module Gen
|
|
7
|
-
module Filter
|
|
8
|
-
module Item
|
|
9
|
-
class Reject < ItemFilter
|
|
10
|
-
def apply(items)
|
|
11
|
-
items.select do |item|
|
|
12
|
-
pass_reject?(item["meta"])
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
def pass_reject?(meta)
|
|
19
|
-
return false if value == "*" && meta.key?(key)
|
|
20
|
-
return false if key == "*" && meta.values.any? { |arr| arr.include?(value) }
|
|
21
|
-
|
|
22
|
-
!meta[key]&.include?(value)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "../item_filter"
|
|
4
|
-
|
|
5
|
-
module Text
|
|
6
|
-
module Gen
|
|
7
|
-
module Filter
|
|
8
|
-
module Item
|
|
9
|
-
class Select < ItemFilter
|
|
10
|
-
def apply(items)
|
|
11
|
-
items.select do |item|
|
|
12
|
-
pass_select?(item["meta"])
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
def pass_select?(meta)
|
|
19
|
-
return true if value == "*" && meta.key?(key)
|
|
20
|
-
return true if key == "*" && meta.values.any? { |arr| arr.include?(value) }
|
|
21
|
-
|
|
22
|
-
meta[key]&.include?(value)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "base"
|
|
4
|
-
|
|
5
|
-
module Text
|
|
6
|
-
module Gen
|
|
7
|
-
module Filter
|
|
8
|
-
# Base class for filters that operate on item arrays
|
|
9
|
-
# Subclasses should implement #apply(items) -> Array
|
|
10
|
-
class ItemFilter < Base
|
|
11
|
-
def apply(items)
|
|
12
|
-
raise NotImplementedError, "Subclasses must implement #apply(items)"
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "../result_filter"
|
|
4
|
-
|
|
5
|
-
module Text
|
|
6
|
-
module Gen
|
|
7
|
-
module Filter
|
|
8
|
-
module Result
|
|
9
|
-
class Capitalize < ResultFilter
|
|
10
|
-
def apply(result)
|
|
11
|
-
transform_text(result, result.text.sub(/\S/, &:upcase))
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "../result_filter"
|
|
4
|
-
|
|
5
|
-
module Text
|
|
6
|
-
module Gen
|
|
7
|
-
module Filter
|
|
8
|
-
module Result
|
|
9
|
-
class Censor < ResultFilter
|
|
10
|
-
def apply(result)
|
|
11
|
-
return result if lookup.nil? || key.nil?
|
|
12
|
-
|
|
13
|
-
# Get the censor builder
|
|
14
|
-
builder = lookup.call(key)
|
|
15
|
-
return result unless builder
|
|
16
|
-
|
|
17
|
-
# Create a runner to evaluate each item
|
|
18
|
-
runner = Text::Gen::Runner.new(key: key, lookup: lookup)
|
|
19
|
-
|
|
20
|
-
# Run each item in the builder once
|
|
21
|
-
censor_texts = builder["items"].map do |item|
|
|
22
|
-
item_result = runner.send(:run_item, key, item, 0)
|
|
23
|
-
apply_function(item_result.text) if item_result
|
|
24
|
-
end.compact.uniq
|
|
25
|
-
|
|
26
|
-
# Get the text to compare
|
|
27
|
-
compare_text = apply_function(result.text)
|
|
28
|
-
|
|
29
|
-
# Check if result text matches any censor text
|
|
30
|
-
return nil if censor_texts.include?(compare_text)
|
|
31
|
-
|
|
32
|
-
result
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def apply_function(text)
|
|
38
|
-
return text if value.nil? || value.empty?
|
|
39
|
-
|
|
40
|
-
case value
|
|
41
|
-
when "downcase"
|
|
42
|
-
text.downcase
|
|
43
|
-
when "upcase"
|
|
44
|
-
text.upcase
|
|
45
|
-
when "capitalize"
|
|
46
|
-
text.capitalize
|
|
47
|
-
else
|
|
48
|
-
text
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|