module DataMetaProtobuf

DataMetaDOM and Protobuf IDL.

For command line details either check the new method's source or the README, the usage section.

Constants

GEM_ROOT

The root of the gem.

INDENT

First level indent

L

Since we are piping source in and spilling the result into the STDOUT, need logger for any other output.

PROTO_TYPES

Mapping from a DataMeta DOM type to a matching renderer of Protobuf IDL.

TMPL_ROOT

Location of templates.

VERSION

Current version

Public Class Methods

assertNamespace(fullName) click to toggle source

Splits the full name of a class into the namespace and the base, returns an array of the namespace (empty string if there is no namespace on the name) and the base name.

Examples:

  • 'BaseNameAlone' -> ['', 'BaseNameAlone']

  • 'one.package.another.pack.FinallyTheName' -> ['one.package.another.pack', 'FinallyTheName']

# File lib/dataMetaProtobuf.rb, line 95
def assertNamespace(fullName)
  ns, base = DataMetaDom::splitNameSpace(fullName)
  [DataMetaDom.validNs?(ns, base) ? ns : '', base]
end
genSchema(model) click to toggle source

Generates the Protobuf IDL, returns the source.

We decided against using the template and use simple string concatenation instead; because the way Protobuf IDL is designed, it's easier to do the string concat. For example, rendering a message in a message in a message…

# File lib/dataMetaProtobuf.rb, line 106
    def genSchema(model)
        result = %<
// This Protobuf schema is generated by DataMeta exporter.

syntax = "proto3";
package #{model.sources[model.sources.doneKeys[0]].namespace};

>
        model.enums.values.each { |e| renderEnum model, e, result }
        model.records.values.each { |r| renderRec model, r, result }
        result << "\n"
        result
    end
helpProtobufGen(file, errorText=nil) click to toggle source

Shortcut to help for the Protobuf IDL generator

# File lib/dataMetaProtobuf.rb, line 177
def helpProtobufGen(file, errorText=nil)
    DataMetaDom::help(file, %<DataMeta DOM Protobuf IDL Generation ver #{VERSION}>, '<DataMeta DOM source file>', errorText)
end
protoType(dataMetaType, model, rec) click to toggle source

Converts DataMeta DOM type to Protobuf IDL type.

# File lib/dataMetaProtobuf.rb, line 72
def protoType(dataMetaType, model, rec)
    ty = dataMetaType.type
    if model.records[ty]
        nameSpace, base = assertNamespace(ty)
        base
    elsif model.enums[ty]
        nameSpace, base = assertNamespace(ty)
        base
    else
        renderer = PROTO_TYPES[dataMetaType.type]
        raise "Unsupported type #{dataMetaType}" unless renderer
        renderer.call(dataMetaType)
    end
end
renderEnum(model, enm, result) click to toggle source

Render one single enum

# File lib/dataMetaProtobuf.rb, line 151
    def renderEnum(model, enm, result)
        nameSpace, base = assertNamespace(enm.name)
        L.info("Rendering enum #{enm.name} of the type #{enm.class}")
        case enm
            when DataMetaDom::Enum
                values = enm.keys.map{|k| enm[k]} # sorted by ordinals to preserve the original order
                pbInt = 0
                result << %<
enum #{base} {
>
                values.each{|v|
                    result << "#{INDENT}#{v} = #{pbInt};\n"
                    pbInt += 1
                }
                result << %q<}
>
            when DataMetaDom::Mapping
                # taken care of in renderDer
            else
                raise ArgumentError, %<Unsupported enum type "#{enm.class}" for the name "#{enm.name}">

        end

    end
renderRec(model, rec, result) click to toggle source

Render a single record

# File lib/dataMetaProtobuf.rb, line 121
    def renderRec(model, rec, result)
        nameSpace, base = assertNamespace(rec.name)
        L.info("Rendering record #{rec.name}")
        result << %<
message #{base} {
>
        pbInt = 1
        rec.fields.each_key { | fldId|
            fld = rec.fields[fldId]
            ty = fld.dataType.type
            if fld.aggr && !fld.map?
                result << "#{INDENT}repeated #{protoType(fld.dataType, model, rec)} #{fldId} = #{pbInt};\n"
            elsif fld.map?
                raise ArgumentError, %<Field "#{fldId}" of the rec "#{base}": for Protobuf, map can not be optional, it must be required> unless fld.isRequired
                result << "#{INDENT}map<#{protoType(fld.dataType, model, rec)}, #{protoType(fld.trgType, model, rec)}> #{fldId} = #{pbInt};\n"
            elsif model.enums[ty] && model.enums[ty].is_a?(DataMetaDom::Mapping)
                raise ArgumentError, %<Field "#{fldId}" of the rec "#{base}": for Protobuf, map can not be optional, it must be required> unless fld.isRequired
                result << "#{INDENT}map<#{protoType(model.enums[ty].fromT, model, rec)}, #{protoType(model.enums[ty].toT, model, rec)}> #{fldId} = #{pbInt};\n"
            elsif fld.isRequired
                result << "#{INDENT}#{protoType(fld.dataType, model, rec)} #{fldId} = #{pbInt};\n"
            else
                result << "#{INDENT}repeated #{protoType(fld.dataType, model, rec)} #{fldId} = #{pbInt};\n"
            end
            pbInt += 1
        }
        result << %q<}
