Skip to content

Commit

Permalink
Clean up dynamic code generation in prep for extension namespacing
Browse files Browse the repository at this point in the history
  • Loading branch information
embark committed Feb 12, 2016
1 parent 2970c0d commit 7d8247e
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 189 deletions.
4 changes: 2 additions & 2 deletions lib/protobuf/field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ module Field
:bool => ::Protobuf::Field::BoolField,
}.freeze

def self.build(message_class, rule, type, name, tag, options = {})
field_class(type).new(message_class, rule, field_type(type), name, tag, options)
def self.build(message_class, rule, type, name, tag, simple_name, options = {})
field_class(type).new(message_class, rule, field_type(type), name, tag, simple_name, options)
end

# Returns the field class for primitives,
Expand Down
104 changes: 20 additions & 84 deletions lib/protobuf/field/base_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def self.default
# Constructor
#

def initialize(message_class, rule, type_class, name, tag, options)
def initialize(message_class, rule, type_class, name, tag, simple_name, options)
@message_class = message_class
@name = name
@rule = rule
Expand All @@ -44,7 +44,7 @@ def initialize(message_class, rule, type_class, name, tag, options)
@options = options

validate_packed_field if packed?
define_accessor
define_accessor(simple_name, name) if simple_name
tag_encoded
end

Expand All @@ -69,10 +69,12 @@ def default
end

def default_value
@default_value ||= case
when optional? then typed_default_value
when repeated? then ::Protobuf::Field::FieldArray.new(self).freeze
when required? then nil
@default_value ||= if optional? || required?
typed_default_value
elsif repeated?
::Protobuf::Field::FieldArray.new(self).freeze
else
raise "something went very wrong"
end
end

Expand Down Expand Up @@ -122,10 +124,10 @@ def required?

# FIXME: need to cleanup (rename) this warthog of a method.
def set(message_instance, bytes)
return message_instance.__send__(setter, decode(bytes)) unless repeated?
return message_instance.__send__(getter) << decode(bytes) unless packed?
return message_instance[name] = decode(bytes) unless repeated?
return message_instance[name] << decode(bytes) unless packed?

array = message_instance.__send__(getter)
array = message_instance[name]
stream = StringIO.new(bytes)

if wire_type == ::Protobuf::WireType::VARINT
Expand Down Expand Up @@ -169,89 +171,23 @@ def wire_type
# Private Instance Methods
#

def define_accessor
if repeated?
define_array_getter
define_array_setter
else
define_getter
define_setter
end
end

def define_array_getter
field = self
method_name = field.getter

def define_accessor(simple_field_name, fully_qualified_field_name)
message_class.class_eval do
define_method(method_name) do
@values[field.name] ||= ::Protobuf::Field::FieldArray.new(field)
define_method("#{simple_field_name}!") do
@values[fully_qualified_field_name]
end
end

::Protobuf.field_deprecator.deprecate_method(message_class, method_name) if field.deprecated?
end

def define_array_setter
field = self
method_name = field.setter

message_class.class_eval do
define_method(method_name) do |val|
@encode = nil
if val.is_a?(Array)
val = val.dup
val.compact!
else
fail TypeError, <<-TYPE_ERROR
Expected repeated value of type '#{field.type_class}'
Got '#{val.class}' for repeated protobuf field #{field.name}
TYPE_ERROR
end

if val.nil? || (val.respond_to?(:empty?) && val.empty?)
@values.delete(field.name)
else
@values[field.name] ||= ::Protobuf::Field::FieldArray.new(field)
@values[field.name].replace(val)
end
end
define_method(simple_field_name) { self[fully_qualified_field_name] }
define_method("#{simple_field_name}=") { |v| self[fully_qualified_field_name] = v }
end

::Protobuf.field_deprecator.deprecate_method(message_class, method_name) if field.deprecated?
end

def define_getter
field = self
method_name = field.getter

message_class.class_eval do
define_method(method_name) do
@values[field.name] || field.default_value
end
if deprecated?
::Protobuf.field_deprecator.deprecate_method(message_class, simple_field_name)
::Protobuf.field_deprecator.deprecate_method(message_class, "#{simple_field_name}!")
::Protobuf.field_deprecator.deprecate_method(message_class, "#{simple_field_name}=")
end

