Ruby Types
LUMOS generates Ruby classes with borsh-rb for Borsh serialization.
Installation
Section titled “Installation”gem install borsh-rbType Mapping Reference
Section titled “Type Mapping Reference”| LUMOS Type | Ruby Type | Borsh Schema |
|---|---|---|
u8 | Integer | :u8 |
u16 | Integer | :u16 |
u32 | Integer | :u32 |
u64 | Integer | :u64 |
u128 | Integer | :u128 |
i8 | Integer | :i8 |
i16 | Integer | :i16 |
i32 | Integer | :i32 |
i64 | Integer | :i64 |
i128 | Integer | :i128 |
bool | TrueClass/FalseClass | :bool |
String | String | :string |
PublicKey | Array<Integer> | [:u8, 32] |
Signature | Array<Integer> | [:u8, 64] |
Vec<T> | Array | [:array, T] |
Option<T> | T / nil | [:option, T] |
[T; N] | Array | [:u8, N] |
Generated Code Example
Section titled “Generated Code Example”LUMOS Schema
Section titled “LUMOS Schema”#[solana]struct PlayerAccount { wallet: PublicKey, level: u16, experience: u64, username: String, guild: Option<PublicKey>, inventory: [u8],}Generated Ruby
Section titled “Generated Ruby”# Code generated by LUMOS - DO NOT EDITrequire 'borsh'
# PlayerAccount represents the player account data# @attr wallet [Array<Integer>] The wallet public key (32 bytes)# @attr level [Integer] The player level# @attr experience [Integer] Total experience points# @attr username [String] Player username# @attr guild [Array<Integer>, nil] Optional guild public key# @attr inventory [Array<Integer>] Player inventory itemsclass PlayerAccount attr_accessor :wallet, :level, :experience, :username, :guild, :inventory
# Borsh serialization schema SCHEMA = { wallet: [:u8, 32], level: :u16, experience: :u64, username: :string, guild: [:option, [:u8, 32]], inventory: [:array, :u8] }
# Initialize with optional parameters # @param opts [Hash] initialization options def initialize(opts = {}) @wallet = opts[:wallet] || Array.new(32, 0) @level = opts[:level] || 0 @experience = opts[:experience] || 0 @username = opts[:username] || "" @guild = opts[:guild] @inventory = opts[:inventory] || [] end
# Convert to hash representation # @return [Hash] hash representation def to_h { wallet: @wallet, level: @level, experience: @experience, username: @username, guild: @guild, inventory: @inventory } endendUsage Examples
Section titled “Usage Examples”Serialization
Section titled “Serialization”require_relative 'player_schema'
# Create an accountaccount = PlayerAccount.new( wallet: [0x11] * 32, # 32-byte public key level: 42, experience: 150_000, username: "CryptoGamer", guild: nil, # No guild inventory: [1, 2, 3, 4, 5])
# Serialize to bytesserialized = Borsh.serialize(PlayerAccount::SCHEMA, account.to_h)puts "Serialized: #{serialized.unpack1('H*')}"Deserialization
Section titled “Deserialization”# Deserialize from bytesdata = [0x11, 0x22, ...].pack('C*') # Borsh-encoded dataparsed = Borsh.deserialize(PlayerAccount::SCHEMA, data)
account = PlayerAccount.new(parsed)puts "Level: #{account.level}"puts "XP: #{account.experience}"Enum Support
Section titled “Enum Support”Unit Enums (Module Constants)
Section titled “Unit Enums (Module Constants)”# GameStatus enummodule GameStatus ACTIVE = 0 PAUSED = 1 ENDED = 2
def self.from_value(val) case val when 0 then :active when 1 then :paused when 2 then :ended else raise "Unknown GameStatus: #{val}" end end
def self.to_value(sym) case sym when :active then 0 when :paused then 1 when :ended then 2 else raise "Unknown GameStatus: #{sym}" end endendComplex Enums (Struct-based)
Section titled “Complex Enums (Struct-based)”# Transfer variantTransfer = Struct.new(:recipient, :amount, keyword_init: true) do DISCRIMINANT = 0 SCHEMA = { recipient: [:u8, 32], amount: :u64 }end
# Stake variantStake = Struct.new(:validator, :amount, keyword_init: true) do DISCRIMINANT = 1 SCHEMA = { validator: [:u8, 32], amount: :u64 }end
# Instruction enum helpermodule Instruction def self.deserialize(data) discriminant = data[0] payload = data[1..]
case discriminant when 0 parsed = Borsh.deserialize(Transfer::SCHEMA, payload) Transfer.new(**parsed) when 1 parsed = Borsh.deserialize(Stake::SCHEMA, payload) Stake.new(**parsed) else raise "Unknown instruction: #{discriminant}" end endendBest Practices
Section titled “Best Practices”1. PublicKey Handling
Section titled “1. PublicKey Handling”require 'base58'
class PlayerAccount # Convert wallet to base58 string # @return [String] base58-encoded public key def wallet_base58 Base58.binary_to_base58(wallet.pack('C*'), :bitcoin) end
# Set wallet from base58 string # @param str [String] base58-encoded public key def wallet_base58=(str) @wallet = Base58.base58_to_binary(str, :bitcoin).bytes endend2. Large Integer Safety
Section titled “2. Large Integer Safety”Ruby’s Integer handles arbitrary precision natively:
# Safe in Rubymax_u64 = 18_446_744_073_709_551_615max_u128 = 340_282_366_920_938_463_463_374_607_431_768_211_455
# Both work correctlyaccount.balance = max_u64account.total_supply = max_u1283. Optional Fields
Section titled “3. Optional Fields”# Safe option handlingif account.guild puts "Guild: #{Base58.binary_to_base58(account.guild.pack('C*'), :bitcoin)}"else puts "No guild"end
# Or using Ruby's safe navigationguild_base58 = account.guild&.then { |g| Base58.binary_to_base58(g.pack('C*'), :bitcoin) }4. Validation
Section titled “4. Validation”class PlayerAccount # Validate account data # @return [Boolean] true if valid def valid? wallet.length == 32 && level >= 0 && level <= 65535 && !username.empty? && (guild.nil? || guild.length == 32) end
# Validate and raise on error def validate! raise "Invalid wallet length" unless wallet.length == 32 raise "Invalid level" unless level >= 0 && level <= 65535 raise "Username cannot be empty" if username.empty? raise "Invalid guild length" if guild && guild.length != 32 true endendIntegration with Solana
Section titled “Integration with Solana”Using solana-ruby (community gem)
Section titled “Using solana-ruby (community gem)”require 'solana_ruby'
client = SolanaRuby::Client.new("https://api.devnet.solana.com")
def get_account_data(client, pubkey) response = client.get_account_info(pubkey) return nil unless response && response['value']
data = Base64.decode64(response['value']['data'][0]) # Skip 8-byte Anchor discriminator account_data = data[8..]
parsed = Borsh.deserialize(PlayerAccount::SCHEMA, account_data) PlayerAccount.new(parsed)endYARD Documentation
Section titled “YARD Documentation”LUMOS generates YARD-compatible documentation:
# Generate documentation# yard doc player_schema.rb
# View in browser# yard serverGenerated YARD tags:
@attrfor each field@returnfor methods@paramfor method parameters
See Also
Section titled “See Also”- Type System - Full type reference
- Python Types - Python type mappings
- Go Types - Go type mappings