class ProtocolBuffers::Message

Generated Code

This text describes exactly what Ruby code the protocol buffer compiler generates for any given protocol definition. You should read the language guide before reading this document:

code.google.com/apis/protocolbuffers/docs/proto.html

Packages

If a package name is given in the .proto file, all top-level messages and enums in the file will be defined underneath a module with the same name as the package. The first letter of the package is capitalized if necessary. This applies to message and enum names as well, since Ruby classes and modules must be capitalized.

For example, the following .proto file:

package wootcakes;
message uberWoot { }

Will define a module Wootcakes and a class Wootcakes::UberWoot

Messages

Given a simple message definition:

message Foo {}

The compiler will generate a class called Foo, which subclasses ProtocolBuffers::Message.

These generated classes are not designed for subclassing.

Ruby message classes have no particular public methods or accessors other than those defined by ProtocolBuffers::Message and those generated for nested fields, messages, and enum types (see below).

A message can be declared inside another message. For example: message Foo { message Bar { } }

In this case, the Bar class is declared inside the Foo class, so you can access it as Foo::Bar (or if in package Baz, Baz::Foo::Bar)

Fields

For each field in the message type, the corresponding class has a member with the same name as the field. How you can manipulate the member depends on its type.

Singular Fields

If you have a singular (optional or required) field foo of any non-message type, you can manipulate the field foo as if it were a regular object attribute. For example, if foo‘s type is int32, you can say:

message.foo = 123
puts message.foo

Note that setting foo to a value of the wrong type will raise a TypeError. Setting foo to a value of the right type, but one that doesn’t fit (such as assigning an out-of-bounds enum value) will raise an ArgumentError.

If foo is read when it is not set, its value is the default value for that field. To check if foo is set, call has_foo? To clear foo, call message.foo = nil. For example:

assert(!message.has_foo?)
message.foo = 123
assert(message.has_foo?)
message.foo = nil
assert(!message.has_foo?)

Singular String Fields

String fields are treated like other singular fields, but note that the default value for string fields is frozen, so it is effectively an immutable string. Attempting to modify this default string will raise a TypeError, so assign a new string to the field instead.

Singular Message Fields

Message types are a bit special, since they are mutable. Accessing an unset message field will return a default instance of the message type. Say you have the following .proto definition:

message Foo {
  optional Bar bar = 1;
}
message Bar {
  optional int32 i = 1;
}

To set the message field, you can do either of the following:

foo = Foo.new
assert(!foo.has_bar?)
foo.bar = Bar.new
assert(foo.has_bar?)

Or, to set bar, you can simply assign a value directly to a field within bar, and - presto! - foo has a bar field:

foo = Foo.new
assert(!foo.has_bar?)
foo.bar.i = 1
assert(foo.has_bar?)

Note that simply reading a field inside bar does not set the field:

foo = Foo.new
assert(!foo.has_bar?)
puts foo.bar.i
assert(!foo.has_bar?)

Repeated Fields

Repeated fields are represented as an object that acts like an Array. For example, given this message definition:

message Foo {
  repeated int32 nums = 1;
}

You can do the following:

foo = Foo.new
foo.nums << 15
foo.nums.push(32)
assert(foo.nums.length == 2)
assert(foo.nums[0] == 15)
assert(foo.nums[1] == 32)
foo.nums.each { |i| puts i }
foo.nums[1] = 56
assert(foo.nums[1] == 56)

To clear a repeated field, call the clear method, or assign nil to it like a singular field.

foo = Foo.new
foo.nums << 15
foo.nums.push(32)
assert(foo.nums.length == 2)
foo.nums.clear
assert(foo.nums.length == 0)
foo.nums = nil # equivalent to foo.nums.clear
assert(foo.nums.length == 0)

You can assign to a repeated field using an array, or any other object that responds to each. This will replace the current contents of the repeated field.

foo = Foo.new
foo.nums << 15
foo.nums = [1, 3, 5]
assert(foo.nums.length == 3)
assert(foo.nums.to_a == [1,3,5])

Repeated fields are always set, so foo.has_nums? will always be true. Repeated fields don’t take up any space in a serialized message if they are empty.

Repeated Message Fields

Repeated message fields work like other repeated fields. For example, given this message definition:

message Foo {
  repeated Bar bars = 1;
}
message Bar {
  optional int32 i = 1;
}

You can do the following:

