Class: Lutaml::Model::Type::Value
- Inherits:
-
Object
- Object
- Lutaml::Model::Type::Value
- Includes:
- UninitializedClassGuard, Xml::Type::Configurable
- Defined in:
- lib/lutaml/model/type/value.rb
Overview
Base class for all value types
Direct Known Subclasses
Boolean, Date, DateTime, Decimal, Duration, Float, Hash, Integer, QName, Reference, String, Symbol, Time, TimeWithoutDate, Union
Constant Summary collapse
- EMPTY_OPTIONS =
Performance optimization: reusable empty options hash Use options.equal?(EMPTY_OPTIONS) for fast-path checks
{}.freeze
- MIN_FACETS =
Canonical facet keys grouped by how a tighter constraint compares: a min-like facet tightens upward (greater wins), a max-like facet tightens downward (lesser wins).
:length(exact) only matches equal. %i[min_inclusive min_exclusive min_length].freeze
- MAX_FACETS =
%i[ max_inclusive max_exclusive max_length total_digits fraction_digits ].freeze
- LIST_FACETS =
Facets whose combined value is the concatenation of every declaration in the chain: accumulating them is always a tightening (all patterns must match), so they bypass the widen check on inheritance.
%i[pattern].freeze
- WHITE_SPACE_MODES =
xs:whiteSpace normalization modes in ascending strictness: a stricter mode transforms at least as much as a looser one, so
tighter_facetpicks the stricter and a subclass may tighten but not loosen it. %i[preserve replace collapse].freeze
Instance Attribute Summary collapse
-
#value ⇒ Object
readonly
Returns the value of attribute value.
Class Method Summary collapse
- .cast(value, _options = {}) ⇒ Object
-
.enumeration(*values) ⇒ Object
Restrict to an enumerated set of allowed values (any type).
-
.exclusive(min: nil, max: nil) ⇒ Object
Constrain ordered values via min/maxExclusive.
-
.facets ⇒ Object
Effective canonical facets for this type, merged parent-first.
-
.format_type_serializer_for(format, type_class) ⇒ Hash?
Look up a format type serializer, walking the class hierarchy.
-
.fraction_digits(count) ⇒ Object
Cap the number of significant fraction digits (xs:fractionDigits) an integer- or decimal-derived value may carry (a maximum).
-
.from_format(value, format) ⇒ Object
Class-level format conversion.
-
.inclusive(min: nil, max: nil) ⇒ Object
Constrain ordered values via min/maxInclusive.
-
.inherited(subclass) ⇒ Object
Freeze a type's facets to further declarations once it is subclassed, so a parent cannot widen facets after children exist.
-
.length(exact = nil, min: nil, max: nil) ⇒ Object
Constrain length: exact
length N, orlength min:, max:. -
.pattern(regex_or_string) ⇒ Object
Restrict a string-derived value to match a regular expression.
-
.register_format_to_from_methods(format) ⇒ Object
Called from FormatRegistry when a new format is registered.
-
.register_format_type_serializer(format, type_class, to: nil, from: nil) ⇒ Object
Register a custom type serializer for a specific format and type class.
- .serialize(value) ⇒ Object
-
.tighter_facet(key, existing, incoming) ⇒ Object
Conjunctive-merge primitive for two facet values of the same key: the tighter one wins (greater for min-like, lesser for max-like), an exact
:lengthmust agree. -
.total_digits(count) ⇒ Object
Cap the total number of significant digits (xs:totalDigits) an integer- or decimal-derived value may carry.
-
.white_space(mode) ⇒ Object
Normalize a string-derived value's whitespace at cast time (xs:whiteSpace).
Instance Method Summary collapse
-
#initialize(value) ⇒ Value
constructor
A new instance of Value.
- #initialized? ⇒ Boolean
-
#to_s ⇒ Object
Instance methods for serialization.
Methods included from Xml::Type::Configurable
Methods included from UninitializedClassGuard
Constructor Details
#initialize(value) ⇒ Value
Returns a new instance of Value.
306 307 308 |
# File 'lib/lutaml/model/type/value.rb', line 306 def initialize(value) @value = self.class.cast(value) end |
Instance Attribute Details
#value ⇒ Object (readonly)
Returns the value of attribute value.
304 305 306 |
# File 'lib/lutaml/model/type/value.rb', line 304 def value @value end |
Class Method Details
.cast(value, _options = {}) ⇒ Object
314 315 316 317 318 319 |
# File 'lib/lutaml/model/type/value.rb', line 314 def self.cast(value, = {}) return nil if value.nil? return value if Utils.uninitialized?(value) value end |
.enumeration(*values) ⇒ Object
Restrict to an enumerated set of allowed values (any type). A
subclass narrows the parent's set; the effective set is the
intersection across the chain (see tighter_facet).
158 159 160 |
# File 'lib/lutaml/model/type/value.rb', line 158 def enumeration(*values) declare_facets(enumeration: values.map { |v| cast_facet_value(v) }) end |
.exclusive(min: nil, max: nil) ⇒ Object
Constrain ordered values via min/maxExclusive.
138 139 140 141 142 143 |
# File 'lib/lutaml/model/type/value.rb', line 138 def exclusive(min: nil, max: nil) min = cast_facet_value(min) max = cast_facet_value(max) raise_unordered!("exclusive", min, max) declare_facets(min_exclusive: min, max_exclusive: max) end |
.facets ⇒ Object
Effective canonical facets for this type, merged parent-first. A subclass may only tighten an inherited facet; widening raises.
103 104 105 106 107 108 109 |
# File 'lib/lutaml/model/type/value.rb', line 103 def facets facet_layers.each_with_object({}) do |layer, merged| layer.each do |key, value| merged[key] = inherit_facet(key, merged[key], value) end end end |
.format_type_serializer_for(format, type_class) ⇒ Hash?
Look up a format type serializer, walking the class hierarchy.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/lutaml/model/type/value.rb', line 69 def format_type_serializer_for(format, type_class) # Hot path: memoized per format -> class with NO key # allocation — the previous [format, class] array key # allocated on every lookup (2k arrays per 200 serializes in # the object-mode profile). Identity-keyed nested maps; the # hierarchy walk below only runs on a miss, and format # serializers register at load time, so a hit is stable. per_format = (SERIALIZER_LOOKUP_CACHE[format] ||= {}.compare_by_identity) return per_format[type_class] if per_format.key?(type_class) klass = type_class found = nil while klass && klass <= Value s = @format_type_serializers[[format, klass]] if s found = s break end klass = klass.superclass end per_format[type_class] = found found end |
.fraction_digits(count) ⇒ Object
Cap the number of significant fraction digits (xs:fractionDigits) an integer- or decimal-derived value may carry (a maximum).
199 200 201 |
# File 'lib/lutaml/model/type/value.rb', line 199 def fraction_digits(count) declare_digit_facet(:fraction_digits, count, minimum: 0) end |
.from_format(value, format) ⇒ Object
Class-level format conversion
334 335 336 |
# File 'lib/lutaml/model/type/value.rb', line 334 def self.from_format(value, format) new(send(:"from_#{format}", value)) end |
.inclusive(min: nil, max: nil) ⇒ Object
Constrain ordered values via min/maxInclusive.
130 131 132 133 134 135 |
# File 'lib/lutaml/model/type/value.rb', line 130 def inclusive(min: nil, max: nil) min = cast_facet_value(min) max = cast_facet_value(max) raise_unordered!("inclusive", min, max) declare_facets(min_inclusive: min, max_inclusive: max) end |
.inherited(subclass) ⇒ Object
Freeze a type's facets to further declarations once it is subclassed, so a parent cannot widen facets after children exist.
96 97 98 99 |
# File 'lib/lutaml/model/type/value.rb', line 96 def inherited(subclass) super @facets_closed = true end |
.length(exact = nil, min: nil, max: nil) ⇒ Object
Constrain length: exact length N, or length min:, max:.
146 147 148 149 150 151 152 153 |
# File 'lib/lutaml/model/type/value.rb', line 146 def length(exact = nil, min: nil, max: nil) return declare_exact_length(exact, min, max) unless exact.nil? raise_unordered!("length", min, max) raise_negative_length!(min) raise_negative_length!(max) declare_facets(min_length: min, max_length: max) end |
.pattern(regex_or_string) ⇒ Object
Restrict a string-derived value to match a regular expression. A String argument is compiled to a Regexp. Patterns accumulate across the chain; a value must match all of them.
165 166 167 168 |
# File 'lib/lutaml/model/type/value.rb', line 165 def pattern(regex_or_string) regexp = regex_or_string.is_a?(Regexp) ? regex_or_string : Regexp.new(regex_or_string) declare_facets(pattern: [regexp]) end |
.register_format_to_from_methods(format) ⇒ Object
Called from FormatRegistry when a new format is registered. Defines to_format and from_format methods that check the serializer registry first, falling back to default behavior.
341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/lutaml/model/type/value.rb', line 341 def self.register_format_to_from_methods(format) define_method(:"to_#{format}") do s = Value.format_type_serializer_for(format, self.class) s&.dig(:to) ? s[:to].call(self) : value end define_singleton_method(:"from_#{format}") do |v| s = Value.format_type_serializer_for(format, self) s&.dig(:from) ? s[:from].call(v) : cast(v) end end |
.register_format_type_serializer(format, type_class, to: nil, from: nil) ⇒ Object
Register a custom type serializer for a specific format and type class. Format plugins call this at load time to register their custom serialization logic.
58 59 60 61 62 |
# File 'lib/lutaml/model/type/value.rb', line 58 def register_format_type_serializer(format, type_class, to: nil, from: nil) @format_type_serializers[[format, type_class]] = { to: to, from: from }.compact end |
.serialize(value) ⇒ Object
321 322 323 324 325 326 |
# File 'lib/lutaml/model/type/value.rb', line 321 def self.serialize(value) return nil if value.nil? return value if Utils.uninitialized?(value) new(value).to_s end |
.tighter_facet(key, existing, incoming) ⇒ Object
Conjunctive-merge primitive for two facet values of the same key:
the tighter one wins (greater for min-like, lesser for max-like),
an exact :length must agree. The canonical home for facet
combination, reused by the Layer-1/Layer-2 merge.
115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/lutaml/model/type/value.rb', line 115 def tighter_facet(key, existing, incoming) return incoming if existing.nil? return existing if incoming.nil? return incoming & existing if key == :enumeration return existing + incoming if LIST_FACETS.include?(key) return [existing, incoming].max if MIN_FACETS.include?(key) return [existing, incoming].min if MAX_FACETS.include?(key) return stricter_white_space(existing, incoming) if key == :white_space return existing if existing == incoming raise ArgumentError, "conflicting `#{key}` facets: #{existing} and #{incoming}" end |
.total_digits(count) ⇒ Object
Cap the total number of significant digits (xs:totalDigits) an
integer- or decimal-derived value may carry. A maximum; the value
must have at most count significant digits. Applicability to the
resolved type is enforced lazily at validation time.
193 194 195 |
# File 'lib/lutaml/model/type/value.rb', line 193 def total_digits(count) declare_digit_facet(:total_digits, count, minimum: 1) end |
.white_space(mode) ⇒ Object
Normalize a string-derived value's whitespace at cast time
(xs:whiteSpace). Unlike every other facet this transforms the
stored value rather than validating it, so it is applied in
String.cast, not the lazy validator. Only string-derived types
carry lexical text, so declaring it elsewhere fails fast.
175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/lutaml/model/type/value.rb', line 175 def white_space(mode) unless self <= Type::String raise ArgumentError, "`white_space` is only allowed for string-derived types" end unless WHITE_SPACE_MODES.include?(mode) raise ArgumentError, "`white_space` must be one of " \ "#{WHITE_SPACE_MODES.map(&:inspect).join(', ')}" end declare_facets(white_space: mode) end |
Instance Method Details
#initialized? ⇒ Boolean
310 311 312 |
# File 'lib/lutaml/model/type/value.rb', line 310 def initialized? true end |
#to_s ⇒ Object
Instance methods for serialization
329 330 331 |
# File 'lib/lutaml/model/type/value.rb', line 329 def to_s value.to_s end |