Created
June 19, 2015 01:45
-
-
Save dugancathal/65777ae3dba08e1e7cf9 to your computer and use it in GitHub Desktop.
A DSL for protobuf in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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