class ObjectFramework::Protobuf::Message
Immutable message descriptor encoding and decoding ordinary value hashes. Declare fields in the construction block.
message = ObjectFramework::Protobuf::Message.new("Greeting") do field :text, 1, :string end bytes = message.encode(text: "Hello") message.decode(bytes) # => { text: "Hello" }
Attributes
Public Class Methods
Source
# File lib/object_framework/protobuf.rb, line 93 def initialize(name, &declaration) @name, @fields = Protobuf.name(name), [] instance_eval(&declaration) if declaration @numbers = fields.to_h { |field| [field.number, field] }.freeze @fields.freeze freeze end
Public Instance Methods
Source
# File lib/object_framework/protobuf.rb, line 144 def decode(bytes, depth: 0, initial: nil) raise InvalidInput, "Protobuf nesting limit exceeded" if depth >= 64 cursor = Wire::Cursor.new(bytes) result = initial ? Values.mutable_copy(initial) : fields.to_h { |field| [field.name, default_value(field)] } until cursor.done? tag = cursor.varint number, wire = tag >> 3, tag & 7 raise InvalidInput, "Invalid protobuf field tag" unless number.between?(1, 2**29 - 1) field = @numbers[number] if field && field.repeated && packable?(field) && wire == 2 packed = Wire::Cursor.new(cursor.take(cursor.varint)) result[field.name] << decode_value(field.type, packed, depth:) until packed.done? elsif field && wire == wire_type(field.type) value = decode_value(field.type, cursor, depth:, initial: field.type.is_a?(Message) && !field.repeated ? result[field.name] : nil) field.repeated ? result[field.name] << value : result[field.name] = value else cursor.skip(wire) end end Values.detach(result) end
Decodes binary proto3 into a Hash, supplying defaults for absent fields and skipping unknown fields. Malformed input raises InvalidInput.
Source
# File lib/object_framework/protobuf.rb, line 170 def definition "message #{name} {\n" + fields.map do |field| type = field.type.respond_to?(:name) ? field.type.name : field.type.to_s option = field.repeated && packable?(field) && !field.packed ? " [packed = false]" : "" " #{field.repeated ? 'repeated ' : ''}#{type} #{field.name} = #{field.number}#{option};\n" end.join + "}\n" end
Source
# File lib/object_framework/protobuf.rb, line 166 def dependencies fields.map(&:type).select { |type| type.is_a?(Message) || type.is_a?(Enum) } end
Source
# File lib/object_framework/protobuf.rb, line 117 def encode(values = nil, depth: 0, **attributes) raise InvalidInput, "Protobuf nesting limit exceeded" if depth >= 64 raise InvalidInput, "Pass message values once" if values && !attributes.empty? values ||= attributes values = values.to_h if values.is_a?(Data) raise InvalidInput, "Expected protobuf message values" unless values.is_a?(Hash) values = values.transform_keys { |key| key.to_s.to_sym } raise InvalidInput, "Unknown protobuf field" unless (values.keys - fields.map(&:name)).empty? fields.each_with_object(+"".b) do |field, output| next unless values.key?(field.name) value = values[field.name] if field.repeated raise InvalidInput, "Repeated fields require arrays" unless value.is_a?(Array) if packable?(field) && field.packed && !value.empty? payload = value.map { |item| encode_value(field.type, item, depth:) }.join.b output << Wire.varint((field.number << 3) | 2) << Wire.varint(payload.bytesize) << payload else value.each { |item| output << encode_field(field, item, depth:) } end elsif !default?(field.type, value) output << encode_field(field, value, depth:) end end end
Encodes a Hash or Data value, or keyword attributes, as binary proto3. Raises InvalidInput for unknown fields, invalid values, or excessive nesting. Repeated fields require arrays.
Source
# File lib/object_framework/protobuf.rb, line 104 def field(name, number, type, repeated: false, packed: true) name = Protobuf.name(name).to_sym unless number.is_a?(Integer) && number.between?(1, 2**29 - 1) && !number.between?(19_000, 19_999) raise ConfigurationError, "Invalid protobuf field number" end raise ConfigurationError, "Duplicate protobuf field" if fields.any? { |field| field.name == name || field.number == number } raise ConfigurationError, "Unknown protobuf type" unless SCALARS.key?(type) || type.is_a?(Message) || type.is_a?(Enum) @fields << Field.new(name:, number:, type:, repeated:, packed:) end