foo = Foo.new
foo.bars << Bar.new(:i => 15)
foo.bars << Bar.new(:i => 32)
assert(foo.bars.length == 2)
assert(foo.bars[0].i == 15)
assert(foo.bars[1].i == 32)
foo.bars.each { |bar| puts bar.i }
foo.bars[1].i = 56
assert(foo.bars[1].i == 56)

Enumerations

Enumerations are defined as a module with an integer constant for each valid value. For example, given:

enum Foo {
  VALUE_A = 1;
  VALUE_B = 5;
  VALUE_C = 1234;
}

The following Ruby code will be generated:

module Foo
  VALUE_A = 1
  VALUE_B = 5
  VALUE_C = 1234
end

An exception will be thrown if an enum field is assigned a value not in the enum. If an unknown enum value is found while parsing a message, this is treated like an unknown tag id. This matches the C++ library behavior.

Extensions

Protocol Buffer extensions are not currently supported in this library.

Services

Protocol Buffer service (RPC) definitions are ignored.

Public Class Methods

field_for_name(name) click to toggle source

Find the field for the given attribute name. Returns a ProtocolBuffers::field

# File lib/protocol_buffers/runtime/message.rb, line 424
def self.field_for_name(name)
  name = name.to_sym
  field = fields.find { |tag,field| field.name == name }
  field && field.last
end
field_for_tag(tag) click to toggle source

Equivalent to fields

# File lib/protocol_buffers/runtime/message.rb, line 431
def self.field_for_tag(tag)
  fields[tag]
end
fields() click to toggle source

Returns a hash of { tag => ProtocolBuffers::Field }

# File lib/protocol_buffers/runtime/message.rb, line 409
def self.fields
  @fields || @fields = {}
end
fully_qualified_name() click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 560
def self.fully_qualified_name
  @fully_qualified_name
end
initial_set_fields() click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 413
def self.initial_set_fields
  @set_fields ||= []
end
new(attributes = {}) click to toggle source

Create a new Message of this class.

message = MyMessageClass.new(attributes)
# is equivalent to
message = MyMessageClass.new
message.attributes = attributes
# File lib/protocol_buffers/runtime/message.rb, line 236
def initialize(attributes = {})
  @set_fields = self.class.initial_set_fields.dup
  self.attributes = attributes
end
parse(io) click to toggle source

Shortcut, simply calls self.new.parse(io)

# File lib/protocol_buffers/runtime/message.rb, line 310
def self.parse(io)
  self.new.parse(io)
end
parse_from_text(text) click to toggle source

Shortcut, simply calls self.new.parse_from_text(text)

# File lib/protocol_buffers/runtime/message.rb, line 323
def self.parse_from_text(text)
  self.new.parse_from_text(text)
end
set_fully_qualified_name(name) click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 556
def self.set_fully_qualified_name(name)
  @fully_qualified_name = name.dup.freeze
end
to_hash(message) click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 279
def self.to_hash(message)
  return nil if message == nil
  return message.is_a?(String) ? message.dup : message unless message.is_a?(::ProtocolBuffers::Message)
  message.fields.select do |tag, field|
    message.value_for_tag?(tag)
  end.inject(Hash.new) do |hash, (tag, field)|
    value = message.value_for_tag(tag)
    hash[field.name] = value.is_a?(::ProtocolBuffers::RepeatedField) ? value.map { |elem| to_hash(elem) } : to_hash(value)
    hash
  end
end
valid?(message, raise_exception=false) click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 572
def self.valid?(message, raise_exception=false)
  return true unless @has_required_field

  fields.each do |tag, field|
    next if field.otype != :required
    next if message.value_for_tag?(tag) && (field.class != Field::MessageField || message.value_for_tag(tag).valid?)
    return false unless raise_exception
    raise(ProtocolBuffers::EncodeError.new(field), "Required field '#{field.name}' is invalid")
  end

  true
end
validate!(message) click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 589
def self.validate!(message)
  valid?(message, true)
end

Public Instance Methods

==(obj) click to toggle source

Comparison by class and field values.

# File lib/protocol_buffers/runtime/message.rb, line 358
def ==(obj)
  return false unless obj.is_a?(self.class)
  fields.each do |tag, _|
    if value_for_tag?(tag)
      return false unless (obj.value_for_tag?(tag) && value_for_tag(tag) == obj.value_for_tag(tag))
    else
      return false if obj.value_for_tag?(tag)
    end
  end
  return true