::Protobuf.field_deprecator.deprecate_method(message_class, method_name) if field.deprecated?
end

def define_setter
field = self
method_name = field.setter

message_class.class_eval do
define_method(method_name) do |val|
@encode = nil
if val.nil? || (val.respond_to?(:empty?) && val.empty?)
@values.delete(field.name)
elsif field.acceptable?(val)
@values[field.name] = field.coerce!(val)
else
fail TypeError, "Unacceptable value #{val} for field #{field.name} of type #{field.type_class}"
end
end
end

::Protobuf.field_deprecator.deprecate_method(message_class, method_name) if field.deprecated?
end

def typed_default_value
Expand Down
16 changes: 3 additions & 13 deletions lib/protobuf/field/bool_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,11 @@ def encode(value)
# Private Instance Methods
#

def define_getter
field = self
method_name = field.getter

def define_accessor(simple_field_name, _fully_qualified_field_name)
super
message_class.class_eval do
define_method(method_name) do
@values.fetch(field.name, field.default_value)
end
alias_method "#{simple_field_name}?", simple_field_name
end

message_class.class_eval do
alias_method "#{method_name}?", method_name
end

::Protobuf.field_deprecator.deprecate_method(message_class, method_name) if field.deprecated?
end

end
Expand Down
37 changes: 10 additions & 27 deletions lib/protobuf/field/bytes_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def decode(bytes)
end

def encode(value)
value_to_encode = ""
if value.is_a?(::Protobuf::Message)
value_to_encode = value.encode
else
Expand All @@ -50,33 +49,17 @@ def wire_type
::Protobuf::WireType::LENGTH_DELIMITED
end

private

##
# Private Instance Methods
#

def define_setter
field = self
method_name = field.setter

message_class.class_eval do
define_method(method_name) do |val|
@encode = nil
case val
when String, Symbol
@values[field.name] = "#{val}"
when NilClass
@values.delete(field.name)
when ::Protobuf::Message
@values[field.name] = val.dup
else
fail TypeError, "Unacceptable value #{val} for field #{field.name} of type #{field.type_class}"
end
end
def coerce!(value)
case value
when String, Symbol
value.to_s
when NilClass
nil
when ::Protobuf::Message
value.dup
else
fail TypeError, "Unacceptable value #{value} for field #{name} of type #{type_class}"
end

::Protobuf.field_deprecator.deprecate_method(message_class, method_name) if field.deprecated?
end
end
end
Expand Down
24 changes: 6 additions & 18 deletions lib/protobuf/field/enum_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,18 @@ def enum?
true
end

def coerce!(value)
enum_value = type_class.fetch(value)
fail TypeError, "Invalid Enum value: #{value.inspect} for #{name}" unless enum_value
enum_value
end

private

##
# Private Instance Methods
#

def define_setter
field = self
message_class.class_eval do
define_method("#{field.name}=") do |value|
@encode = nil
orig_value = value
if value.nil?
@values.delete(field.name)
else
value = field.type_class.fetch(value)
fail TypeError, "Invalid Enum value: #{orig_value.inspect} for #{field.name}" unless value

@values[field.name] = value
end
end
end
end

def typed_default_value
if default.is_a?(Symbol)
type_class.const_get(default)
Expand Down
35 changes: 11 additions & 24 deletions lib/protobuf/field/message_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,17 @@ def wire_type
::Protobuf::WireType::LENGTH_DELIMITED
end

private

##
# Private Instance Methods
#

def define_setter
field = self
message_class.class_eval do
define_method("#{field.name}=") do |val|
@encode = nil
case
when val.nil?
@values.delete(field.name)
when val.is_a?(field.type_class)
@values[field.name] = val
when val.respond_to?(:to_proto)
@values[field.name] = val.to_proto
when val.respond_to?(:to_hash)
@values[field.name] = field.type_class.new(val.to_hash)
else
fail TypeError, "Expected value of type '#{field.type_class}' for field #{field.name}, but got '#{val.class}'"
end
end
def coerce!(value)
if value.nil?
nil
elsif value.is_a?(type_class)
value
elsif value.respond_to?(:to_proto)
value.to_proto
elsif value.respond_to?(:to_hash)
type_class.new(value.to_hash)
else
fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{value.class}'"
end
end

Expand Down
Loading

0 comments on commit 7d8247e

Please sign in to comment.