-
Notifications
You must be signed in to change notification settings - Fork 101
Messages & Enums
Given the following compiled definition of a Foo::User
:
# lib/foo/user.pb.rb
require 'protobuf/message'
module Foo
class States < ::Protobuf::Enum
define :ALABAMA, 1
#...
end
class User < ::Protobuf::Message; end
class Address < ::Protobuf::Message; end
class User
optional :string, :first_name, 1
optional :string, :last_name, 2
optional ::Foo::Address, :address, 3
optional :bool, :is_active, 4
end
class Address
optional :string, :street, 1
optional :string, :city, 2
optional ::Foo::States, :state, 3
optional :int32, :zip_code, 4
end
end
We can require the .pb.rb
file and create user objects in a similar style to working with a Struct
. New objects can use dot-notation or bracket-notation to get/set fields from the proto object. You can also instantiate new protos with a hash where keys are the field names. Any keys not corresponding to defined fields are silently ignored.
Whether using a the setter method or passing in values via hash, the value is processed by the same code that will reject bad values. Notice in the example below that we can assign fields of a Message type either with the object of that type or a hash. If a hash is given the type is simply instantiated with that hash and assigned.
require 'protobuf'
require 'lib/foo/user.pb'
user = Foo::User.new
# dot notation reading/writing fields
user.first_name = "Lloyd"
user.last_name = "Christmas"
user.first_name # => "Lloyd"
user.last_name # => "Christmas"
# bracket notation reading/writing fields
user[:first_name] = "Harry"
user[:last_name] = "Dunne"
user[:first_name] # => "Harry"
user[:last_name] # => "Dunne"
# assign whole message instances
user.address = Foo::Address.new({ :street => '21 Jump Street',
:city => 'Alphaville',
:state => Foo::States::CALIFORNIA,
:zip_code => 12345 })
# or pass in the fields as a hash to the initializer (nested hashes are perfectly acceptable)
user = Foo::User.new({ :first_name => "Lloyd",
:last_name => "Christmas",
:address => { :street => '21 Jump Street',
:city => 'Alphaville',
:state => Foo::States::CALIFORNIA,
:zip_code => 12345 } })
user.first_name # => Lloyd
user.last_name # => Christmas
With a message instance you can check for the presence of certain values.
user = Foo::User.new(:first_name => 'Bob', :last_name => nil)
user.field?(:first_name) # => true
user.field?(:address) # => false
user.field?(:last_name) # => false (because we set the field to nil)
user.respond_to_has_and_present?(:last_name) # => false (check if the field has been set and the value is "present?")
Any message that defines a boolean field will provide a predicate getter method in addition to the normal getter.
user = Foo::User.new
user.is_active # => false
user.is_active? # => false
user.is_active = true
user.is_active # => true
user.is_active? # => true
# lib/foo/user.pb.rb
require 'protobuf/message'
module Foo
class States < ::Protobuf::Enum
define :ALABAMA, 1
define :ALASKA, 2
define :ARIZONA, 3
#...
end
end
A class that inherits from Protobuf::Enum
has a very simple interface. Any defined enum is instantiated and assigned as a class constant. Each Enum
instance has a name
and a tag
(alternately accessed via to_i
).
# Access an EnumValue directly by the named constant
Foo::States::ALABAMA # => 1
Foo::States::ALABAMA.to_i # => 1 (preferred method)
Foo::States::ALABAMA.tag # => 1
Foo::States::ALABAMA.name # => :ALABAMA
Constant access is nice if you know the name, but if you only have the tag or name in a variable, use Enum.fetch
to get the corresponding Enum
instance. There are other access methods if you have an integer or a string/symbol name, but fetch handles all those cases (and more), so use it.
user.address.state # => 1
enum_state = Foo::States.fetch(user.address.state) # => Foo::State::ALABAMA
If you wish to programmatically access all of the defined values in an enum, you should use the .enums
class method to get an array of all defined instances, or the .all_tags
class method to get each unique defined tag. The class method .values
has been deprecated and should be upgraded to access .enums
or .all_tags
depending on your use case.
Foo::States.enums # => ["#<Protobuf::Enum(Foo::States)::ALABAMA=1>", "#<Protobuf::Enum(Foo::States)::ALASKA=2>", "#<Protobuf::Enum(Foo::States)::ARIZONA=3>"]
Foo::States.all_tags # => [ 1, 2, 3 ]
Protocol Buffers provides the ability to enable aliases in an enum definition. As of v3.0.0.rc1, we can support this capability.
class StateMachine < ::Protobuf::Enum
set_option :allow_alias
define :ON, 1
define :STARTED, 1
define :OFF, 2
end
StateMachine.enums # => ["#<Protobuf::Enum(StateMachine)::ON=1>","#<Protobuf::Enum(StateMachine)::STARTED=1>,"#<Protobuf::Enum(StateMachine)::OFF=2>""]
StateMachine.all_tags # => [ 1, 2 ]
Next: Serialization
Back: Compiling Definitions