end
attributes=(hash = {}) click to toggle source

Assign values to attributes in bulk.

message.attributes = { :field1 => value1, :field2 => value2 } -> message
# File lib/protocol_buffers/runtime/message.rb, line 350
def attributes=(hash = {})
  hash.each do |name, value|
    self.send("#{name}=", value)
  end
  self
end
clear!() click to toggle source

Reset all fields to the default value.

# File lib/protocol_buffers/runtime/message.rb, line 394
def clear!
  fields.each { |tag, field| self.__send__("#{field.name}=", nil) }
end
default_changed(tag) click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 548
def default_changed(tag)
  @set_fields[tag] = true
  if @parent_for_notify
    @parent_for_notify.default_changed(@tag_for_notify)
    @parent_for_notify = @tag_for_notify = nil
  end
end
dup() click to toggle source

This is a shallow copy.

# File lib/protocol_buffers/runtime/message.rb, line 399
def dup
  ret = self.class.new
  fields.each do |tag, field|
    val = self.__send__(field.name)
    ret.__send__("#{field.name}=", val)
  end
  return ret
end
eql?(obj) click to toggle source

Comparison by class and field values.

# File lib/protocol_buffers/runtime/message.rb, line 371
def eql?(obj)
  return false unless obj.is_a?(self.class)
  fields.each do |tag, _|
    if value_for_tag?(tag)
      return false unless (obj.value_for_tag?(tag) && value_for_tag(tag).eql?(obj.value_for_tag(tag)))
    else
      return false if obj.value_for_tag?(tag)
    end
  end
  return true
end
fields() click to toggle source

Returns a hash of { tag => ProtocolBuffers::Field }

# File lib/protocol_buffers/runtime/message.rb, line 418
def fields
  self.class.fields
end
fully_qualified_name() click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 564
def fully_qualified_name
  self.class.fully_qualified_name
end
get(*nested_field_names, &b) click to toggle source

Gets the field, returning nil if not set If a block is given, this block is called and it’s return value returned if the value is not set

# File lib/protocol_buffers/runtime/message.rb, line 460
def get(*nested_field_names, &b)
  if nested_field_names.size == 1
    field_name = nested_field_names.first
    field = self.class.field_for_name(field_name)
    raise ArgumentError.new unless field
    unless self.value_for_tag?(field.tag)
      return b ? b.call : nil
    end
    return self.value_for_tag(field.tag)
  end
  last_proto = nested_field_names[0..-2].inject(self) do |sub_proto, ifield_name|
    sub_field = sub_proto.class.field_for_name(ifield_name)
    raise ArgumentError.new unless sub_field
    raise ArgumentError.new unless sub_field.is_a?(ProtocolBuffers::Field::MessageField)
    unless sub_proto.value_for_tag?(sub_field.tag)
      return b ? b.call : nil
    end
    sub_proto.value_for_tag(sub_field.tag)
  end
  last_field_name = nested_field_names.last
  last_field = last_proto.class.field_for_name(last_field_name)
  unless last_proto.value_for_tag?(last_field.tag)
    return b ? b.call : nil
  end
  last_proto.value_for_tag(last_field.tag)
end
get!(*nested_field_names) click to toggle source

Gets the field, throwing ArgumentError if not set

# File lib/protocol_buffers/runtime/message.rb, line 488
def get!(*nested_field_names)
  get(*nested_field_names) { raise ArgumentError.new("#{nested_field_names} is not set") }
end
hash() click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 383
def hash
  hash_code = 0
  fields.each do |tag, _|
    if value_for_tag?(tag)
      hash_code = hash_code ^ value_for_tag(tag).hash
    end
  end
  hash_code
end
inspect() click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 492
def inspect
  ret = ProtocolBuffers.bin_sio
  ret << "#<#{self.class.name}"
  fields.each do |tag, field|
    if value_for_tag?(tag)
      value = field.inspect_value(self.__send__(field.name))
    else
      value = "<unset>"
    end
    ret << " #{field.name}=#{value}"
  end
  ret << ">"
  return ret.string
end
merge_from(obj) click to toggle source

Merge the attribute values from obj into this Message, which must be of the same class.

Singular fields will be overwritten, except for embedded messages which will be merged. Repeated fields will be concatenated.

