Skip to content

Add support for "oneof" keyword #211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 32 additions & 15 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,49 @@ require 'rubocop/rake_task'
RSpec::Core::RakeTask.new(:spec)
RuboCop::RakeTask.new

task :default => ['compile:spec', 'compile:rpc', :spec, :rubocop]
task :default => ['compile:spec', 'compile:descriptors', :spec, :rubocop]

desc 'Run both the spec and descriptors compilation tasks'
task :compile => ['compile:spec', 'compile:descriptors']

desc 'Run specs'
namespace :compile do

desc 'Compile spec protos in spec/supprt/ directory'
task :spec do
proto_path = ::File.expand_path('../spec/support/', __FILE__)
proto_files = Dir[File.join(proto_path, '**', '*.proto')]
cmd = %(protoc --plugin=./bin/protoc-gen-ruby --ruby_out=#{proto_path} -I #{proto_path} #{proto_files.join(' ')})
source = ::File.expand_path('../spec/support/', __FILE__)
input_files = ::File.join(source, '**', '*.proto')
destination = source

command = []
command << "PB_NO_TAG_WARNINGS=1"
command << "protoc --plugin=./bin/protoc-gen-ruby"
command << "--ruby_out=#{destination}"
command << "-I #{source}"
command << Dir[input_files].join(' ')
command = command.join(' ')

puts cmd
system(cmd)
puts command
system(command)
end

desc 'Compile rpc protos in protos/ directory'
task :rpc do
proto_path = ::File.expand_path('../proto', __FILE__)
proto_files = Dir[File.join(proto_path, '**', '*.proto')]
output_dir = ::File.expand_path('../tmp/rpc', __FILE__)
::FileUtils.mkdir_p(output_dir)
task :descriptors do
source = ::File.expand_path('../proto', __FILE__)
input_files = ::File.join(source, '**', '*.proto')
destination = ::File.expand_path('../tmp/rpc', __FILE__)
::FileUtils.mkdir_p(destination)

cmd = %(protoc --plugin=./bin/protoc-gen-ruby --ruby_out=#{output_dir} -I #{proto_path} #{proto_files.join(' ')})
command = []
command << "PB_NO_TAG_WARNINGS=1"
command << "protoc --plugin=./bin/protoc-gen-ruby"
command << "--ruby_out=#{destination}"
command << "-I #{source}"
command << Dir[input_files].join(' ')
command = command.join(' ')

puts cmd
system(cmd)
puts command
system(command)

files = {
'tmp/rpc/dynamic_discovery.pb.rb' => 'lib/protobuf/rpc',
Expand All @@ -48,7 +65,7 @@ namespace :compile do
}

files.each_pair do |source_file, destination_dir|
source_file = ::File.expand_path("../#{source_file}", __FILE__)
source_file = ::File.expand_path("../#{source_file}", __FILE__)
destination_dir = ::File.expand_path("../#{destination_dir}", __FILE__)
::FileUtils::Verbose.cp(source_file, destination_dir)
end
Expand Down
16 changes: 15 additions & 1 deletion lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class Label < ::Protobuf::Enum

end

class OneofDescriptorProto < ::Protobuf::Message; end
class EnumDescriptorProto < ::Protobuf::Message; end
class EnumValueDescriptorProto < ::Protobuf::Message; end
class ServiceDescriptorProto < ::Protobuf::Message; end
Expand Down Expand Up @@ -120,6 +121,7 @@ class ExtensionRange
repeated ::Google::Protobuf::DescriptorProto, :nested_type, 3
repeated ::Google::Protobuf::EnumDescriptorProto, :enum_type, 4
repeated ::Google::Protobuf::DescriptorProto::ExtensionRange, :extension_range, 5
repeated ::Google::Protobuf::OneofDescriptorProto, :oneof_decl, 8
optional ::Google::Protobuf::MessageOptions, :options, 7
end

Expand All @@ -131,9 +133,14 @@ class FieldDescriptorProto
optional :string, :type_name, 6
optional :string, :extendee, 2
optional :string, :default_value, 7
optional :int32, :oneof_index, 9
optional ::Google::Protobuf::FieldOptions, :options, 8
end

class OneofDescriptorProto
optional :string, :name, 1
end

class EnumDescriptorProto
optional :string, :name, 1
repeated ::Google::Protobuf::EnumValueDescriptorProto, :value, 2
Expand Down Expand Up @@ -164,11 +171,13 @@ class FileOptions
optional :string, :java_outer_classname, 8
optional :bool, :java_multiple_files, 10, :default => false
optional :bool, :java_generate_equals_and_hash, 20, :default => false
optional :bool, :java_string_check_utf8, 27, :default => false
optional ::Google::Protobuf::FileOptions::OptimizeMode, :optimize_for, 9, :default => ::Google::Protobuf::FileOptions::OptimizeMode::SPEED
optional :string, :go_package, 11
optional :bool, :cc_generic_services, 16, :default => false
optional :bool, :java_generic_services, 17, :default => false
optional :bool, :py_generic_services, 18, :default => false
optional :bool, :deprecated, 23, :default => false
repeated ::Google::Protobuf::UninterpretedOption, :uninterpreted_option, 999
# Extension Fields
extensions 1000...536870912
Expand All @@ -177,6 +186,7 @@ class FileOptions
class MessageOptions
optional :bool, :message_set_wire_format, 1, :default => false
optional :bool, :no_standard_descriptor_accessor, 2, :default => false
optional :bool, :deprecated, 3, :default => false
repeated ::Google::Protobuf::UninterpretedOption, :uninterpreted_option, 999
# Extension Fields
extensions 1000...536870912
Expand All @@ -195,25 +205,29 @@ class FieldOptions
end

class EnumOptions
optional :bool, :allow_alias, 2, :default => true
optional :bool, :allow_alias, 2
optional :bool, :deprecated, 3, :default => false
repeated ::Google::Protobuf::UninterpretedOption, :uninterpreted_option, 999
# Extension Fields
extensions 1000...536870912
end

class EnumValueOptions
optional :bool, :deprecated, 1, :default => false
repeated ::Google::Protobuf::UninterpretedOption, :uninterpreted_option, 999
# Extension Fields
extensions 1000...536870912
end

class ServiceOptions
optional :bool, :deprecated, 33, :default => false
repeated ::Google::Protobuf::UninterpretedOption, :uninterpreted_option, 999
# Extension Fields
extensions 1000...536870912
end

class MethodOptions
optional :bool, :deprecated, 33, :default => false
repeated ::Google::Protobuf::UninterpretedOption, :uninterpreted_option, 999
# Extension Fields
extensions 1000...536870912
Expand Down
13 changes: 12 additions & 1 deletion lib/protobuf/field/base_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ def message?
false
end

def oneof?
options.key?(:oneof)
end

def oneof_name
options[:oneof]
end

def optional?
rule == :optional
end
Expand Down Expand Up @@ -207,6 +215,7 @@ def define_array_setter
if val.nil? || (val.respond_to?(:empty?) && val.empty?)
@values.delete(field.name)
else
clear_oneof_group(field.oneof_name) if field.oneof?
@values[field.name] ||= ::Protobuf::Field::FieldArray.new(field)
@values[field.name].replace(val)
end
Expand Down Expand Up @@ -238,7 +247,9 @@ def define_setter
if val.nil? || (val.respond_to?(:empty?) && val.empty?)
@values.delete(field.name)
elsif field.acceptable?(val)
@values[field.name] = field.coerce!(val)
coerced_value = field.coerce!(val)
clear_oneof_group(field.oneof_name) if field.oneof?
@values[field.name] = coerced_value
else
fail TypeError, "Unacceptable value #{val} for field #{field.name} of type #{field.type_class}"
end
Expand Down
1 change: 1 addition & 0 deletions lib/protobuf/field/bytes_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def define_setter
if val.nil?
@values.delete(field.name)
elsif field.acceptable?(val)
clear_oneof_group(field.oneof_name) if field.oneof?
@values[field.name] = val.dup
else
fail TypeError, "Unacceptable value #{val} for field #{field.name} of type #{field.type_class}"
Expand Down
1 change: 1 addition & 0 deletions lib/protobuf/field/enum_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def define_setter
value = field.type_class.fetch(value)
fail TypeError, "Invalid Enum value: #{orig_value.inspect} for #{field.name}" unless value

clear_oneof_group(field.oneof_name) if field.oneof?
@values[field.name] = value
end
end
Expand Down
27 changes: 15 additions & 12 deletions lib/protobuf/field/message_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ class MessageField < BaseField
# Public Instance Methods
#

def acceptable?(val)
unless val.is_a?(type_class) || val.respond_to?(:to_hash)
fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{val.class}'"
def acceptable?(value)
unless value.is_a?(type_class) || value.respond_to?(:to_hash)
fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{value.class}'"
end

true
Expand Down Expand Up @@ -43,18 +43,21 @@ def wire_type
def define_setter
field = self
message_class.class_eval do
define_method("#{field.name}=") do |val|
define_method("#{field.name}=") do |value|
case
when val.nil? then
when value.nil? then
@values.delete(field.name)
when val.is_a?(field.type_class) then
@values[field.name] = val
when val.respond_to?(:to_proto) then
@values[field.name] = val.to_proto
when val.respond_to?(:to_hash) then
@values[field.name] = field.type_class.new(val.to_hash)
when value.is_a?(field.type_class) then
clear_oneof_group(field.oneof_name) if field.oneof?
@values[field.name] = value
when value.respond_to?(:to_proto) then
clear_oneof_group(field.oneof_name) if field.oneof?
@values[field.name] = value.to_proto
when value.respond_to?(:to_hash) then
clear_oneof_group(field.oneof_name) if field.oneof?
@values[field.name] = field.type_class.new(value.to_hash)
else
fail TypeError, "Expected value of type '#{field.type_class}' for field #{field.name}, but got '#{val.class}'"
fail TypeError, "Expected value of type '#{field.type_class}' for field #{field.name}, but got '#{value.class}'"
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/protobuf/generators/extension_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def compile
run_once(:compile) do
print_class(@message_type, :message) do
group = GroupGenerator.new(current_indent)
group.add_extension_fields(@field_descriptors)
group.add_extension_fields(@field_descriptors, [])
group.order = [:extension_field]
print group.to_s
end
Expand Down
50 changes: 35 additions & 15 deletions lib/protobuf/generators/field_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,30 @@ class FieldGenerator < Base
##
# Attributes
#
attr_reader :field_options
attr_reader :field_options, :oneof_descriptors

##
# Constructor
#
def initialize(descriptor, oneof_descriptors = [], indent_level = 0, options = {})
super(descriptor, indent_level, options)
@oneof_descriptors = oneof_descriptors
end

##
# Public Instance Methods
#
def applicable_options
@applicable_options ||= field_options.map { |k, v| ":#{k} => #{v}" }
end

def compile
run_once(:compile) do
field_definition = ["#{label} #{type_name}", name, number, applicable_options]
puts field_definition.flatten.compact.join(', ')
end
end

def default_value
@default_value ||= begin
if defaulted?
Expand Down Expand Up @@ -52,11 +70,16 @@ def extension?
descriptor.respond_to_has_and_present?(:extendee)
end

def compile
run_once(:compile) do
field_definition = ["#{label} #{type_name}", name, number, applicable_options]
puts field_definition.flatten.compact.join(', ')
end
def field_options
@field_options ||= begin
opts = {}
opts[:default] = default_value if defaulted?
opts[:packed] = 'true' if packed?
opts[:deprecated] = 'true' if deprecated?
opts[:extension] = 'true' if extension?
opts[:oneof] = oneof_name if oneof?
opts
end
end

def label
Expand All @@ -71,15 +94,12 @@ def number
@number ||= descriptor.number
end

def field_options
@field_options ||= begin
opts = {}
opts[:default] = default_value if defaulted?
opts[:packed] = 'true' if packed?
opts[:deprecated] = 'true' if deprecated?
opts[:extension] = 'true' if extension?
opts
end
def oneof?
!descriptor.oneof_index!.nil?
end

def oneof_name
":#{oneof_descriptors[descriptor.oneof_index].name}"
end

def packed?
Expand Down
8 changes: 4 additions & 4 deletions lib/protobuf/generators/group_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ def add_extended_messages(extended_messages)
end
end

def add_extension_fields(field_descriptors)
def add_extension_fields(field_descriptors, oneof_descriptors)
field_descriptors.each do |field_descriptor|
@groups[:extension_field] << FieldGenerator.new(field_descriptor, indent_level)
@groups[:extension_field] << FieldGenerator.new(field_descriptor, oneof_descriptors, indent_level)
end
end

Expand All @@ -59,9 +59,9 @@ def add_message_declarations(descriptors)
end
end

def add_message_fields(field_descriptors)
def add_message_fields(field_descriptors, oneof_descriptors)
field_descriptors.each do |field_descriptor|
@groups[:field] << FieldGenerator.new(field_descriptor, indent_level)
@groups[:field] << FieldGenerator.new(field_descriptor, oneof_descriptors, indent_level)
end
end

Expand Down
7 changes: 4 additions & 3 deletions lib/protobuf/generators/message_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,18 @@ def compile_message
print_class(descriptor.name, nil) do
group = GroupGenerator.new(current_indent)
group.add_messages(descriptor.nested_type, :extension_fields => @extension_fields, :namespace => type_namespace)
group.add_message_fields(descriptor.field)

group.add_message_fields(descriptor.field, descriptor.oneof_decl)
self.class.validate_tags(fully_qualified_type_namespace, descriptor.field.map(&:number))

group.add_comment(:extension_range, 'Extension Fields')
group.add_extension_ranges(descriptor.extension_range) do |extension_range|
"extensions #{extension_range.start}...#{extension_range.end}"
end

group.add_extension_fields(message_extension_fields)
group.add_extension_fields(message_extension_fields, descriptor.oneof_decl)

group.order = [:message, :field, :extension_range, :extension_field]
group.order = [:message, :oneof_descriptors, :field, :extension_range, :extension_field]
print group.to_s
end
end
Expand Down
10 changes: 10 additions & 0 deletions lib/protobuf/message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ def clear!
self
end

def clear_oneof_group(oneof_name)
fields = self.class.oneof_fields.select do |field|
field.oneof_name == oneof_name
end

fields.each do |field|
@values.delete(field.name)
end
end

def clone
copy_to(super, :clone)
end
Expand Down
Loading