eunomia_gen 0.1.6 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dda6a6807dc92353a11e090f8d61cbeb238510a8a7a0f6d524e9a90328192317
4
- data.tar.gz: 0b54749ea476821a8511a355d6ddce2591128bc2417f60f8161d2405120708c4
3
+ metadata.gz: 2bd9d4df4c021ff782daad47aafdd3ec4e16bcabe47cccbf81cd9d5dbf3816c2
4
+ data.tar.gz: d5de7918c9d7d8ff2c62f7c5b4376c020442574f1dda00cc4de43529a1eafcb1
5
5
  SHA512:
6
- metadata.gz: b624e0b5f19812df73cacf9bf2abab0d55078f78cf5adc6d50eac078feb6d3bdce34d2e0ba5b2b9bae1d005a7049b971465399dd142b5c29dac876e49a048aa8
7
- data.tar.gz: ea823e74de17872ec4706c6648319f425e11ec9a25cf6c9581872c942d21af08495aab34e2b47f0c21142c4154759c618f00059b29674dad5196d3c5aa59496b
6
+ metadata.gz: b328162d070df9ed863886cae24a72e884b7b732bb5300dc42c09f7a0650de5921c008a68df9f40cfe2af6760f2ff48e51c0a2ec1f3336a56a7178628f6d8d94
7
+ data.tar.gz: 012fd582fb8a5726e5bd5ee55c57bbae27b8d51153f3e9e056e1f4cff81fe119a4f71cee36d03fd7da74bb76765f674f7dc0fc509fb5993d21f42b902399814a
data/.rubocop.yml CHANGED
@@ -1,9 +1,20 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ require: rubocop-rspec
4
+
1
5
  AllCops:
2
6
  NewCops: enable
3
- TargetRubyVersion: 3.0
7
+ TargetRubyVersion: 3.1
8
+ SuggestExtensions: false
4
9
 
5
10
  Style/StringLiterals:
6
11
  EnforcedStyle: double_quotes
7
12
 
8
13
  Style/StringLiteralsInInterpolation:
9
14
  EnforcedStyle: double_quotes
15
+
16
+ RSpec/ExampleLength:
17
+ Max: 20
18
+
19
+ RSpec/MultipleExpectations:
20
+ Max: 5
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,67 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2024-12-21 18:48:58 UTC using RuboCop version 1.66.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
11
+ Metrics/AbcSize:
12
+ Max: 29
13
+
14
+ # Offense count: 2
15
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
16
+ Metrics/MethodLength:
17
+ Max: 13
18
+
19
+ # Offense count: 2
20
+ # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
21
+ Metrics/ParameterLists:
22
+ Max: 8
23
+
24
+ # Offense count: 2
25
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
26
+ # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
27
+ Naming/MethodParameterName:
28
+ Exclude:
29
+ - 'lib/eunomia/result.rb'
30
+ - 'lib/eunomia/segment/dice.rb'
31
+
32
+ # Offense count: 1
33
+ # Configuration parameters: Max, CountAsOne.
34
+ RSpec/ExampleLength:
35
+ Exclude:
36
+ - 'spec/eunomia/selector_spec.rb'
37
+
38
+ # Offense count: 2
39
+ # Configuration parameters: Max.
40
+ RSpec/MultipleExpectations:
41
+ Exclude:
42
+ - 'spec/eunomia/segment/dice_spec.rb'
43
+ - 'spec/eunomia/segment_spec.rb'
44
+
45
+ # Offense count: 1
46
+ # Configuration parameters: AllowedPatterns.
47
+ # AllowedPatterns: ^expect_, ^assert_
48
+ RSpec/NoExpectationExample:
49
+ Exclude:
50
+ - 'spec/examples/**/*.rb'
51
+ - 'spec/eunomia/request_spec.rb'
52
+
53
+ # Offense count: 1
54
+ # Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata.
55
+ # Include: **/*_spec.rb
56
+ RSpec/SpecFilePathFormat:
57
+ Exclude:
58
+ - '**/spec/routing/**/*'
59
+ - 'spec/eunomia/tags_spec.rb'
60
+
61
+ # Offense count: 1
62
+ # Configuration parameters: AllowedConstants.
63
+ Style/Documentation:
64
+ Exclude:
65
+ - 'spec/**/*'
66
+ - 'test/**/*'
67
+ - 'lib/eunomia/separator.rb'
data/README.md CHANGED
@@ -71,7 +71,7 @@ data = [
71
71
  ]