# File lib/protocol_buffers/runtime/message.rb, line 332
def merge_from(obj)
  raise(ArgumentError, "Incompatible merge types: #{self.class} and #{obj.class}") unless obj.is_a?(self.class)
  for tag, field in self.class.fields
    next unless obj.value_for_tag?(tag)
    value = obj.value_for_tag(tag)
    merge_field(tag, value, field)
  end
end
merge_from_string(string) click to toggle source

Parse the string into a new Message of this class, and merge it into the current message like merge_from.

# File lib/protocol_buffers/runtime/message.rb, line 343
def merge_from_string(string)
  merge_from(self.class.new.parse(string))
end
notify_on_change(parent, tag) click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 543
def notify_on_change(parent, tag)
  @parent_for_notify = parent
  @tag_for_notify = tag
end
parse(io_or_string) click to toggle source

Parse a Message of this class from the given IO/String. Since Protocol Buffers are not length delimited, this will read until the end of the stream.

This does not call clear! beforehand, so this is logically equivalent to

new_message = self.class.new
new_message.parse(io)
merge_from(new_message)
# File lib/protocol_buffers/runtime/message.rb, line 300
def parse(io_or_string)
  io = io_or_string
  if io.is_a?(String)
    io = ProtocolBuffers.bin_sio(io)
  end
  Decoder.decode(io, self)
  return self
end
parse_from_text(text) click to toggle source

Parse the text as a text representation of this class, and merge the parsed fields into the current message.

# File lib/protocol_buffers/runtime/message.rb, line 316
def parse_from_text(text)
  parser = TextParser.new
  parser.parse_text(text, self)
  return self
end
remember_unknown_field(tag_int, value) click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 593
def remember_unknown_field(tag_int, value)
  @unknown_fields || @unknown_fields = []
  @unknown_fields << [tag_int, value]
end
serialize(io) click to toggle source

Serialize this Message to the given IO stream using the Protocol Buffer wire format.

Equivalent to, but more efficient than

io << message

Returns io

# File lib/protocol_buffers/runtime/message.rb, line 249
def serialize(io)
  Encoder.encode(io, self)
  io
end
serialize_to_string() click to toggle source

Serialize this Message to a String and return it.

# File lib/protocol_buffers/runtime/message.rb, line 255
def serialize_to_string
  sio = ProtocolBuffers.bin_sio
  serialize(sio)
  return sio.string
end
Also aliased as: to_s
set_value_for_tag(tag, value) click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 444
def set_value_for_tag(tag, value)
  self.__send__("#{fields[tag].name}=", value)
end
text_format(io, options = nil) click to toggle source

Format this message into the given IO stream using the text format of Protocol Buffers.

# File lib/protocol_buffers/runtime/message.rb, line 263
def text_format(io, options = nil)
  formatter = TextFormatter.new(options)
  formatter.format(io, self)
end
text_format_to_string(options = nil) click to toggle source

Format this message into a text and return it.

# File lib/protocol_buffers/runtime/message.rb, line 269
def text_format_to_string(options = nil)
  sio = ProtocolBuffers.utf8_sio
  text_format(sio, options)
  return sio.string
end
to_hash() click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 275
def to_hash
  self.class.to_hash(self)
end
to_s()
Alias for: serialize_to_string
unknown_field_count() click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 604
def unknown_field_count
  (@unknown_fields || []).size
end
valid?() click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 568
def valid?
  self.class.valid?(self)
end
validate!() click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 585
def validate!
  self.class.validate!(self)
end
value_for_tag(tag) click to toggle source

Reflection: get the attribute value for the given tag id.

message.value_for_tag(message.class.field_for_name(:f1).tag)
# is equivalent to
message.f1
# File lib/protocol_buffers/runtime/message.rb, line 440
def value_for_tag(tag)
  self.__send__(fields[tag].name)
end
value_for_tag?(tag) click to toggle source

Reflection: does this Message have the field set?

message.value_for_tag?(message.class.field_for_name(:f1).tag)
# is equivalent to
message.has_f1?
# File lib/protocol_buffers/runtime/message.rb, line 453
def value_for_tag?(tag)
  @set_fields[tag] || false
end

Protected Instance Methods

initialize_field(tag) click to toggle source
# File lib/protocol_buffers/runtime/message.rb, line 615
def initialize_field(tag)
  field = fields[tag]
  new_value = field.default_value
  self.instance_variable_set("@#{field.name}", new_value)
  if field.kind_of? Field::AggregateField
    new_value.notify_on_change(self, tag)
  end
  @set_fields[tag] = false
end