text-gen 0.7.1 → 0.8.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/README.md +33 -11
- data/lib/text/gen/filter/base.rb +36 -0
- data/lib/text/gen/filter/item/reject.rb +28 -0
- data/lib/text/gen/filter/item/select.rb +28 -0
- data/lib/text/gen/filter/item_filter.rb +17 -0
- data/lib/text/gen/filter/result/capitalize.rb +17 -0
- data/lib/text/gen/filter/result/censor.rb +55 -0
- data/lib/text/gen/filter/result/clear.rb +19 -0
- data/lib/text/gen/filter/result/downcase.rb +17 -0
- data/lib/text/gen/filter/result/exclude.rb +22 -0
- data/lib/text/gen/filter/result/match.rb +22 -0
- data/lib/text/gen/filter/result/meta.rb +31 -0
- data/lib/text/gen/{function/pluralizer.rb → filter/result/pluralize.rb} +32 -28
- data/lib/text/gen/{function/swapper.rb → filter/result/swap.rb} +15 -12
- data/lib/text/gen/filter/result/titleize.rb +28 -0
- data/lib/text/gen/filter/result/upcase.rb +17 -0
- data/lib/text/gen/filter/result_filter.rb +35 -0
- data/lib/text/gen/filter.rb +64 -42
- data/lib/text/gen/runner.rb +1 -1
- data/lib/text/gen/version.rb +1 -1
- metadata +17 -9
- data/lib/text/gen/function/capitalizer.rb +0 -16
- data/lib/text/gen/function/clear.rb +0 -17
- data/lib/text/gen/function/downcase.rb +0 -16
- data/lib/text/gen/function/meta.rb +0 -17
- data/lib/text/gen/function/titleizer.rb +0 -24
- data/lib/text/gen/function/upcase.rb +0 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 60294b39f992c96f317ad35a502145577250616b6955133b1502d4671af29517
|
|
4
|
+
data.tar.gz: f9fd5e105682af4c36f11632c1603425b977a59171d6c0597428761e397ce443
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 84dec457f1b0c2853f014f6e75f1136baa40afffebe3ac8c5e15932fd1ab8938367e30509bb26dd65cb4f50c2be9e18149d7c14a61a876a381ee632e2f9573e2
|
|
7
|
+
data.tar.gz: 7d7bb543d5fe6d6582746f807cf1d31d1e82211749ec54f8abdade318f97cb069960d3d3ef327bdef9f830a19a450506b11cc446c48f8c88218997c4d6d77b6b
|
data/README.md
CHANGED
|
@@ -1,28 +1,50 @@
|
|
|
1
1
|
# Text::Gen
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/text/gen`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
3
|
+
A Ruby gem for building random text strings from rules defined as Ruby hashes. Rules consist of Builders containing Items composed of Segments. Generated text can be modified with filters like pluralize, capitalize, and titleize.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'text-gen'
|
|
11
|
+
```
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
And then execute:
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
bundle
|
|
16
|
+
bundle install
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
Or install it yourself as:
|
|
18
20
|
|
|
19
21
|
```bash
|
|
20
|
-
gem install
|
|
22
|
+
gem install text-gen
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
## Usage
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
```ruby
|
|
28
|
+
require 'text/gen'
|
|
29
|
+
|
|
30
|
+
# Define builders with items and filters
|
|
31
|
+
random_fruit = {
|
|
32
|
+
"items" => [{ "segments" => Text::Gen::Segment::Parser.parse("[d4] [fruit]") }],
|
|
33
|
+
"filters" => [{ "type" => "pluralize" }, { "type" => "titleize" }]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fruit = {
|
|
37
|
+
"items" => %w[apple banana orange pear kiwi].map { |f| { "segments" => Text::Gen::Segment::Parser.parse(f) } }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Create a lookup function
|
|
41
|
+
lookup = lambda { |key| key == "random-fruit" ? random_fruit : fruit }
|
|
42
|
+
|
|
43
|
+
# Run the generator
|
|
44
|
+
runner = Text::Gen::Runner.new(key: "random-fruit", lookup: lookup)
|
|
45
|
+
result = runner.run.first
|
|
46
|
+
puts result.text # => "3 Bananas"
|
|
47
|
+
```
|
|
26
48
|
|
|
27
49
|
## Development
|
|
28
50
|
|
|
@@ -32,7 +54,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
|
32
54
|
|
|
33
55
|
## Contributing
|
|
34
56
|
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
57
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/palmergs/text-gen. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/palmergs/text-gen/blob/release/CODE_OF_CONDUCT.md).
|
|
36
58
|
|
|
37
59
|
## License
|
|
38
60
|
|
|
@@ -40,4 +62,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
40
62
|
|
|
41
63
|
## Code of Conduct
|
|
42
64
|
|
|
43
|
-
Everyone interacting in the Text::Gen project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
|
65
|
+
Everyone interacting in the Text::Gen project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/palmergs/text-gen/blob/release/CODE_OF_CONDUCT.md).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Text
|
|
4
|
+
module Gen
|
|
5
|
+
module Filter
|
|
6
|
+
# Base class for all filters
|
|
7
|
+
# Provides common utilities and template method pattern
|
|
8
|
+
class Base
|
|
9
|
+
attr_reader :filter, :lookup
|
|
10
|
+
|
|
11
|
+
def initialize(filter = {}, lookup: nil)
|
|
12
|
+
@filter = filter
|
|
13
|
+
@lookup = lookup
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Template method - must be implemented by subclasses
|
|
17
|
+
def apply(input)
|
|
18
|
+
raise NotImplementedError, "Subclasses must implement #apply"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Shared utilities
|
|
22
|
+
def key
|
|
23
|
+
filter["key"]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def value
|
|
27
|
+
filter["value"]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def type
|
|
31
|
+
filter["type"]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
|
@@ -0,0 +1,17 @@
|
|
|
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.capitalize)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
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, 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
|
|
@@ -0,0 +1,19 @@
|
|
|
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 Clear < ResultFilter
|
|
10
|
+
def apply(result)
|
|
11
|
+
transform_with_meta(result) do |new_result|
|
|
12
|
+
new_result.clear_meta(key, value)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
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 Downcase < ResultFilter
|
|
10
|
+
def apply(result)
|
|
11
|
+
transform_text(result, result.text.downcase)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
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 Exclude < ResultFilter
|
|
10
|
+
def apply(result)
|
|
11
|
+
return nil if result.nil?
|
|
12
|
+
return nil if value == "*" && result.meta.key?(key)
|
|
13
|
+
return nil if key == "*" && result.meta.values.any? { |arr| arr.include?(value) }
|
|
14
|
+
return nil if result.meta[key]&.include?(value)
|
|
15
|
+
|
|
16
|
+
result
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
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 Match < ResultFilter
|
|
10
|
+
def apply(result)
|
|
11
|
+
return nil if result.nil?
|
|
12
|
+
return result if value == "*" && result.meta.key?(key)
|
|
13
|
+
return result if key == "*" && result.meta.values.any? { |arr| arr.include?(value) }
|
|
14
|
+
return result if result.meta[key]&.include?(value)
|
|
15
|
+
|
|
16
|
+
nil
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
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 Meta < ResultFilter
|
|
10
|
+
def apply(result)
|
|
11
|
+
transform_with_meta(result) do |new_result|
|
|
12
|
+
meta_value = case value
|
|
13
|
+
when "_text_"
|
|
14
|
+
result.text
|
|
15
|
+
when "_value_"
|
|
16
|
+
result.value.to_s
|
|
17
|
+
when "_multiplier_"
|
|
18
|
+
result.multiplier.to_s
|
|
19
|
+
when "_length_"
|
|
20
|
+
result.text.length.to_s
|
|
21
|
+
else
|
|
22
|
+
value
|
|
23
|
+
end
|
|
24
|
+
new_result.merge_kv(key, meta_value)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,37 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "set"
|
|
4
|
+
require_relative "../result_filter"
|
|
5
|
+
|
|
3
6
|
module Text
|
|
4
7
|
module Gen
|
|
5
|
-
module
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def apply(result
|
|
31
|
-
|
|
32
|
-
Result.from(text:, type: :function, result:)
|
|
8
|
+
module Filter
|
|
9
|
+
module Result
|
|
10
|
+
class Pluralize < ResultFilter
|
|
11
|
+
EXCEPTIONS = {
|
|
12
|
+
"foot" => "feet",
|
|
13
|
+
"axis" => "axes",
|
|
14
|
+
"child" => "children",
|
|
15
|
+
"codex" => "codices",
|
|
16
|
+
"die" => "dice",
|
|
17
|
+
"dwarf" => "dwarves",
|
|
18
|
+
"goose" => "geese",
|
|
19
|
+
"elf" => "elves",
|
|
20
|
+
"man" => "men",
|
|
21
|
+
"ox" => "oxen",
|
|
22
|
+
"cactus" => "cacti",
|
|
23
|
+
"thesis" => "theses",
|
|
24
|
+
"criterion" => "criteria",
|
|
25
|
+
"thief" => "thieves",
|
|
26
|
+
"tooth" => "teeth",
|
|
27
|
+
"wolf" => "wolves",
|
|
28
|
+
"woman" => "women"
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
SINGLE = Set.new(%w[a an the this his her its my your our that their])
|
|
32
|
+
|
|
33
|
+
def apply(result)
|
|
34
|
+
transform_text(result, pluralize(result.text))
|
|
33
35
|
end
|
|
34
36
|
|
|
37
|
+
private
|
|
38
|
+
|
|
35
39
|
def pluralize(str)
|
|
36
40
|
return str if str.empty?
|
|
37
41
|
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../result_filter"
|
|
4
|
+
|
|
3
5
|
module Text
|
|
4
6
|
module Gen
|
|
5
|
-
module
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def apply(result
|
|
15
|
-
|
|
16
|
-
Result.from(text:, type: :function, result:)
|
|
7
|
+
module Filter
|
|
8
|
+
module Result
|
|
9
|
+
class Swap < ResultFilter
|
|
10
|
+
DIGIT_WORDS = {
|
|
11
|
+
"1" => "one", "2" => "two", "3" => "three",
|
|
12
|
+
"4" => "four", "5" => "five", "6" => "six",
|
|
13
|
+
"7" => "seven", "8" => "eight", "9" => "nine"
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
def apply(result)
|
|
17
|
+
transform_text(result, swap(result.text, key, value))
|
|
17
18
|
end
|
|
18
19
|
|
|
20
|
+
private
|
|
21
|
+
|
|
19
22
|
def swap(str, key, val)
|
|
20
23
|
return str if key.nil? || key.empty?
|
|
21
24
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "set"
|
|
4
|
+
require_relative "../result_filter"
|
|
5
|
+
|
|
6
|
+
module Text
|
|
7
|
+
module Gen
|
|
8
|
+
module Filter
|
|
9
|
+
module Result
|
|
10
|
+
class Titleize < ResultFilter
|
|
11
|
+
SKIP_WORDS = Set.new(%w[a an and as at but by for if in of on or the to v via vs])
|
|
12
|
+
|
|
13
|
+
def apply(result)
|
|
14
|
+
transform_text(result, titleize(result.text))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def titleize(str)
|
|
20
|
+
str.split(/\s+/).map.with_index do |word, idx|
|
|
21
|
+
idx.zero? || !SKIP_WORDS.include?(word) ? word.capitalize : word
|
|
22
|
+
end.join(" ")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
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 Upcase < ResultFilter
|
|
10
|
+
def apply(result)
|
|
11
|
+
transform_text(result, result.text.upcase)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
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 Result objects
|
|
9
|
+
# Subclasses should implement #apply(result) -> Result or nil
|
|
10
|
+
class ResultFilter < Base
|
|
11
|
+
def apply(result)
|
|
12
|
+
raise NotImplementedError, "Subclasses must implement #apply(result)"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
protected
|
|
16
|
+
|
|
17
|
+
# Helper to create a new result preserving metadata
|
|
18
|
+
def transform_text(result, new_text)
|
|
19
|
+
Text::Gen::Result.from(text: new_text, type: :function, result: result)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Helper to create a new result and modify metadata
|
|
23
|
+
def transform_with_meta(result, new_text = nil)
|
|
24
|
+
new_result = Text::Gen::Result.from(
|
|
25
|
+
text: new_text || result.text,
|
|
26
|
+
type: :function,
|
|
27
|
+
result: result
|
|
28
|
+
)
|
|
29
|
+
yield new_result if block_given?
|
|
30
|
+
new_result
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/text/gen/filter.rb
CHANGED
|
@@ -1,37 +1,60 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "
|
|
4
|
-
require_relative "
|
|
5
|
-
require_relative "
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
require_relative "
|
|
9
|
-
require_relative "
|
|
10
|
-
require_relative "
|
|
3
|
+
require_relative "filter/base"
|
|
4
|
+
require_relative "filter/result_filter"
|
|
5
|
+
require_relative "filter/item_filter"
|
|
6
|
+
|
|
7
|
+
# Result filters
|
|
8
|
+
require_relative "filter/result/capitalize"
|
|
9
|
+
require_relative "filter/result/censor"
|
|
10
|
+
require_relative "filter/result/clear"
|
|
11
|
+
require_relative "filter/result/downcase"
|
|
12
|
+
require_relative "filter/result/exclude"
|
|
13
|
+
require_relative "filter/result/match"
|
|
14
|
+
require_relative "filter/result/meta"
|
|
15
|
+
require_relative "filter/result/pluralize"
|
|
16
|
+
require_relative "filter/result/swap"
|
|
17
|
+
require_relative "filter/result/titleize"
|
|
18
|
+
require_relative "filter/result/upcase"
|
|
19
|
+
|
|
20
|
+
# Item filters
|
|
21
|
+
require_relative "filter/item/select"
|
|
22
|
+
require_relative "filter/item/reject"
|
|
11
23
|
|
|
12
24
|
module Text
|
|
13
25
|
module Gen
|
|
14
|
-
|
|
26
|
+
module Filter
|
|
15
27
|
SEPARATORS = { "tab" => "\t", "newline" => "\n", "space" => " " }.freeze
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
"capitalize" =>
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
29
|
+
RESULT_FILTER_CLASSES = {
|
|
30
|
+
"capitalize" => Filter::Result::Capitalize,
|
|
31
|
+
"censor" => Filter::Result::Censor,
|
|
32
|
+
"clear" => Filter::Result::Clear,
|
|
33
|
+
"downcase" => Filter::Result::Downcase,
|
|
34
|
+
"exclude" => Filter::Result::Exclude,
|
|
35
|
+
"match" => Filter::Result::Match,
|
|
36
|
+
"meta" => Filter::Result::Meta,
|
|
37
|
+
"pluralize" => Filter::Result::Pluralize,
|
|
38
|
+
"swap" => Filter::Result::Swap,
|
|
39
|
+
"titleize" => Filter::Result::Titleize,
|
|
40
|
+
"upcase" => Filter::Result::Upcase
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
ITEM_FILTER_CLASSES = {
|
|
44
|
+
"select" => Filter::Item::Select,
|
|
45
|
+
"reject" => Filter::Item::Reject
|
|
26
46
|
}.freeze
|
|
27
47
|
|
|
28
48
|
class << self
|
|
29
|
-
def functions(result, filters)
|
|
49
|
+
def functions(result, filters, lookup: nil)
|
|
30
50
|
return result if filters.nil? || filters.empty?
|
|
31
51
|
|
|
32
52
|
filters.each do |filter|
|
|
33
|
-
|
|
34
|
-
|
|
53
|
+
filter_class = RESULT_FILTER_CLASSES[filter["type"]]
|
|
54
|
+
if filter_class
|
|
55
|
+
filter_instance = filter_class.new(filter, lookup: lookup)
|
|
56
|
+
result = filter_instance.apply(result)
|
|
57
|
+
end
|
|
35
58
|
end
|
|
36
59
|
|
|
37
60
|
result
|
|
@@ -41,8 +64,12 @@ module Text
|
|
|
41
64
|
select_filters = filters_by_type(filters, "select")
|
|
42
65
|
return items if select_filters.empty?
|
|
43
66
|
|
|
67
|
+
# OR logic: any filter matches
|
|
44
68
|
items.select do |item|
|
|
45
|
-
select_filters.any?
|
|
69
|
+
select_filters.any? do |filter|
|
|
70
|
+
filter_instance = ITEM_FILTER_CLASSES["select"].new(filter)
|
|
71
|
+
filter_instance.apply([item]).any?
|
|
72
|
+
end
|
|
46
73
|
end
|
|
47
74
|
end
|
|
48
75
|
|
|
@@ -50,25 +77,34 @@ module Text
|
|
|
50
77
|
reject_filters = filters_by_type(filters, "reject")
|
|
51
78
|
return items if reject_filters.empty?
|
|
52
79
|
|
|
53
|
-
|
|
54
|
-
|
|
80
|
+
# AND logic: all filters must pass
|
|
81
|
+
reject_filters.each do |filter|
|
|
82
|
+
filter_instance = ITEM_FILTER_CLASSES["reject"].new(filter)
|
|
83
|
+
items = filter_instance.apply(items)
|
|
55
84
|
end
|
|
85
|
+
items
|
|
56
86
|
end
|
|
57
87
|
|
|
58
88
|
def result_select(result, filters)
|
|
59
89
|
match_filters = filters_by_type(filters, "match")
|
|
60
90
|
return result if match_filters.empty?
|
|
61
|
-
return result if match_filters.any? { |f| pass_select?(result.meta, f["key"], f["value"]) }
|
|
62
91
|
|
|
63
|
-
|
|
92
|
+
# OR logic: any filter matches
|
|
93
|
+
match_filters.any? do |filter|
|
|
94
|
+
filter_instance = RESULT_FILTER_CLASSES["match"].new(filter)
|
|
95
|
+
filter_instance.apply(result)
|
|
96
|
+
end ? result : nil
|
|
64
97
|
end
|
|
65
98
|
|
|
66
99
|
def result_reject(result, filters)
|
|
67
100
|
exclude_filters = filters_by_type(filters, "exclude")
|
|
68
101
|
return result if exclude_filters.empty?
|
|
69
|
-
return result if exclude_filters.all? { |f| pass_reject?(result.meta, f["key"], f["value"]) }
|
|
70
102
|
|
|
71
|
-
|
|
103
|
+
# AND logic: all filters must pass
|
|
104
|
+
exclude_filters.all? do |filter|
|
|
105
|
+
filter_instance = RESULT_FILTER_CLASSES["exclude"].new(filter)
|
|
106
|
+
filter_instance.apply(result)
|
|
107
|
+
end ? result : nil
|
|
72
108
|
end
|
|
73
109
|
|
|
74
110
|
# Create a builder that always returns a constant value
|
|
@@ -111,20 +147,6 @@ module Text
|
|
|
111
147
|
constant_item(locale_text, item)
|
|
112
148
|
end
|
|
113
149
|
|
|
114
|
-
def pass_select?(meta, key, value)
|
|
115
|
-
return true if value == "*" && meta.key?(key)
|
|
116
|
-
return true if key == "*" && meta.values.any? { |arr| arr.include?(value) }
|
|
117
|
-
|
|
118
|
-
meta[key]&.include?(value)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def pass_reject?(meta, key, value)
|
|
122
|
-
return false if value == "*" && meta.key?(key)
|
|
123
|
-
return false if key == "*" && meta.values.any? { |arr| arr.include?(value) }
|
|
124
|
-
|
|
125
|
-
!meta[key]&.include?(value)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
150
|
def separator(filters)
|
|
129
151
|
return "" unless filters
|
|
130
152
|
|
data/lib/text/gen/runner.rb
CHANGED
data/lib/text/gen/version.rb
CHANGED
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.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- G Palmer
|
|
@@ -42,14 +42,22 @@ files:
|
|
|
42
42
|
- Rakefile
|
|
43
43
|
- lib/text/gen.rb
|
|
44
44
|
- lib/text/gen/filter.rb
|
|
45
|
-
- lib/text/gen/
|
|
46
|
-
- lib/text/gen/
|
|
47
|
-
- lib/text/gen/
|
|
48
|
-
- lib/text/gen/
|
|
49
|
-
- lib/text/gen/
|
|
50
|
-
- lib/text/gen/
|
|
51
|
-
- lib/text/gen/
|
|
52
|
-
- lib/text/gen/
|
|
45
|
+
- lib/text/gen/filter/base.rb
|
|
46
|
+
- lib/text/gen/filter/item/reject.rb
|
|
47
|
+
- lib/text/gen/filter/item/select.rb
|
|
48
|
+
- lib/text/gen/filter/item_filter.rb
|
|
49
|
+
- lib/text/gen/filter/result/capitalize.rb
|
|
50
|
+
- lib/text/gen/filter/result/censor.rb
|
|
51
|
+
- lib/text/gen/filter/result/clear.rb
|
|
52
|
+
- lib/text/gen/filter/result/downcase.rb
|
|
53
|
+
- lib/text/gen/filter/result/exclude.rb
|
|
54
|
+
- lib/text/gen/filter/result/match.rb
|
|
55
|
+
- lib/text/gen/filter/result/meta.rb
|
|
56
|
+
- lib/text/gen/filter/result/pluralize.rb
|
|
57
|
+
- lib/text/gen/filter/result/swap.rb
|
|
58
|
+
- lib/text/gen/filter/result/titleize.rb
|
|
59
|
+
- lib/text/gen/filter/result/upcase.rb
|
|
60
|
+
- lib/text/gen/filter/result_filter.rb
|
|
53
61
|
- lib/text/gen/result.rb
|
|
54
62
|
- lib/text/gen/result_accumulator.rb
|
|
55
63
|
- lib/text/gen/runner.rb
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Text
|
|
4
|
-
module Gen
|
|
5
|
-
module Function
|
|
6
|
-
class Capitalizer
|
|
7
|
-
class << self
|
|
8
|
-
def apply(result, _filter)
|
|
9
|
-
text = result.text.capitalize
|
|
10
|
-
Result.from(text:, type: :function, result:)
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Text
|
|
4
|
-
module Gen
|
|
5
|
-
module Function
|
|
6
|
-
class Clear
|
|
7
|
-
class << self
|
|
8
|
-
def apply(result, filter)
|
|
9
|
-
new_result = Result.from(text: result.text, type: :function, result:)
|
|
10
|
-
new_result.clear_meta(filter["key"], filter["value"])
|
|
11
|
-
new_result
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Text
|
|
4
|
-
module Gen
|
|
5
|
-
module Function
|
|
6
|
-
class Downcase
|
|
7
|
-
class << self
|
|
8
|
-
def apply(result, _filter)
|
|
9
|
-
text = result.text.downcase
|
|
10
|
-
Result.from(text:, type: :function, result:)
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Text
|
|
4
|
-
module Gen
|
|
5
|
-
module Function
|
|
6
|
-
class Meta
|
|
7
|
-
class << self
|
|
8
|
-
def apply(result, filter)
|
|
9
|
-
new_result = Result.from(text: result.text, type: :function, result:)
|
|
10
|
-
new_result.merge_kv(filter["key"], filter["value"])
|
|
11
|
-
new_result
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
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 apply(result, _filter)
|
|
11
|
-
text = titleize(result.text)
|
|
12
|
-
Result.from(text:, type: :function, result:)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def titleize(str)
|
|
16
|
-
str.split(/\s+/).map.with_index do |word, idx|
|
|
17
|
-
idx.zero? || !SKIP_WORDS.include?(word) ? word.capitalize : word
|
|
18
|
-
end.join(" ")
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Text
|
|
4
|
-
module Gen
|
|
5
|
-
module Function
|
|
6
|
-
class Upcase
|
|
7
|
-
class << self
|
|
8
|
-
def apply(result, _filter)
|
|
9
|
-
text = result.text.upcase
|
|
10
|
-
Result.from(text:, type: :function, result:)
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|