72
72
 
73
73
  Eunomia.add(data)
74
- request = Eunomia::Request.new("fruit", alt_key: "es", unique: true, alts: { "kiwi" => "a small flightless bird" })
74
+ request = Eunomia::Request.new("fruit", locale: "es", unique: true, alts: { "kiwi" => "a small flightless bird" })
75
75
  arr = []
76
76
  5.times { arr << request.generate.to_s }
77
77
  p arr.join(", ") # => "manzana, banana, orange, pear, a small flightless bird"
@@ -101,7 +101,7 @@ request = Eunomia::Request.new("random-fruit", functions: %w[pluralize reverse])
101
101
  p request.generate.to_s # => "3 sananab"
102
102
 
103
103
  request = Eunomia::Request.new("random-fruit", functions: %w[reverse pluralize])
104
- p request.generate.to_s # => "3 ikiks"
104
+ p request.generate.to_s # => "3 iwiks"
105
105
  ```
106
106
 
107
107
  ### Weights
@@ -125,31 +125,31 @@ If the first segment is not a number the multiplier defaults to 1.
125
125
  A constant hash can be added to the request. A string that matches the hash key will be replaced by the
126
126
  constant value.
127
127
 
128
- ### Tags and Filtering
128
+ ### Filtering
129
129
 
130
- Tags can be used to filter items. For example, some plant names are also used as names. So a list of
131
- plants could be filtered to only those that are also names to generate a character name. Tags assigned
132
- to an item are also added as metadat to the result if the tag is in `key:value` format.
130
+ Filters can be used to filter items. For example, some plant names are also used as names. So a list of
131
+ plants could be filtered to only those that are also names to generate a character name. Filters assigned
132
+ to an item are also added as metadata to the result if the filter is in `key:value` format.
133
133
 
134
134
  ```ruby
135
135
  data = [
136
136
  { key: "plant", items: %w[[flower] [tree]] },
137
137
  { key: "flower", items: [
138
- { segments: "rose", tags: %w[name:plant] },
138
+ { segments: "rose", filters: %w[name:plant] },
139
139
  { segments: "hydrangea" }
140
140
  ]
141
141
  },
142
142
  { key: "tree", items: [
143
- { segments: "ash", tags: %w[name:plant] },
143
+ { segments: "ash", filters: %w[name:plant] },
144
144
  { segments: "oak" }
145
145
  ]
146
146
  }
147
147
  ]
148
148
 
149
149
  Eunomia.add(data)
150
- val = Eunomia.generate("plant", tags: %w[name:plant], functions: ["capitalize"])
150
+ val = Eunomia.generate("plant", filters: %w[name:plant], functions: ["capitalize"])
151
151
  p val.to_s # => "Rose" or "Ash"