>
    end

Private Instance Methods

assertNamespace(fullName) click to toggle source

Splits the full name of a class into the namespace and the base, returns an array of the namespace (empty string if there is no namespace on the name) and the base name.

Examples:

  • 'BaseNameAlone' -> ['', 'BaseNameAlone']

  • 'one.package.another.pack.FinallyTheName' -> ['one.package.another.pack', 'FinallyTheName']

# File lib/dataMetaProtobuf.rb, line 95
def assertNamespace(fullName)
  ns, base = DataMetaDom::splitNameSpace(fullName)
  [DataMetaDom.validNs?(ns, base) ? ns : '', base]
end
genSchema(model) click to toggle source

Generates the Protobuf IDL, returns the source.

We decided against using the template and use simple string concatenation instead; because the way Protobuf IDL is designed, it's easier to do the string concat. For example, rendering a message in a message in a message…

# File lib/dataMetaProtobuf.rb, line 106
    def genSchema(model)
        result = %<
// This Protobuf schema is generated by DataMeta exporter.

syntax = "proto3";
package #{model.sources[model.sources.doneKeys[0]].namespace};

>
        model.enums.values.each { |e| renderEnum model, e, result }
        model.records.values.each { |r| renderRec model, r, result }
        result << "\n"
        result
    end
helpProtobufGen(file, errorText=nil) click to toggle source

Shortcut to help for the Protobuf IDL generator

# File lib/dataMetaProtobuf.rb, line 177
def helpProtobufGen(file, errorText=nil)
    DataMetaDom::help(file, %<DataMeta DOM Protobuf IDL Generation ver #{VERSION}>, '<DataMeta DOM source file>', errorText)
end
protoType(dataMetaType, model, rec) click to toggle source

Converts DataMeta DOM type to Protobuf IDL type.

# File lib/dataMetaProtobuf.rb, line 72
def protoType(dataMetaType, model, rec)
    ty = dataMetaType.type
    if model.records[ty]
        nameSpace, base = assertNamespace(ty)
        base
    elsif model.enums[ty]
        nameSpace, base = assertNamespace(ty)
        base
    else
        renderer = PROTO_TYPES[dataMetaType.type]
        raise "Unsupported type #{dataMetaType}" unless renderer
        renderer.call(dataMetaType)
    end
end
renderEnum(model, enm, result) click to toggle source

Render one single enum

# File lib/dataMetaProtobuf.rb, line 151
    def renderEnum(model, enm, result)
        nameSpace, base = assertNamespace(enm.name)
        L.info("Rendering enum #{enm.name} of the type #{enm.class}")
        case enm
            when DataMetaDom::Enum
                values = enm.keys.map{|k| enm[k]} # sorted by ordinals to preserve the original order
                pbInt = 0
                result << %<
enum #{base} {
>
                values.each{|v|
                    result << "#{INDENT}#{v} = #{pbInt};\n"
                    pbInt += 1
                }
                result << %q<}
>
            when DataMetaDom::Mapping
                # taken care of in renderDer
            else
                raise ArgumentError, %<Unsupported enum type "#{enm.class}" for the name "#{enm.name}">

        end

    end
renderRec(model, rec, result) click to toggle source

Render a single record

# File lib/dataMetaProtobuf.rb, line 121
    def renderRec(model, rec, result)
        nameSpace, base = assertNamespace(rec.name)
        L.info("Rendering record #{rec.name}")
        result << %<
message #{base} {
>
        pbInt = 1
        rec.fields.each_key { | fldId|
            fld = rec.fields[fldId]
            ty = fld.dataType.type
            if fld.aggr && !fld.map?
                result << "#{INDENT}repeated #{protoType(fld.dataType, model, rec)} #{fldId} = #{pbInt};\n"
            elsif fld.map?
                raise ArgumentError, %<Field "#{fldId}" of the rec "#{base}": for Protobuf, map can not be optional, it must be required> unless fld.isRequired
                result << "#{INDENT}map<#{protoType(fld.dataType, model, rec)}, #{protoType(fld.trgType, model, rec)}> #{fldId} = #{pbInt};\n"
            elsif model.enums[ty] && model.enums[ty].is_a?(DataMetaDom::Mapping)
                raise ArgumentError, %<Field "#{fldId}" of the rec "#{base}": for Protobuf, map can not be optional, it must be required> unless fld.isRequired
                result << "#{INDENT}map<#{protoType(model.enums[ty].fromT, model, rec)}, #{protoType(model.enums[ty].toT, model, rec)}> #{fldId} = #{pbInt};\n"
            elsif fld.isRequired
                result << "#{INDENT}#{protoType(fld.dataType, model, rec)} #{fldId} = #{pbInt};\n"
            else
                result << "#{INDENT}repeated #{protoType(fld.dataType, model, rec)} #{fldId} = #{pbInt};\n"
            end
            pbInt += 1
        }
        result << %q<}
>
    end