Class: ProtocolBuffers::Message

Inherits:
Object
  • Object
show all
Defined in:
lib/protocol_buffers/runtime/message.rb

Overview

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.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = {}) ⇒ Message

Create a new Message of this class.

message = MyMessageClass.new(attributes)
# is equivalent to
message = MyMessageClass.new
message.attributes = attributes


234
235
236
237
# File 'lib/protocol_buffers/runtime/message.rb', line 234

def initialize(attributes = {})
  @set_fields = self.class.initial_set_fields.dup
  self.attributes = attributes
end

Class Method Details

.define_field(otype, type, name, tag, opts = {}) ⇒ Object

:NODOC:



457
458
459
460
461
462
463
464
465
# File 'lib/protocol_buffers/runtime/message.rb', line 457

def self.define_field(otype, type, name, tag, opts = {}) # :NODOC:
  type = type.is_a?(Module) ? type : type.to_sym
  name = name.to_sym
  tag  = tag.to_i
  raise("Field already exists for tag: #{tag}") if fields[tag]
  field = Field.create(self, otype, type, name, tag, opts)
  fields[tag] = field
  field.add_methods_to(self)
end

.field_for_name(name) ⇒ Object

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



396
397
398
399
400
# File 'lib/protocol_buffers/runtime/message.rb', line 396

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) ⇒ Object

Equivalent to fields



403
404
405
# File 'lib/protocol_buffers/runtime/message.rb', line 403

def self.field_for_tag(tag)
  fields[tag]
end

.fieldsObject

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



381
382
383
# File 'lib/protocol_buffers/runtime/message.rb', line 381

def self.fields
  @fields || @fields = {}
end

.fully_qualified_nameObject



497
498
499
# File 'lib/protocol_buffers/runtime/message.rb', line 497

def self.fully_qualified_name
  @fully_qualified_name
end

.gen_methods!Object

left in for compatibility with previously created .pb.rb files – no longer used



546
547
548
# File 'lib/protocol_buffers/runtime/message.rb', line 546

def self.gen_methods! # :NODOC:
  @methods_generated = true
end

.initial_set_fieldsObject



385
386
387
# File 'lib/protocol_buffers/runtime/message.rb', line 385

def self.initial_set_fields
  @set_fields ||= []
end

.optional(type, name, tag, opts = {}) ⇒ Object

:NODOC:



472
473
474
# File 'lib/protocol_buffers/runtime/message.rb', line 472

def self.optional(type, name, tag, opts = {}) # :NODOC:
  define_field(:optional, type, name, tag, opts)
end

.parse(io) ⇒ Object

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



295
296
297
# File 'lib/protocol_buffers/runtime/message.rb', line 295

def self.parse(io)
  self.new.parse(io)
end

.repeated(type, name, tag, opts = {}) ⇒ Object

:NODOC:



476
477
478
# File 'lib/protocol_buffers/runtime/message.rb', line 476

def self.repeated(type, name, tag, opts = {}) # :NODOC:
  define_field(:repeated, type, name, tag, opts)
end

.required(type, name, tag, opts = {}) ⇒ Object

:NODOC:



467
468
469
470
# File 'lib/protocol_buffers/runtime/message.rb', line 467

def self.required(type, name, tag, opts = {}) # :NODOC:
  define_field(:required, type, name, tag, opts)
  @has_required_field = true
end

.set_fully_qualified_name(name) ⇒ Object



493
494
495
# File 'lib/protocol_buffers/runtime/message.rb', line 493

def self.set_fully_qualified_name(name)
  @fully_qualified_name = name.dup.freeze
end

.to_hash(message) ⇒ Object



264
265
266
267
268
269
270
271
272
273
274
# File 'lib/protocol_buffers/runtime/message.rb', line 264

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) ⇒ Boolean

Returns:

  • (Boolean)


509
510
511
512
513
514
515
516
517
518
519
520
# File 'lib/protocol_buffers/runtime/message.rb', line 509

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) ⇒ Object



526
527
528
# File 'lib/protocol_buffers/runtime/message.rb', line 526

def self.validate!(message)
  valid?(message, true)
end

Instance Method Details

#==(obj) ⇒ Object

Comparison by class and field values.



330
331
332
333
334
335
336
337
338
339
340
# File 'lib/protocol_buffers/runtime/message.rb', line 330

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 = {}) ⇒ Object

Assign values to attributes in bulk.

message.attributes = { :field1 => value1, :field2 => value2 } -> message


322
323
324
325
326
327
# File 'lib/protocol_buffers/runtime/message.rb', line 322

def attributes=(hash = {})
  hash.each do |name, value|
    self.send("#{name}=", value)
  end
  self
end

#clear!Object

Reset all fields to the default value.



366
367
368
# File 'lib/protocol_buffers/runtime/message.rb', line 366

def clear!
  fields.each { |tag, field| self.__send__("#{field.name}=", nil) }
end

