My favorites | Sign in
Project Home Wiki Issues
Search
for
Tutorials  
Brief instruction how to use this library
Featured, Phase-Implementation
Updated Feb 24, 2010 by macksx

See also: http://code.google.com/apis/protocolbuffers/docs/tutorials.html

Table of Contents

Install

The gem is registered to a gem server, so that all you should do to install is to execute the following command:

 $ sudo gem install ruby_protobuf

Compile Your .proto File

 $ rprotoc PROTOFILE

When PROTOFILE is named 'person.proto,' the name of the generated ruby source is 'person.pb.rb.'

To know about other options, use -h option.

 $ rprotoc -h
rprotoc [OPTIONS] PROTO_FILE
    -p, --proto_path <PATH>          Specify the directory in which to search for imports. The current directory is default.
    -o, --out <OUT_DIR>              Specify the directory in which Ruby source file is generated. The current directory is default.
    -v, --version                    Show version.
    -h, --help                       Show this message.

Generated Ruby Code

A .proto file like this:

package tutorial;

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;

  extensions 100 to 200;
}

extend Person {
  optional int32 age = 100;
}

message AddressBook {
  repeated Person person = 1;
}

generates ruby code like this:

module Tutorial
  class Person < ::Protobuf::Message
    required :string, :name, 1
    required :int32, :id, 2
    optional :string, :email, 3

    class PhoneType < ::Protobuf::Enum
      define :MOBILE, 0
      define :HOME, 1
      define :WORK, 2
    end

    class PhoneNumber < ::Protobuf::Message
      required :string, :number, 1
      optional :PhoneType, :type, 2, :default => :HOME
    end

    repeated :PhoneNumber, :phone, 4
    extensions 100..200
  end

  class Person < ::Protobuf::Message
    optional :int32, :age, 100, :extension => true
  end

  class AddressBook < ::Protobuf::Message
    repeated :Person, :person, 1
  end
end

The Protocol Buffer API

As you can see in the previous section, messages and fields in a proto file is respectively converted to ruby classes derived from Protobuf::Message class and their attributes. Using the class and its instance, you could write:

require 'addressbook.pb'
include Tutorial
person = Person.new
person.id = 1234
person.name = 'John Doe'
person.email = 'jdoe@example.com'
phone = Person::PhoneNumber.new
phone.number = '555-4321'
phone.type = Person::PhoneType::HOME
person.phone << phone

Note that the type of variable is checked in accordance with the proto file when assigned:

person.no_such_field = 1  # raises NoMethodError
person.id = '1234'        # raises TypeError

Enums

Enum field can accept integer value, symbol value or EnumValue object. On general case, you will use integers or symbols, and may not notice EnumValue object because EnumValue object is automatically converted into integer value.

For example, when .proto is:

enum EnumType {
  FIRST = 1;
  SECOND = 2;
}
message Message {
  required EnumType enum = 1;
}

generated code is:

class EnumType < ::Protobuf::Enum
  define :FIRST, 1
  define :SECOND, 2
end
class Message < ::Protobuf::Message
  required :EnumType, :enum, 1
end

and you can use like this:

EnumType::FIRST.class   #=> Protobuf::EnumValue
EnumType::FIRST == 1    #=> true
1 == EnumType::FIRST    #=> true

message = Message.new
message.enum = EnumType::FIRST  # OK
message.enum = 1                # OK
message.enum = :FIRST           # OK
message.enum.class              #=> Protobuf::EnumValue

puts "value is #{message.enum}"  #=> "value is FIRST"

Standard Message Methods

  • initialized? : checks if all the required fields have been set.
  • dup : duplicate the message.
  • clear! : clears all the elements back to the empty state.
  • has_field?(field_name) : checks a field value is already set or not. It's useful for optional fields.
  • inspect : returns a human-readable representation of the message.

Parsing and Serialization

Finally, each protocol buffer class has methods for writing and reading messages of your chosen type using the protocol buffer binary format. These include:

  • serialize_to_string : serializes the message and returns it as a string. Note that the bytes are binary, not text; we only use the str type as a convenient container.
  • to_s : alias of serialize_to_string.
  • serialize_to_file(filename)
  • serialize_to(stream)
  • parse_from_string(data) : parses a message from the given string.
  • parse_from_file(filename)
  • parse_from(stream)

Sample (Mini) Applications

Writing A Message

examples/writing_a_message.rb

#!/usr/local/bin/ruby

require 'addressbook.pb'