152
- p val.meta # => { "tag" => "name:plant" }
152
+ p val.meta # => { "filter" => "name:plant" }
153
153
  ```
154
154
 
155
155
  ## Development
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Eunomia
4
+ # One generated element
2
5
  class Element
3
6
  attr_reader :orig, :str, :value, :multiplier, :meta, :children
4
7
 
@@ -6,40 +6,57 @@ module Eunomia
6
6
  class Generator
7
7
  include Eunomia::HashHelpers
8
8
 
9
- attr_reader :key,
10
- :aliases,
11
- :alts,
12
- :meta,
13
- :functions,
14
- :tags,
15
- :items,
16
- :selector,
17
- :sep
9
+ PASS_FILTER_ATTEMPTS = 20
10
+
11
+ # Normalized identifier for this generator
12
+ attr_reader :key
13
+
14
+ # Key value pairs of string values to swap out for this generator results
15
+ attr_reader :alts
16
+
17
+ # Key vaule pair of generator keys to values; if the key is seen the value is replaced
18
+ # with the constant value
19
+ attr_reader :constants
20
+
21
+ # Functions to apply to the generated string from this generator
22
+ attr_reader :functions
23
+
24
+ # Filters are key value paris that must match the generated metadata to be selected
25
+ attr_reader :filters
26
+
27
+ # Are the items selected in order (sequence) or random
28
+ attr_reader :gen
29
+
30
+ # The items in this generator
31
+ attr_reader :items
32
+
33
+ # The random number generator used to select items
34
+ attr_reader :selector
18
35
 
19
36
  def initialize(hsh)
20
37
  @key = field_or_raise(hsh, :key)
21
- @aliases = list_field(hsh, :aliases)
22
38
  @functions = list_field(hsh, :functions)
23
39
  @alts = hash_field(hsh, :alts)
24
- @meta = meta_field(hsh)
25
- @tags = tags_field(hsh)
40
+ @constants = hash_field(hsh, :constants)
41
+ @filters = filters_field(hsh)
42
+ @gen = field_or_nil(hsh, :gen).to_s == "sequence" ? :sequence : :random
26
43
  @selector = Eunomia::Selector.new(field_or_nil(hsh, :rng))
27
44
  @items = items_from(hsh)
28
45
  raise "Generators must have items" if @items.empty?
29
46
  end
30
47
 
48
+ def random?
49
+ gen == :random
50
+ end
51
+
52
+ def sequence?
53
+ !random?
54
+ end
55
+
31
56
  def items_from(hsh)
32
57
  list_field(hsh, :items).map do |item|
33
58
  item = { segments: item } if item.is_a?(String)
34
- Eunomia::Item.new(@key, item, @tags)
35
- end
36
- end
37
-
38
- def item_tags
39
- @item_tags ||= begin
40
- set = Set.new
41
- items.each { |item| set += item.available_tags }
42
- set
59
+ Eunomia::Item.new(@key, item)
43
60
  end
44
61
  end
45
62
 
@@ -50,22 +67,58 @@ module Eunomia
50
67
  alts[key][segment] || segment
51
68
  end
52
69
 
53
- # Select items that have all the given tag values
54
- def filter(tags)
55
- return items if tags.empty?
70
+ def passes_filters?(result)
71
+ return true if filters.empty?
56
72
 
57
- items.select { |item| item.match_tags?(tags) }
73
+ # For every filter defined on this generator
74
+ filters.all? do |key, values|
75
+ found = result.meta[key]
76
+ found && (values.include?("*") || found.any? { |f| values.include?(f) })
77
+ end
58
78
  end
59
79
 
60
80
  def generate(request)
61
- items = filter(request.tags)
81
+ trys = PASS_FILTER_ATTEMPTS
82
+
83
+ loop do
84
+ trys -= 1
85
+ break if trys <= 0
86
+
87
+ result = if random?
88
+ generate_random(request, items)
89
+ else
90
+ generate_sequence(request, items)
91
+ end
92
+ return result if passes_filters?(result)
93
+ end
94
+
95
+ raise "Unable to generate a filtered result after #{PASS_FILTER_ATTEMPTS} attempts"
96
+ end
97
+
98
+ def generate_random(request, items)
62
99
  item = selector.select(items)
63
100
  raise "No items found for #{key}" unless item
64
101
 
65
102
  result = item.generate(request)
66
- result.apply(alts, functions, locale: request.alt_key)
67
- result.merge_meta(meta)
103
+ result.apply(alts, functions, locale: request.locale)
68
104
  result
69
105
  end
106
+
107
+ def generate_sequence(request, items)
108
+ result = Eunomia::Result.new(key)
109
+ items.each do |item|
110
+ result.append(item.generate(request))
111
+ end
112
+ result
113
+ end
114
+
115
+ def to_h
116
+ hsh = { key:, gen:, items: items.map(&:to_h) }
117
+ hsh[:rng] = selector if selector.count
118
+ hsh[:filters] = filters_to_tags(filters) unless filters.empty?
119
+ hsh[:alts] = alts unless alts.empty?
120
+ hsh[:functions] = functions unless functions.empty?
121
+ hsh
122
+ end
70
123
  end
71
124
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eunomia
4
+ # Lookup methods for hashes to kandle symbol and string keys
4
5
  module HashHelpers
5
6
  def field_or_nil(hsh, key)
6
7
  hsh[key.to_sym] || hsh[key.to_s]
@@ -25,11 +26,16 @@ module Eunomia
25
26
  field.is_a?(String) ? field.split(/\s+/) : field
26
27
  end
27
28
 
28
- def tags_field(hsh)
29
- tags = list_field(hsh, :tags)
30
- return Set.new unless tags
29
+ def filters_field(hsh)
30
+ tags = list_field(hsh, :filters)
31
+ tags.each_with_object({}) do |tag, filters|
32
+ key, values = tag.split(":")
33
+ filters[key] = Set.new(values.split(","))
34
+ end
35
+ end
31
36
 
32
- Set.new(tags)
37
+ def filters_to_tags(hsh)
38
+ hsh.to_a.map { |k, v| "#{k}:#{v.join(",")}" }.join(" ")
33
39
  end
34
40
 
35
41
  def alts_field(hsh)
data/lib/eunomia/item.rb CHANGED
@@ -8,13 +8,12 @@ module Eunomia
8
8
  attr_reader :key,
9
9
  :weight,
10
10
  :value,
11
- :tags,
12
11
  :alts,
13
12
  :meta,
14
13
  :functions,
15
14
  :segments
16
15
 
17
- def initialize(key, hsh, add_tags = nil)
16
+ def initialize(key, hsh)
18
17
  @key = key
19
18
  @weight = int_field(hsh, :weight).clamp(1, 1000)
20
19
  @value = int_field(hsh, :value).clamp(0, 1_000_000)
@@ -22,22 +21,10 @@ module Eunomia
22
21
  @meta = meta_field(hsh)
23
22
  @functions = list_field(hsh, :functions)
24
23
  @segments = scan(field_or_raise(hsh, :segments)).flatten
25
- @tags = tags_field(hsh)
26
- @tags += add_tags if add_tags
27
- @available_tags = nil
28
24
  end
29
25
 
30
- def available_tags
31
- @available_tags ||= begin
32
- s = Set.new
33
- s += tags
34
- refs = segments.filter { |seg| seg.is_a?(Eunomia::Segment::Reference) }
35
- unless refs.empty?
36
- s = refs[0].item_tags
37
- refs[1..].each { |ref| s &= ref.item_tags }
38
- end
39
- s
40
- end
26
+ def meta_keys
27
+ @meta_keys ||= (Set.new(meta.keys) + segments.map(&:meta_keys).inject(&:merge))
41
28
  end
42
29
 
43
30
  def scan(obj)
@@ -47,14 +34,6 @@ module Eunomia
47
34
  obj.map { |e| Eunomia::Segment.build(e) }.flatten
48
35
  end
49
36
 
50
- def match_tags?(tags)
51
- return true if tags.nil? || tags.empty?
52
- return true if (tags - self.tags).empty?
53
- return true if (tags - available_tags).empty?
54
-
55
- false
56
- end
57
-
58
37
  def alt_for(key, segment)
59
38
  return segment unless key
60
39
  return segment unless alts[key]
@@ -63,12 +42,23 @@ module Eunomia
63
42
  end
64
43
 
65
44
  def generate(request)
66
- result = Eunomia::Result.new(key, value: value)
45
+ result = Eunomia::Result.new(key, value:)
67
46
  segments.each { |seg| result.append(seg.generate(request)) }
68
- result.apply(alts, functions, locale: request.alt_key)
47
+ result.apply(alts, functions, locale: request.locale)
69
48
  result.merge_meta(meta)
70
- result.add_tags_as_meta(tags)
71
49
  result
72
50
  end
51
+
52
+ def to_h
53
+ hsh = {
54
+ segments: segments.map(&:to_s).join,
55
+ weight:
56
+ }
57
+
58
+ hsh[:value] = value if value != 0
59
+ hsh[:meta] = meta unless meta.empty?
60
+ hsh[:functinos] = functions unless functions.empty?
61
+ hsh
62
+ end
73
63
  end
74
64
  end
@@ -1,31 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eunomia
4
- class Request
5
- attr_reader :key, :alt_key, :alts, :meta, :tags, :functions, :constants, :depth
6
-
7
- def initialize(key, alts: {}, alt_key: nil, meta: {}, tags: [], functions: [], constants: {}, unique: false)
8
- @key = key
9
- @alt_key = alt_key
10
- @alts = alts || {}
11
- @meta = meta || {}
12
- @tags = Set.new(tags || [])
13
- @constants = constants || {}
14
- @functions = functions || []
15
- @depth = 0
16
- @unique = unique ? Set.new : nil
17
- end
4
+ # Encapsuate a request for string generation
5
+ class Request < Eunomia::Generator
6
+ # If not nil, a set of value strings; this is
7
+ # Persisted across calls to generate
8
+ attr_reader :unique
18
9
 
19
- def generate_unique?
20
- !@unique.nil?
21
- end
10
+ # If given, alts are chosen from the given locale key
11
+ attr_reader :locale
22
12
 
23
- def alt_key?
24
- !alt_key.nil?
25
- end
13
+ # The current depth (calls to generate) for the run
14
+ attr_reader :depth
26
15
 
27
- def alt_for(segment)
28
- alts[segment] || segment
16
+ def initialize(key, alts: {}, locale: nil, constants: {}, filters: [], functions: [], unique: false)
17
+ hsh = { key:, items: ["request"], alts:, constants:, filters:, gen: "sequence", functions: }
18
+ @unique = unique ? Set.new : nil
19
+ @depth = 0
20
+ @locale = locale
21
+ super(hsh)
29
22
  end
30
23
 
31
24
  def increase_depth
@@ -40,10 +33,10 @@ module Eunomia
40
33
  100.times do
41
34
  result = gen.generate(self)
42
35
  result.apply(alts, functions)
43
- return result if !@unique || @unique.add?(result.to_s)
36
+ return result if passes_filters?(result) && (!unique || unique.add?(result.to_s))
44
37
  end
45
38
 
46
- raise "Unable to find a unique result"
39
+ raise "Unable to find a unique result that passes filters"
47
40
  end
48
41
  end
49
42
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eunomia
4
+ # A generated result
4
5
  class Result
5
6
  attr_reader :key, :display, :elements, :multiplier, :base_value, :meta
6
7
 
@@ -9,16 +10,17 @@ module Eunomia
9
10
  @base_value = value
10
11
  @multiplier = multiplier
11
12
  @elements = []
12
- @meta = Hash.new {|h,k| h[k] = Set.new}
13
+ @meta = Hash.new { |h, k| h[k] = Set.new }
13
14
  @display = ""
14
15
  end
15
16
 
16
17
  def append(obj)
17
- if obj.is_a?(Element)
18
+ case obj
19
+ when Element
18
20
  append_element(obj)
19
- elsif obj.is_a?(Result)
21
+ when Result
20
22
  append_result(obj)
21
- elsif obj.is_a?(Separator)
23
+ when Separator
22
24
  append_separator(obj)
23
25
  end
24
26
  end
@@ -55,19 +57,11 @@ module Eunomia
55
57
  end
56
58
  end
57
59
 
58
- def add_tags_as_meta(tags)
59
- tags.each do |tag|
60
- arr = tag.split(':')
61
- @meta[arr[0]] << arr[1].gsub("-", " ") if arr.length > 1
62
- end
63
- end
64
-
65
-
66
60
  def apply_translations(alts, locale: nil)
67
61
  arr = to_s.split(/\s+/)
68
- arr = arr.map do |segment|
62
+ arr.map do |segment|
69
63
  hsh = alts[segment]
70
- hsh = hsh.is_a?(Hash) ? hsh[locale] || hsh['*'] || segment : hsh || segment
64
+ hsh.is_a?(Hash) ? hsh[locale] || hsh["*"] || segment : hsh || segment
71
65
  end
72
66
  end
73
67
 
@@ -76,10 +70,10 @@ module Eunomia
76
70
  end
77
71
 
78
72
  def apply(alts, functions, locale: nil)
79
- arr = apply_translations(alts, locale: locale)
73
+ arr = apply_translations(alts, locale:)
80
74
  arr = apply_functions(arr, functions)
81
75
 
82
- @display = arr.join(' ')
76
+ @display = arr.join(" ")
83
77
  self
84
78
  end
85
79
 
@@ -14,7 +14,11 @@ module Eunomia
14
14
 
15
15
  def generate(_request)
16
16
  calc # update values for dynamic segments
17
- Eunomia::Element.new(text, value: value, multiplier: multiplier)
17
+ Eunomia::Element.new(text, value:, multiplier:)
18
+ end
19
+
20
+ def meta_keys
21
+ Set.new
18
22
  end
19
23
 
20
24
  def calc; end
@@ -2,10 +2,11 @@
2
2
 
3
3
  module Eunomia
4
4
  module Segment
5
+ # Represents a constant in a segment.
5
6
  class Constant
6
7
  include Common
7
8
 
8
- SEPARATOR_MATCHER = /\p{Space}|\p{Punct}/
9
+ SEPARATOR_MATCHER = /\p{Space}|\p{Punct}|[+]/
9
10
 
10
11
  attr_reader :text
11
12
 
@@ -14,10 +15,14 @@ module Eunomia
14
15
  end
15
16
 
16
17
  def generate(_request)
17
- calc # update values for dynamic segments
18
+ # calc # update values for dynamic segments
18
19
  Eunomia::Separator.new(text)
19
20
  end
20
21
 
22
+ def to_s
23
+ text
24
+ end
25
+
21
26
  def self.build(scanner)
22
27
  str = scanner.scan(SEPARATOR_MATCHER)
23
28
  new(str) if str
@@ -54,6 +54,10 @@ module Eunomia
54
54
  @multiplier ||= calc
55
55
  end
56
56
 
57
+ def to_s
58
+ "#{count}d#{range}#{opp}#{constant}"
59
+ end
60
+
57
61
  def self.build(scanner)
58
62
  str = scanner.scan(DICE_MATCHER)
59
63
  return unless str
@@ -2,10 +2,12 @@
2
2
 
3
3
  module Eunomia
4
4
  module Segment
5
+ # Represents a number in a segment. The value
6
+ # is the multiplier for the item.
5
7
  class Number
6
8
  include Common
7
9
 
8
- NUMBER_MATCHER = /(\d+)/
10
+ NUMBER_MATCHER = /([+-]?\d+)/
9
11
 
10
12
  attr_reader :text, :multipler
11
13
 
@@ -14,6 +16,10 @@ module Eunomia
14
16
  @text = number.to_s
15
17
  end
16
18
 
19
+ def to_s
20
+ text
21
+ end
22
+
17
23
  def self.build(scanner)
18
24
  str = scanner.scan(NUMBER_MATCHER)
19
25
  new(str) if str
@@ -30,8 +30,8 @@ module Eunomia
30
30
  @generator ||= Eunomia.lookup(key)
31
31
  end
32
32
 
33
- def item_tags
34
- generator.item_tags
33
+ def meta_keys
34
+ generator.meta_keys
35
35
  end
36
36
 
37
37
  def generate(request)
@@ -45,6 +45,10 @@ module Eunomia
45
45
  @lookup ||= label.nil? ? key : "#{key}:#{label}"
46
46
  end
47
47
 
48
+ def to_s
49
+ "[#{lookup}]"
50
+ end
51
+
48
52
  def self.build(scanner)
49
53
  str = scanner.scan(REFERENCE_MATCHER)
50
54
  return unless str
@@ -14,6 +14,10 @@ module Eunomia
14
14
  @text = text
15
15
  end
16
16
 
17
+ def to_s
18
+ text
19
+ end
20
+
17
21
  def self.build(scanner)
18
22
  str = scanner.scan(TEXT_MATCHER)
19
23
  new(str) if str
@@ -10,6 +10,8 @@ require_relative "segment/number"
10
10
  require_relative "segment/reference"
11
11
 
12
12
  module Eunomia
13
+ class EunomiaParseError < StandardError; end
14
+
13
15
  # Segment represents a single token in an `Item`.
14
16
  module Segment
15
17
  def self.build(str)
@@ -21,7 +23,7 @@ module Eunomia
21
23
  Eunomia::Segment::Number.build(ss) ||
22
24
  Eunomia::Segment::Text.build(ss) ||
23
25
  Eunomia::Segment::Constant.build(ss)
24
- raise "Unable to parse #{ss.rest} (#{ss.rest_size})" unless seg
26
+ raise EunomiaParseError, "Unable to parse #{ss.rest} (#{ss.rest_size})" unless seg
25
27
 
26
28
  arr << seg
27
29
  end
@@ -39,6 +39,14 @@ module Eunomia
39
39
  nil
40
40
  end
41
41
 
42
+ def sequence(items)
43
+ # Weights for a sequence are the chance of not appearing in the
44
+ # result
45
+ items.select do |item|
46
+ item.weight <= count.nil? ? rand(1..100) : roll
47
+ end
48
+ end
49
+
42
50
  def random(items)
43
51
  max_weight = items.map(&:weight).sum
44
52
  if count.nil? || count >= max_weight
@@ -50,10 +58,13 @@ module Eunomia
50
58
  end
51
59
  end
52
60
 
61
+ # zero-index roll of dice (e.g. 3d6 => 0..15)
53
62
  def roll
54
- sum = 0
55
- count.times { sum += rand(range) }
56
- sum
63
+ (1..count).inject(0) { |sum, _| sum + rand(range) }
64
+ end
65
+
66
+ def to_s
67
+ count ? "#{count}d#{range}" : "random"
57
68
  end
58
69
  end
59
70
  end
@@ -13,7 +13,7 @@ module Eunomia
13
13
  end
14
14
 
15
15
  def to_h
16
- { orig: text, text: text }
16
+ { orig: text, text: }
17
17
  end
18
18
  end
19
19
  end
data/lib/eunomia/store.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eunomia
4
+ class EunomiaKeyNotFoundError < StandardError; end
5
+
4
6
  # Store holds all the generators. Generators are stored by key.
5
7
  class Store
6
8
  def initialize
@@ -8,7 +10,7 @@ module Eunomia
8
10
  end
9
11
 
10
12
  def lookup(key)
11
- @generators[key] or raise Error, "Generator #{key} not found"
13
+ @generators[key] or raise EunomiaKeyNotFoundError, "Generator #{key} not found"
12
14
  end
13
15
 
14
16
  def keys
@@ -31,9 +33,7 @@ module Eunomia
31
33
  else
32
34
  gen = Eunomia::Generator.new(hsh_or_array)
33
35
  @generators[gen.key] = gen
34
- gen.aliases.each do |alias_key|
35
- @generators[alias_key] = gen
36
- end
36
+ gen
37
37
  end
38
38
  end
39
39
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eunomia
4
- VERSION = "0.1.6"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/eunomia.rb CHANGED
@@ -13,42 +13,41 @@ require_relative "eunomia/result"
13
13
  require_relative "eunomia/segment"
14
14
  require_relative "eunomia/functions"
15
15
 
16
+ # A random data generator
16
17
  module Eunomia
17
- class Error < StandardError; end
18
-
19
- @@functions = Functions.new
20
- @@generators = Store.new
18
+ @functions = Functions.new
19
+ @generators = Store.new
21
20
 
22
21
  def self.lookup(key)
23
- @@generators.lookup(key)
22
+ @generators.lookup(key)
24
23
  end
25
24
 
26
25
  def self.generate(key, request = nil)
27
- request = Request.new(key) unless request
28
- @@generators.lookup(key).generate(request)
26
+ request ||= Request.new(key)
27
+ @generators.lookup(key).generate(request)
29
28
  end
30
29
 
31
30
  def self.read(path)
32
- @@generators.read(path)
31
+ @generators.read(path)
33
32
  end
34
33
 
35
34
  def self.add(hsh_or_array)
36
- @@generators.add(hsh_or_array)
35
+ @generators.add(hsh_or_array)
37
36
  end
38
37
 
39
38
  def self.add_function(name, function)
40
- @@functions.add(name, function)
39
+ @functions.add(name, function)
41
40
  end
42
41
 
43
42
  def self.keys
44
- @@generators.keys
43
+ @generators.keys
45
44
  end
46
45
 
47
- def self.request(key, alts: {}, alt_key: nil, meta: {}, tags: [], functions: [], constants: {}, unique: false)
48
- Request.new(key, alts:, alt_key:, meta:, tags:, functions:, constants:, unique:)
46
+ def self.request(key, alts: {}, locale: nil, filters: [], functions: [], constants: {}, unique: false)
47
+ Request.new(key, alts:, locale:, filters:, functions:, constants:, unique:)
49
48
  end
50
49
 
51
50
  def self.apply(arr, functions)
52
- @@functions.apply(arr, functions)
51
+ @functions.apply(arr, functions)
53
52
  end
54
53
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eunomia_gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - G Palmer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-09-17 00:00:00.000000000 Z
11
+ date: 2025-10-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Create random string with a focus on RPGs similar to fantasynamegenerators.com
14
14
  email:
@@ -20,6 +20,7 @@ extra_rdoc_files: []
20
20
  files:
21
21
  - ".rspec"
22
22
  - ".rubocop.yml"
23
+ - ".rubocop_todo.yml"
23
24
  - ".zed/settings.json"
24
25
  - ".zed/tasks.json"
25
26
  - CHANGELOG.md
@@ -71,14 +72,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
72
  requirements:
72
73
  - - ">="
73
74
  - !ruby/object:Gem::Version
74
- version: 3.0.0
75
+ version: 3.1.0
75
76
  required_rubygems_version: !ruby/object:Gem::Requirement
76
77
  requirements:
77
78
  - - ">="
78
79
  - !ruby/object:Gem::Version
79
80
  version: 3.3.11
80
81
  requirements: []
81
- rubygems_version: 3.5.11
82
+ rubygems_version: 3.5.23
82
83
  signing_key:
83
84
  specification_version: 4
84
85
  summary: Generate random strings from a hash specification