Module: Mxrb::IO::BsonCodec
- Defined in:
- lib/mxrb/io/bson_codec.rb
Overview
BSON codec with Mendix-specific conventions:
- UUIDs stored as 16-byte blobs with partial little-endian byte-swap (MS GUID format)
- Arrays have a 4-byte int32 marker as first element (1=by-name, 2=part-secondary, 3=part-primary)
- $ID can appear as String, BSON Binary, or {Data:, Subtype:} map
- $Type uses storage names ("DomainModels$Entity") not SDK qualified names
Constant Summary collapse
- UUID_PATTERN =
/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i- BINARY_UUID_KEYS =
%w[ $ID ChildPointer CloseButtonPointer DataStorageGuid DefaultButtonPointer DefaultPagePointer DestinationPointer GUID OriginPointer ParentPointer StableId TypeParameterPointer TypePointer ].freeze
- CONTAINMENT_TO_TYPE =
Map ContainmentName → expected $Type prefix for quick type detection.
{ "Modules" => "Projects$Module", "DomainModel" => "DomainModels$DomainModel", "Documents" => nil, # could be Page, Microflow, etc — read $Type "Folders" => "Projects$Folder", "ModuleSecurity" => "Security$ModuleSecurity", "Settings" => "Settings$ProjectSettings", "Security" => "Security$ProjectSecurity", "NavigationDocuments" => "Navigation$NavigationDocument", "SystemTexts" => "Texts$SystemTextCollection", }.freeze
Class Method Summary collapse
-
.blob_to_uuid(blob) ⇒ Object
Convert a 16-byte MS-GUID blob to UUID string.
-
.build_array(items, marker: 3) ⇒ Object
Build a Mendix-style array (prepend marker).
-
.contents_hash(bson_bytes) ⇒ Object
Compute the Mendix ContentsHash: Base64(SHA256(bson_bytes)).
-
.extract_id(value) ⇒ Object
Extract the Mendix $ID value from any of its three representations: 1.
-
.parse(bytes) ⇒ Object
Parse raw BSON bytes → Ruby Hash (with string keys).
-
.parse_array(raw) ⇒ Object
Mendix arrays have an int32 marker as first element.
-
.serialize(doc) ⇒ Object
Serialize a Ruby Hash → BSON bytes.
-
.uuid_to_blob(uuid) ⇒ Object
Convert a UUID string to a 16-byte MS-GUID blob.
Class Method Details
.blob_to_uuid(blob) ⇒ Object
Convert a 16-byte MS-GUID blob to UUID string. MS-GUID layout: bytes 0-3 little-endian, 4-5 little-endian, 6-7 little-endian, bytes 8-15 big-endian.
20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/mxrb/io/bson_codec.rb', line 20 def self.blob_to_uuid(blob) return nil unless blob.is_a?(String) && blob.bytesize == 16 b = blob.bytes p1 = b[0..3].reverse.map { "%02x" % _1 }.join p2 = b[4..5].reverse.map { "%02x" % _1 }.join p3 = b[6..7].reverse.map { "%02x" % _1 }.join p4 = b[8..9].map { "%02x" % _1 }.join p5 = b[10..15].map { "%02x" % _1 }.join "#{p1}-#{p2}-#{p3}-#{p4}-#{p5}" end |
.build_array(items, marker: 3) ⇒ Object
Build a Mendix-style array (prepend marker).
72 73 74 |
# File 'lib/mxrb/io/bson_codec.rb', line 72 def self.build_array(items, marker: 3) [marker] + Array(items) end |
.contents_hash(bson_bytes) ⇒ Object
Compute the Mendix ContentsHash: Base64(SHA256(bson_bytes)).
125 126 127 |
# File 'lib/mxrb/io/bson_codec.rb', line 125 def self.contents_hash(bson_bytes) Base64.strict_encode64(Digest::SHA256.digest(bson_bytes.b)) end |
.extract_id(value) ⇒ Object
Extract the Mendix $ID value from any of its three representations:
1. String UUID: "c67c5271-da7d-45f1-81df-ceb6946b8abe"
2. BSON Binary: <16-byte blob> → converted to UUID string
3. Map: { "Data" => "base64...", "Subtype" => 3 } → decoded + converted
45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/mxrb/io/bson_codec.rb', line 45 def self.extract_id(value) case value when String value # already a UUID string when BSON::Binary blob_to_uuid(value.data) when Hash data = value["Data"] || value[:Data] return nil unless data blob_to_uuid(Base64.strict_decode64(data.to_s)) end end |
.parse(bytes) ⇒ Object
Parse raw BSON bytes → Ruby Hash (with string keys).
79 80 81 82 83 84 85 86 |
# File 'lib/mxrb/io/bson_codec.rb', line 79 def self.parse(bytes) return {} if bytes.nil? || bytes.empty? buf = BSON::ByteBuffer.new(bytes.b) BSON::Document.from_bson(buf).to_h rescue => e raise SerializationError, "BSON parse error: #{e.}" end |
.parse_array(raw) ⇒ Object
Mendix arrays have an int32 marker as first element. Returns { marker: Integer, items: Array } Marker values: 1=by-name refs, 2=part-secondary, 3=part-primary
63 64 65 66 67 68 69 |
# File 'lib/mxrb/io/bson_codec.rb', line 63 def self.parse_array(raw) return { marker: 3, items: [] } if raw.nil? || raw.empty? marker = raw.first.is_a?(Integer) ? raw.first : 3 items = raw.first.is_a?(Integer) ? raw[1..] : raw { marker: marker, items: Array(items) } end |
.serialize(doc) ⇒ Object
Serialize a Ruby Hash → BSON bytes.
89 90 91 92 93 94 95 96 |
# File 'lib/mxrb/io/bson_codec.rb', line 89 def self.serialize(doc) bson_doc = BSON::Document.new(storage_value(doc)) buf = BSON::ByteBuffer.new bson_doc.to_bson(buf) buf.get_bytes(buf.length) rescue => e raise SerializationError, "BSON serialize error: #{e.}" end |
.uuid_to_blob(uuid) ⇒ Object
Convert a UUID string to a 16-byte MS-GUID blob.
33 34 35 36 37 |
# File 'lib/mxrb/io/bson_codec.rb', line 33 def self.uuid_to_blob(uuid) hex = uuid.delete("-") parts = hex.scan(/../).map { _1.to_i(16).chr(Encoding::BINARY) } (parts[0..3].reverse + parts[4..5].reverse + parts[6..7].reverse + parts[8..15]).join end |