def prompt_for_address(person)
  print 'Enter person ID number: '
  person.id = STDIN.gets.strip.to_i
  print 'Enter name: '
  person.name = STDIN.gets.strip
  print 'Enter email address (blank for none): '
  email = STDIN.gets.strip
  person.email = email unless email.empty?

  loop do
    print 'Enter a phone number (or leave blank to finish): '
    break if (number = STDIN.gets.strip).empty?

    person.phone << Tutorial::Person::PhoneNumber.new
    person.phone.last.number = number

    print 'Is this a mobile, home, or work phone? '
    person.phone.last.type =
      case type = STDIN.gets.strip
      when 'mobile'
        Tutorial::Person::PhoneType::MOBILE
      when 'home'
        Tutorial::Person::PhoneType::HOME
      when 'work'
        Tutorial::Person::PhoneType::WORK
      else
        puts 'Unknown phone type; leaving as default value.'
        nil
      end
  end
end

unless ARGV.size == 1
  puts "Usage: #{$0} ADDRESS_BOOK_FILE"
  exit
end

address_book = Tutorial::AddressBook.new
address_book.parse_from_file ARGV[0] if File.exist? ARGV[0]
address_book.person << Tutorial::Person.new
prompt_for_address address_book.person.last
address_book.serialize_to_file ARGV[0]

Reading A Message

examples/reading_a_message.rb

#!/usr/local/bin/ruby

require 'addressbook.pb'

def list_people(address_book)
  address_book.person.each do |person|
    puts "Person ID: #{person.id}"
    puts "  Name: #{person.name}"
    puts "  E-mail: #{person.email}" unless person.email.empty?
    person.phone.each do |phone_number|
      print(case phone_number.type
            when Tutorial::Person::PhoneType::MOBILE
              '  Mobile phone #: '
            when Tutorial::Person::PhoneType::HOME
              '  Home phone #: '
            when Tutorial::Person::PhoneType::WORK
              '  Work phone #: '
            end)
      puts phone_number.number
    end
  end
end

unless ARGV.size == 1
  puts "Usage: #{$0} ADDRESS_BOOK_FILE"
  exit
end

address_book = Tutorial::AddressBook.new
address_book.parse_from_file ARGV[0]

list_people address_book
Comment by pauljsol...@gmail.com, Nov 20, 2008

I can successfully run the 'write' code the first time. When it tries to read from the file I just created, it blows up. Any idea what is causing this? I also tried manually creating an address book object (from the 'The Protocol Buffer API' section above) and it blew up the same way in the parse_from_file call.

gems/ruby_protobuf-0.3.0/lib/protobuf/message/field.rb:579:in `pack': pack(U): value out of range (RangeError?)

from gems/ruby_protobuf-0.3.0/lib/protobuf/message/field.rb:579:in `set_array' etc.

Comment by snow.gre...@gmail.com, Nov 26, 2008

I get the same thing.

Comment by aant...@gmail.com, Dec 16, 2008

Does anyone know if this project is still being maintained?

Comment by project member andy...@gmail.com, Dec 17, 2008

Comments are not notified. I read these comment just now. I'm grad if you add an issue when you find a bug.

Comment by project member andy...@gmail.com, Dec 17, 2008

To pauljsolomon and snow.gregory,

How about 0.3.2? On my computer, it looks work.

$ cd examples 
$ RUBYLIB=../lib ruby writing_a_message.rb myaddr 
Enter person ID number: 123 
Enter name: yasushi 
Enter email address (blank for none): yasushi@ando 
Enter a phone number (or leave blank to finish): 321-123 
Is this a mobile, home, or work phone? mobile 
Enter a phone number (or leave blank to finish): 123-456 
Is this a mobile, home, or work phone? home 
Enter a phone number (or leave blank to finish): 
$ RUBYLIB=../lib ruby reading_a_message.rb myaddr 
Person ID: 123 
  Name: yasushi
  E-mail: yasushi@ando
  Mobile phone #: 321-123
  Home phone #: 123-456 
Comment by silent.w...@gmail.com, Oct 26, 2009

I have done a comparison of reading 1 million json message and transform it into a protobuf object. That's about 5.5 times slower than reading the same 1 million json message, convert it to ruby hash, then convert the hash back to json message and output to file.

The json-to-ruby-to-json-plus-io is in theory doing more. The json-to-protobuf doesn't output the serialized stream yet. All it does is construct the protobuf data object.

Comment by jknd...@gmail.com, Dec 30, 2010

@silent.wave

Can your Java app read in that Ruby hash? ;]

Comment by pshuss...@gmail.com, Apr 9, 2011

Can I store multiple message in a single serialize_to_file?

Please advice me

Thanks Hussain


Sign in to add a comment
Powered by Google Project Hosting