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.
304 305 306 |
# File 'lib/lutaml/model/type/value.rb', line 304 def initialize(value) @value = self.class.cast(value) end |
Instance Attribute Details
#value ⇒ Object (readonly)
Returns the value of attribute value.
302 303 304 |
# File 'lib/lutaml/model/type/value.rb', line 302 def value @value end |
Class Method Details
.cast(value, _options = {}) ⇒ Object
312 313 314 315 316 317 |
# File 'lib/lutaml/model/type/value.rb', line 312 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).
156 157 158 |
# File 'lib/lutaml/model/type/value.rb', line 156 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.
136 137 138 139 140 141 |
# File 'lib/lutaml/model/type/value.rb', line 136 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.
101 102 103 104 105 106 107 |
# File 'lib/lutaml/model/type/value.rb', line 101 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 |
# File 'lib/lutaml/model/type/value.rb', line 69 def format_type_serializer_for(format, type_class) # Hot path: memoize per [format, resolved class] — the hierarchy # walk below only runs on a miss. Format serializers register at # load time, so a hit is stable. cache = SERIALIZER_LOOKUP_CACHE key = [format, type_class] return cache[key] if cache.key?(key) 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 cache[key] = 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).
197 198 199 |
# File 'lib/lutaml/model/type/value.rb', line 197 def fraction_digits(count) declare_digit_facet(:fraction_digits, count, minimum: 0) end |
.from_format(value, format) ⇒ Object
Class-level format conversion
332 333 334 |
# File 'lib/lutaml/model/type/value.rb', line 332 def self.from_format(value, format) new(send(:"from_#{format}", value)) end |
.inclusive(min: nil, max: nil) ⇒ Object
Constrain ordered values via min/maxInclusive.
128 129 130 131 132 133 |
# File 'lib/lutaml/model/type/value.rb', line 128 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.
94 95 96 97 |
# File 'lib/lutaml/model/type/value.rb', line 94 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:.
144 145 146 147 148 149 150 151 |
# File 'lib/lutaml/model/type/value.rb', line 144 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.
163 164 165 166 |
# File 'lib/lutaml/model/type/value.rb', line 163 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.
339 340 341 342 343 344 345 346 347 348 349 |
# File 'lib/lutaml/model/type/value.rb', line 339 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
319 320 321 322 323 324 |
# File 'lib/lutaml/model/type/value.rb', line 319 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.
113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/lutaml/model/type/value.rb', line 113 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.
191 192 193 |
# File 'lib/lutaml/model/type/value.rb', line 191 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.
173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/lutaml/model/type/value.rb', line 173 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
308 309 310 |
# File 'lib/lutaml/model/type/value.rb', line 308 def initialized? true end |
#to_s ⇒ Object
Instance methods for serialization
327 328 329 |
# File 'lib/lutaml/model/type/value.rb', line 327 def to_s value.to_s end |