Skip to content

Instantly share code, notes, and snippets.

@dugancathal
Created June 19, 2015 01:45
Show Gist options
  • Save dugancathal/65777ae3dba08e1e7cf9 to your computer and use it in GitHub Desktop.
Save dugancathal/65777ae3dba08e1e7cf9 to your computer and use it in GitHub Desktop.
A DSL for protobuf in Ruby
require 'erb'
require 'binding_of_caller'
GENERATED_CLASS_FOR_MESSAGE = <<-CLS
class <%= name %>
attr_reader <%= message.fields.map {|name, _| ':' + name.to_s }.join(', ') %>
def initialize
<% message.fields.each do |name, field| %>
<%= field.to_s %>
<% end %>
end
end
CLS
class Parser
def self.parse(source)
proto = ParsedProto.new
proto.instance_eval(source)
proto
end
end
module DSL
def message(mess)
end
def method_missing(method_name, *args, &block)
@messages[method_name] = Message.new(args[0], &block)
end
end
class ParsedProto
include DSL
def initialize
@messages = {}
end
def generate
@messages.map do |name, message|
ERB.new(GENERATED_CLASS_FOR_MESSAGE).result(binding).lines.reject {|line| line =~ /^\s*$/ }.join
end
end
end
class Field < Struct.new(:label, :type, :name, :position)
TYPES = %w(double float int32 int64 uint32 uint64 sint32
sint64 fixed32 fixed64 sfixed32 sfixed64 bool string bytes)
LABELS = %w(optional required repeated)
def initial_value
case
when label == :repeated then []
when label == :optional then nil
when type == :double then 0.0
when type == :float then 0.0
when type == :int32 then 0
when type == :int64 then 0
when type == :uint32 then 0
when type == :uint64 then 0
when type == :bool then false
when type == :string then ""
end
end
def to_s
"@#{name} = #{initial_value}"
end
end
class Message
attr_reader :fields, :name
def initialize(name, &definition)
@name = name
@fields = {}
instance_eval(&definition)
end
def repeated(field)
field.label = :repeated
end
def optional(field)
field.label = :optional
end
Field::TYPES.each do |type_method_name|
define_method type_method_name do |value|
field_name = binding.of_caller(1).local_variables[fields.count]
fields[field_name] = Field.new("", type_method_name.to_sym, field_name, value)
end
end
end
puts Parser.parse("
message Thing {
repeated string tags = 1;
int64 id = 2;
bool public = 3;
}
"
).generate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment