require 'stringio' require 'protocol_buffers/runtime/field' require 'protocol_buffers/runtime/encoder' require 'protocol_buffers/runtime/decoder' module ProtocolBuffers # = 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: # # http://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 Message # Create a new Message of this class. # # message = MyMessageClass.new(attributes) # # is equivalent to # message = MyMessageClass.new # message.attributes = attributes def initialize(attributes = {}) @set_fields = self.class.initial_set_fields.dup self.attributes = attributes end # Serialize this Message to the given IO stream using the Protocol Buffer # wire format. # # Equivalent to, but more efficient than # # io << message # # Returns +io+ def serialize(io) Encoder.encode(io, self) io end # Serialize this Message to a String and return it. def serialize_to_string sio = ProtocolBuffers.bin_sio serialize(sio) return sio.string end alias_method :to_s, :serialize_to_string # 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) 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 # Shortcut, simply calls self.new.parse(io) def self.parse(io) self.new.parse(io) end # 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. 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 # Parse the string into a new Message of this class, and merge it into the # current message like +merge_from+. def merge_from_string(string) merge_from(self.class.new.parse(string)) end # Assign values to attributes in bulk. # # message.attributes = { :field1 => value1, :field2 => value2 } -> message def attributes=(hash = {}) hash.each do |name, value| self.send("#{name}=", value) end self end # Comparison by class and field values. def ==(obj) return false unless obj.is_a?(self.class) fields.each do |tag, field| return false unless self.__send__(field.name) == obj.__send__(field.name) end return true end # Reset all fields to the default value. def clear! fields.each { |tag, field| self.__send__("#{field.name}=", nil) } end # This is a shallow copy. 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 # Returns a hash of { tag => ProtocolBuffers::Field } def self.fields @fields || @fields = {} end def self.initial_set_fields @set_fields ||= [] end # Returns a hash of { tag => ProtocolBuffers::Field } def fields self.class.fields end # Find the field for the given attribute name. Returns a # ProtocolBuffers::field def self.field_for_name(name) name = name.to_sym field = fields.find { |tag,field| field.name == name } field && field.last end # Equivalent to fields[tag] def self.field_for_tag(tag) fields[tag] end # 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 def value_for_tag(tag) self.__send__(fields[tag].name) end def set_value_for_tag(tag, value) self.__send__("#{fields[tag].name}=", value) end # 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? def value_for_tag?(tag) @set_fields[tag] || false end def inspect ret = ProtocolBuffers.bin_sio ret << "#<#{self.class.name}" fields.each do |tag, field| ret << " #{field.name}=#{field.inspect_value(self.__send__(field.name))}" end ret << ">" return ret.string end 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 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 def self.required(type, name, tag, opts = {}) # :NODOC: define_field(:required, type, name, tag, opts) @has_required_field = true end def self.optional(type, name, tag, opts = {}) # :NODOC: define_field(:optional, type, name, tag, opts) end def self.repeated(type, name, tag, opts = {}) # :NODOC: define_field(:repeated, type, name, tag, opts) end def notify_on_change(parent, tag) @parent_for_notify = parent @tag_for_notify = tag end 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 def valid? self.class.valid?(self) end 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 def validate! self.class.validate!(self) end def self.validate!(message) valid?(message, true) end def remember_unknown_field(tag_int, value) @unknown_fields || @unknown_fields = [] @unknown_fields << [tag_int, value] end # yields |tag_int, value| pairs def each_unknown_field # :nodoc: return unless @unknown_fields @unknown_fields.each { |tag_int, value| yield tag_int, value } end def unknown_field_count (@unknown_fields || []).size end # left in for compatibility with previously created .pb.rb files -- no longer used def self.gen_methods! # :NODOC: @methods_generated = true end protected def initialize_field(tag) field = fields[tag] new_value = field.default_value self.instance_variable_set("@#{field.name}", new_value) if field.class == Field::MessageField new_value.notify_on_change(self, tag) end @set_fields[tag] = false end end end