Class: Baseh::Profile::Prepared
- Inherits:
-
Object
- Object
- Baseh::Profile::Prepared
- Defined in:
- lib/baseh/profile.rb
Overview
Immutable, fully validated profile with pre-computed derived values.
Instance Attribute Summary collapse
-
#aliases ⇒ Object
readonly
Returns the value of attribute aliases.
-
#blocklist ⇒ Object
readonly
Returns the value of attribute blocklist.
-
#body_alphabet ⇒ Object
readonly
Returns the value of attribute body_alphabet.
-
#body_length ⇒ Object
readonly
Returns the value of attribute body_length.
-
#capacity ⇒ Object
readonly
Returns the value of attribute capacity.
-
#case_sensitive ⇒ Object
readonly
Returns the value of attribute case_sensitive.
-
#checksum_alphabet ⇒ Object
readonly
Returns the value of attribute checksum_alphabet.
-
#checksum_length ⇒ Object
readonly
Returns the value of attribute checksum_length.
-
#checksum_modulus ⇒ Object
readonly
Returns the value of attribute checksum_modulus.
-
#grouping ⇒ Object
readonly
Returns the value of attribute grouping.
-
#max_repetition ⇒ Object
readonly
Returns the value of attribute max_repetition.
-
#min_length ⇒ Object
readonly
Returns the value of attribute min_length.
-
#mode ⇒ Object
readonly
Returns the value of attribute mode.
-
#permutation ⇒ Object
readonly
Returns the value of attribute permutation.
-
#profanity_mode ⇒ Object
readonly
Returns the value of attribute profanity_mode.
-
#profile_id ⇒ Object
readonly
Returns the value of attribute profile_id.
-
#separator ⇒ Object
readonly
Returns the value of attribute separator.
-
#separator_min_length ⇒ Object
readonly
Returns the value of attribute separator_min_length.
-
#short_checksum_length ⇒ Object
readonly
Returns the value of attribute short_checksum_length.
-
#short_checksum_until ⇒ Object
readonly
Returns the value of attribute short_checksum_until.
Class Method Summary collapse
Instance Method Summary collapse
-
#effective_checksum_length(length) ⇒ Object
Spec 22.
-
#initialize(profile) ⇒ Prepared
constructor
A new instance of Prepared.
Constructor Details
#initialize(profile) ⇒ Prepared
Returns a new instance of Prepared.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/baseh/profile.rb', line 30 def initialize(profile) validate_type!(profile) @profile_id = validate_profile_id!(profile[:profile_id]) @case_sensitive = profile[:case_sensitive] == true # Spec 2.2/19.9. A persisted or frozen profile declares its mode; # profiles built before the mode field existed are fixed, so the # frozen vectors keep matching byte for byte. @mode = (profile[:mode] || "fixed").to_s unless %w[fixed expandable].include?(@mode) self.class.fail_profile!("mode must be fixed or expandable") end @body_alphabet = validate_body_alphabet!(profile[:body_alphabet]) # Spec 19.2: in expandable mode the zero ban strips 0 and O from the # body alphabet silently, before any other validation, exactly like # the no-vowels strip of section 18.1. if @mode == "expandable" @body_alphabet = @body_alphabet.delete("0O").freeze end @body_length = if @mode == "fixed" validate_integer!(profile[:body_length], 1, 32, "bodyLength") end @checksum_length = validate_integer!(profile[:checksum_length], 0, 8, "checksumLength") @min_length = (profile[:min_length] || 4) @separator_min_length = (profile[:separator_min_length] || 0) if @mode == "fixed" unless @separator_min_length.is_a?(Integer) && @separator_min_length.zero? self.class.fail_profile!("separatorMinLength must be 0 in fixed mode") end else unless @min_length.is_a?(Integer) && @min_length >= 1 self.class.fail_profile!("minLength must be an integer of at least 1") end unless @min_length > @checksum_length self.class.fail_profile!("minLength must be greater than checksumLength") end unless @separator_min_length.is_a?(Integer) && @separator_min_length >= 0 self.class.fail_profile!("separatorMinLength must be an integer of at least 0") end end # Spec 22. The short checksum is expandable-only; 0 or absent turns # it off. @short_checksum_length = profile[:short_checksum_length] || 0 @short_checksum_until = profile[:short_checksum_until] || 0 # Spec 22. The window field is the switch: shortChecksumUntil of 0 # turns the feature off (the codebase convention, like # maxRepetition), and the length field without a window is # INVALID_PROFILE. With a window set, a shortChecksumLength of 0 is # legal: the window's generations carry no checksum symbols at all. if @mode == "fixed" unless @short_checksum_length == 0 && @short_checksum_until == 0 self.class.fail_profile!( "shortChecksumLength and shortChecksumUntil are expandable-mode only" ) end elsif @short_checksum_until != 0 unless @short_checksum_until.is_a?(Integer) && @short_checksum_until >= @min_length self.class.fail_profile!("shortChecksumUntil must be an integer of at least minLength") end # Beyond 8 the window would swallow nearly every practical code, # and long codes genuinely want two checksum symbols. if @short_checksum_until > 8 self.class.fail_profile!("shortChecksumUntil must be at most 8") end unless @short_checksum_length.is_a?(Integer) && @short_checksum_length >= 0 && @short_checksum_length < @checksum_length self.class.fail_profile!( "shortChecksumLength must be an integer from 0 through checksumLength - 1" ) end unless @min_length > @short_checksum_length self.class.fail_profile!("minLength must be greater than shortChecksumLength") end elsif @short_checksum_length != 0 self.class.fail_profile!("shortChecksumLength requires shortChecksumUntil") end # Spec 19.3: in expandable mode the checksum alphabet is derived from # the body alphabet after every body strip; the configured # checksumAlphabet is not consulted. @checksum_alphabet = if @mode == "expandable" "".freeze else validate_checksum_alphabet!( profile[:checksum_alphabet], @checksum_length ) end # Spec 18: validation happens before the vowel strip so malformed # alphabets are reported as such, then no-vowels strips and # re-validates the result. @profanity_mode = validate_profanity_mode!(profile[:profanity]) if @profanity_mode == "no-vowels" @body_alphabet = Profanity.strip_vowels(@body_alphabet) @checksum_alphabet = Profanity.strip_vowels(@checksum_alphabet) validate_stripped!(@body_alphabet, "body") if @mode == "fixed" && @checksum_length.positive? validate_stripped!(@checksum_alphabet, "checksum") end @body_alphabet.freeze @checksum_alphabet.freeze end if @mode == "expandable" # Spec 19.3: the checksum alphabet is derived, "0" followed by the # body alphabet in order, after every body strip (zero ban, # no-vowels) so all downstream rules see the final alphabets. The # configured checksumAlphabet is not consulted. @checksum_alphabet = ("0" + @body_alphabet).freeze validate_stripped!(@body_alphabet, "body") end @blocklist = if @profanity_mode == "blocklist" Profanity.effective_blocklist(profile[:profanity]).freeze else [].freeze end # Spec 21: 0 disables the filter; an active filter needs a floor of # 3 — banning pairs (2) would destroy roughly 9% of every generation. @max_repetition = profile[:max_repetition] || 0 unless @max_repetition.is_a?(Integer) && @max_repetition >= 0 && (@max_repetition.zero? || @max_repetition >= 3) self.class.fail_profile!("maxRepetition must be 0 (off) or an integer of at least 3") end @separator = validate_separator!( profile[:separator].to_s, @body_alphabet, @checksum_alphabet ) @aliases = validate_aliases!( profile[:aliases] || {}, @body_alphabet, @checksum_alphabet, @case_sensitive ) @grouping = validate_grouping!( profile[:grouping], @separator, @body_length, @checksum_length ) @permutation = validate_permutation!(profile[:permutation] || { enabled: false }) # Capacity is the fixed-mode A^bodyLength; meaningless in expandable # mode, where Baseh#capacity refuses per spec 12.3. @capacity = @body_alphabet.length**(@body_length || 0) @checksum_modulus = [@checksum_alphabet.length, 1].max**@checksum_length freeze end |
Instance Attribute Details
#aliases ⇒ Object (readonly)
Returns the value of attribute aliases.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def aliases @aliases end |
#blocklist ⇒ Object (readonly)
Returns the value of attribute blocklist.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def blocklist @blocklist end |
#body_alphabet ⇒ Object (readonly)
Returns the value of attribute body_alphabet.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def body_alphabet @body_alphabet end |
#body_length ⇒ Object (readonly)
Returns the value of attribute body_length.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def body_length @body_length end |
#capacity ⇒ Object (readonly)
Returns the value of attribute capacity.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def capacity @capacity end |
#case_sensitive ⇒ Object (readonly)
Returns the value of attribute case_sensitive.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def case_sensitive @case_sensitive end |
#checksum_alphabet ⇒ Object (readonly)
Returns the value of attribute checksum_alphabet.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def checksum_alphabet @checksum_alphabet end |
#checksum_length ⇒ Object (readonly)
Returns the value of attribute checksum_length.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def checksum_length @checksum_length end |
#checksum_modulus ⇒ Object (readonly)
Returns the value of attribute checksum_modulus.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def checksum_modulus @checksum_modulus end |
#grouping ⇒ Object (readonly)
Returns the value of attribute grouping.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def grouping @grouping end |
#max_repetition ⇒ Object (readonly)
Returns the value of attribute max_repetition.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def max_repetition @max_repetition end |
#min_length ⇒ Object (readonly)
Returns the value of attribute min_length.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def min_length @min_length end |
#mode ⇒ Object (readonly)
Returns the value of attribute mode.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def mode @mode end |
#permutation ⇒ Object (readonly)
Returns the value of attribute permutation.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def permutation @permutation end |
#profanity_mode ⇒ Object (readonly)
Returns the value of attribute profanity_mode.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def profanity_mode @profanity_mode end |
#profile_id ⇒ Object (readonly)
Returns the value of attribute profile_id.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def profile_id @profile_id end |
#separator ⇒ Object (readonly)
Returns the value of attribute separator.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def separator @separator end |
#separator_min_length ⇒ Object (readonly)
Returns the value of attribute separator_min_length.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def separator_min_length @separator_min_length end |
#short_checksum_length ⇒ Object (readonly)
Returns the value of attribute short_checksum_length.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def short_checksum_length @short_checksum_length end |
#short_checksum_until ⇒ Object (readonly)
Returns the value of attribute short_checksum_until.
22 23 24 |
# File 'lib/baseh/profile.rb', line 22 def short_checksum_until @short_checksum_until end |
Class Method Details
.fail_profile!(reason) ⇒ Object
192 193 194 |
# File 'lib/baseh/profile.rb', line 192 def self.fail_profile!(reason) raise BasehError.new("INVALID_PROFILE", "Invalid baseH profile: #{reason}", safe_for_customer: false) end |
Instance Method Details
#effective_checksum_length(length) ⇒ Object
Spec 22. The checksum length that applies to a generation of the given total length: short_checksum_length at or below short_checksum_until, checksum_length above it (and always in fixed mode). The feature is on exactly when short_checksum_until is non-zero; a short_checksum_length of 0 then means the window's generations carry no checksum symbols at all.
181 182 183 184 185 186 187 188 |
# File 'lib/baseh/profile.rb', line 181 def effective_checksum_length(length) if @mode == "expandable" && @short_checksum_until.positive? && length <= @short_checksum_until @short_checksum_length else @checksum_length end end |