#default_changed(tag) ⇒ Object



485
486
487
488
489
490
491
# File 'lib/protocol_buffers/runtime/message.rb', line 485

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

#dupObject

This is a shallow copy.



371
372
373
374
375
376
377
378
# File 'lib/protocol_buffers/runtime/message.rb', line 371

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

#each_unknown_fieldObject

yields |tag_int, value| pairs



536
537
538
539
# File 'lib/protocol_buffers/runtime/message.rb', line 536

def each_unknown_field # :nodoc:
  return unless @unknown_fields
  @unknown_fields.each { |tag_int, value| yield tag_int, value }
end

#eql?(obj) ⇒ Boolean

Comparison by class and field values.

Returns:

  • (Boolean)


343
344
345
346
347
348
349
350
351
352
353
# File 'lib/protocol_buffers/runtime/message.rb', line 343

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

#fieldsObject

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



390
391
392
# File 'lib/protocol_buffers/runtime/message.rb', line 390

def fields
  self.class.fields
end

#fully_qualified_nameObject



501
502
503
# File 'lib/protocol_buffers/runtime/message.rb', line 501

def fully_qualified_name
  self.class.fully_qualified_name
end

#hashObject



355
356
357
358
359
360
361
362
363
# File 'lib/protocol_buffers/runtime/message.rb', line 355

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

#inspectObject



429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/protocol_buffers/runtime/message.rb', line 429

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_field(tag, value, field = ) ⇒ Object

:nodoc:



444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/protocol_buffers/runtime/message.rb', line 444

def merge_field(tag, value, field = fields[tag]) # :nodoc:
  if field.repeated?
    if value.is_a?(Array)
      self.__send__("#{field.name}=", self.__send__(field.name) + value)
    else
      self.__send__(field.name) << value
    end
  else
    self.__send__("#{field.name}=", value)
    @set_fields[tag] = true
  end
end

#merge_from(obj) ⇒ Object

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.

Raises:

  • (ArgumentError)


304
305
306
307
308
309
310
311
# File 'lib/protocol_buffers/runtime/message.rb', line 304

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) ⇒ Object

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



315
316
317
# File 'lib/protocol_buffers/runtime/message.rb', line 315

def merge_from_string(string)
  merge_from(self.class.new.parse(string))
end

#notify_on_change(parent, tag) ⇒ Object



480
481
482
483
# File 'lib/protocol_buffers/runtime/message.rb', line 480

def notify_on_change(parent, tag)
  @parent_for_notify = parent
  @tag_for_notify = tag
end

#parse(io_or_string) ⇒ Object

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)


285
286
287
288
289
290
291
292
# File 'lib/protocol_buffers/runtime/message.rb', line 285

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

#remember_unknown_field(tag_int, value) ⇒ Object



530
531
532
533
# File 'lib/protocol_buffers/runtime/message.rb', line 530

def remember_unknown_field(tag_int, value)
  @unknown_fields || @unknown_fields = []
  @unknown_fields << [tag_int, value]
end

#serialize(io) ⇒ Object

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

Equivalent to, but more efficient than

io << message

Returns io



247
248
249
250
# File 'lib/protocol_buffers/runtime/message.rb', line 247

def serialize(io)
  Encoder.encode(io, self)
  io
end

#serialize_to_stringObject Also known as: to_s

Serialize this Message to a String and return it.



253
254
255
256
257
# File 'lib/protocol_buffers/runtime/message.rb', line 253

def serialize_to_string
  sio = ProtocolBuffers.bin_sio
  serialize(sio)
  return sio.string
end

#set_value_for_tag(tag, value) ⇒ Object



416
417
418
# File 'lib/protocol_buffers/runtime/message.rb', line 416

def set_value_for_tag(tag, value)
  self.__send__("#{fields[tag].name}=", value)
end

#to_hashObject



260
261
262
# File 'lib/protocol_buffers/runtime/message.rb', line 260

def to_hash
  self.class.to_hash(self)
end

#unknown_field_countObject



541
542
543
# File 'lib/protocol_buffers/runtime/message.rb', line 541

def unknown_field_count
  (@unknown_fields || []).size
end

#valid?Boolean

Returns:

  • (Boolean)


505
506
507
# File 'lib/protocol_buffers/runtime/message.rb', line 505

def valid?
  self.class.valid?(self)
end

#validate!Object



522
523
524
# File 'lib/protocol_buffers/runtime/message.rb', line 522

def validate!
  self.class.validate!(self)
end

#value_for_tag(tag) ⇒ Object

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


412
413
414
# File 'lib/protocol_buffers/runtime/message.rb', line 412

def value_for_tag(tag)
  self.__send__(fields[tag].name)
end

#value_for_tag?(tag) ⇒ Boolean

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?

Returns:

  • (Boolean)


425
426
427
# File 'lib/protocol_buffers/runtime/message.rb', line 425

def value_for_tag?(tag)
  @set_fields[tag] || false
end