Module: Legion::Extensions::Llm::Inventory::ImmutableValue
- Defined in:
- lib/legion/extensions/llm/inventory/immutable_value.rb
Overview
The single strict recursive copier/freezer for JSON-like metadata and collection fields. See phase-1-lex-llm-additive.md section 7.1.
Records call this helper before super; they never implement a
competing recursive-freeze helper. It recursively copies and freezes
only nil, true/false, Symbol, Integer, finite Float, String, Time,
Array, and Hash. Hash keys must be String or Symbol. Strings must be
valid UTF-8. Time values are duplicated and frozen. Arrays and Hashes
preserve order but never preserve a mutable nested reference. NaN,
infinity, an unsupported object, a callable, or a cyclic Array/Hash
raises ValidationError naming the field path.
Class Method Summary collapse
- .copy_and_freeze(value:, field:) ⇒ Object
-
.copy_array(value, path, ancestors) ⇒ Object
rubocop:enable Metrics/MethodLength.
- .copy_hash(value, path, ancestors) ⇒ Object
- .copy_key(key, path) ⇒ Object
- .cyclic?(value, ancestors) ⇒ Boolean
- .deep_copy(value, path, ancestors) ⇒ Object
- .valid_utf8?(string) ⇒ Boolean
Class Method Details
.copy_and_freeze(value:, field:) ⇒ Object
23 24 25 |
# File 'lib/legion/extensions/llm/inventory/immutable_value.rb', line 23 def copy_and_freeze(value:, field:) deep_copy(value, field.to_s, []) end |
.copy_array(value, path, ancestors) ⇒ Object
rubocop:enable Metrics/MethodLength
51 52 53 54 55 56 57 58 |
# File 'lib/legion/extensions/llm/inventory/immutable_value.rb', line 51 def copy_array(value, path, ancestors) raise Errors::ValidationError, "#{path} contains a cyclic Array reference" if cyclic?(value, ancestors) next_ancestors = ancestors + [value] value.each_with_index.map do |element, index| deep_copy(element, "#{path}[#{index}]", next_ancestors) end.freeze end |
.copy_hash(value, path, ancestors) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/legion/extensions/llm/inventory/immutable_value.rb', line 60 def copy_hash(value, path, ancestors) raise Errors::ValidationError, "#{path} contains a cyclic Hash reference" if cyclic?(value, ancestors) next_ancestors = ancestors + [value] copied = {} value.each do |key, element| frozen_key = copy_key(key, path) copied[frozen_key] = deep_copy(element, "#{path}.#{key}", next_ancestors) end copied.freeze end |
.copy_key(key, path) ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/legion/extensions/llm/inventory/immutable_value.rb', line 72 def copy_key(key, path) case key when ::Symbol key when ::String raise Errors::ValidationError, "#{path} has a key that is not valid UTF-8" unless valid_utf8?(key) key.dup.freeze else raise Errors::ValidationError, "#{path} has a non-String/Symbol key (#{key.class})" end end |
.cyclic?(value, ancestors) ⇒ Boolean
85 86 87 |
# File 'lib/legion/extensions/llm/inventory/immutable_value.rb', line 85 def cyclic?(value, ancestors) ancestors.any? { |ancestor| ancestor.equal?(value) } end |
.deep_copy(value, path, ancestors) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/legion/extensions/llm/inventory/immutable_value.rb', line 27 def deep_copy(value, path, ancestors) case value when nil, true, false, ::Symbol, ::Integer value when ::Float raise Errors::ValidationError, "#{path} is not a finite Float" unless value.finite? value when ::String raise Errors::ValidationError, "#{path} is not valid UTF-8" unless valid_utf8?(value) value.dup.freeze when ::Time value.dup.freeze when ::Array copy_array(value, path, ancestors) when ::Hash copy_hash(value, path, ancestors) else raise Errors::ValidationError, "#{path} is an unsupported value type (#{value.class})" end end |
.valid_utf8?(string) ⇒ Boolean
89 90 91 |
# File 'lib/legion/extensions/llm/inventory/immutable_value.rb', line 89 def valid_utf8?(string) [::Encoding::UTF_8, ::Encoding::US_ASCII].include?(string.encoding) && string.valid_encoding? end |