dataMetaProtobuf 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 091be0378d3394410e5d9bd5a4747083a63b8083
4
+ data.tar.gz: c34ba9da421151b2c3fda17d654db24045a311f7
5
+ SHA512:
6
+ metadata.gz: 732ba6ee73997abb8db2092912b659fddcddd013d7ff842a6fb89afe689cbd47f8466be69dece2e9945b1e1913fb7578751ddd7845479b1965cea032503f454e
7
+ data.tar.gz: f93506ea7441ad5742803559588ba6f46122bcd052704e0bb10a25dc9f64d28b8824e245a379efb7e0e2bde69650dc916694585c129bce8af5ad831ad002c9ee
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --title "DataMeta support for Protobuf" -r README.md --charset UTF-8 lib/**/* - README.md
data/History.md ADDED
@@ -0,0 +1,5 @@
1
+ # `dataMetaProtobuf` Release history:
2
+
3
+ ## `1.0.0` released `2017-02-19 Sun`
4
+ * 1 major enhancement:
5
+ * Initial release
data/PostInstall.txt ADDED
@@ -0,0 +1,2 @@
1
+ No special steps
2
+
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # DataMetaProtobuf
2
+
3
+ DataMeta [Protobuf](https://github.com/google/protobuf/wiki) utilities, such as DataMetaDOM source to
4
+ [Protobuf IDL](https://developers.google.com/protocol-buffers/docs/proto) converter.
5
+
6
+ References to this gem's:
7
+
8
+ * [Source](https://github.com/eBayDataMeta/DataMeta-gems)
9
+
10
+
11
+ ## DESCRIPTION:
12
+
13
+ See the [DataMeta Project](https://github.com/eBayDataMeta/DataMeta).
14
+
15
+ ## FEATURES/PROBLEMS:
16
+
17
+ Protobuf support:
18
+
19
+ * ver `3.2.0` or newer
20
+
21
+ Since Protobuf supports limited subset of the DataMetaDOM features, DataMeta's features that are not supported by
22
+ Protobuf cause an error during export.
23
+
24
+ ## SYNOPSIS:
25
+
26
+ ### Protobuf IDL generator
27
+
28
+ Since DataMeta DOM is superset of Protobuf data types, tradeoffs are made:
29
+
30
+
31
+ * Runnables:
32
+ * `dataMetaProtobufGen.rb` - generate [Protobuf IDL](https://developers.google.com/protocol-buffers/docs/proto)
33
+
34
+ Usage:
35
+
36
+ Pipe your DataMeta DOM source to:
37
+
38
+ dataMetaProtobufGen.rb
39
+
40
+ It will output your Protobuf IDL to STDOUT and write the log file named `dataMetaProtobuf.log` in which you may find
41
+ some useful information about what just happened. This log is written into the current directory.
42
+
43
+ DataMeta type conversions:
44
+
45
+ * `datetime` - Protobuf 3 does have a type named `Timestamp`, but it's new and flakey, therefore we export `datetime` as
46
+ the Protobuf IDL `string` type.
47
+ * Any DataMeta aggregate type except `mapping`: all of `list`, `deque`, `set` are exported as Protobuf's `repeated`.
48
+ * Since Protobuf has dropped support for the "required" vs "optional", we use `repeatable` for the optional fields.
49
+ * Protobuf makes maps non-repeatable, therefore maps must be required in DataMetaDOM.
50
+ * DataMeta DOM's `mapping` translates to Protobuf's `map`. Since Protobuf does not support mapping values,
51
+ therefore DataMeta DOM's `mapping` values are dropped.
52
+
53
+ ## REQUIREMENTS:
54
+
55
+ * No special requirements
56
+
57
+ ## INSTALL:
58
+
59
+ gem install dataMetaProtobuf
60
+
61
+ ## LICENSE:
62
+
63
+ [Apache v 2.0](https://github.com/eBayDataMeta/DataMeta/blob/master/LICENSE.md)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ %w(yard rake/testtask).each{ |r| require r}
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc 'Regen RDocs'
8
+ task :default => :docs
9
+
10
+ YARD::Rake::YardocTask.new('docs') {|r|
11
+ r.stats_options = ['--list-undoc']
12
+ }
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ %w( dataMetaDom dataMetaProtobuf ).each(&method(:require))
4
+
5
+ require 'dataMetaDom'
6
+ require 'dataMetaProtobuf'
7
+
8
+ @source = $*[0]
9
+
10
+ DataMetaProtobuf.helpProtobufGen __FILE__, %q<Argument missing: pass the name of the file with DataMeta DOM source to convert> unless @source
11
+ DataMetaProtobuf.helpProtobufGen __FILE__, %<"#{@source}" is not a valid file> unless File.file?(@source)
12
+
13
+ @model = DataMetaDom::Model.new
14
+ begin
15
+ @model.parse(@source)
16
+ puts "Current source: #{@model.sources.inspect}"
17
+ puts DataMetaProtobuf.genSchema(@model)
18
+ rescue Exception => e
19
+ $stderr.puts "ERROR #{e.message}; #{@model.diagn}"
20
+ $stderr.puts e.backtrace.join("\n\t")
21
+ end
@@ -0,0 +1,182 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require 'erb'
4
+ require 'fileutils'
5
+ require 'dataMetaDom'
6
+ require 'dataMetaDom/help'
7
+ require 'dataMetaDom/pojo'
8
+ require 'dataMetaDom/record'
9
+ require 'dataMetaDom/util'
10
+ require 'ostruct'
11
+
12
+ =begin rdoc
13
+ DataMetaDOM and {Protobuf}[https://github.com/google/protobuf/wiki] {IDL}[https://developers.google.com/protocol-buffers/docs/proto].
14
+
15
+ For command line details either check the new method's source or the README, the usage section.
16
+ =end
17
+
18
+ module DataMetaProtobuf
19
+ # Current version
20
+ VERSION = '1.0.0'
21
+
22
+ # First level indent
23
+ INDENT = ' ' * 4
24
+
25
+ # Since we are piping source in and spilling the result into the STDOUT, need logger for any other output.
26
+ L = Logger.new('dataMetaProtobuf.log', 0, 10_000_000)
27
+ L.level = Logger::INFO
28
+ L.datetime_format = '%Y-%m-%d %H:%M:%S'
29
+
30
+ # The root of the gem.
31
+ GEM_ROOT = File.realpath(File.dirname(__FILE__) + '/../')
32
+
33
+ # Location of templates.
34
+ TMPL_ROOT = File.join(GEM_ROOT, 'tmpl')
35
+
36
+ =begin rdoc
37
+ Mapping from a DataMeta DOM type to a matching renderer of Protobuf IDL.
38
+ =end
39
+ PROTO_TYPES = {
40
+ DataMetaDom::BOOL => lambda{|dt| %q<bool>},
41
+ DataMetaDom::CHAR => lambda{|dt| %q<string>},
42
+ DataMetaDom::INT => lambda{ |dt|
43
+ len = dt.length
44
+ case
45
+ when len <= 4; %q<int32>
46
+ when len <= 8; %q<int64>
47
+ else; raise "Invalid integer length #{len}"
48
+ end
49
+ },
50
+ DataMetaDom::FLOAT => lambda{|dt|
51
+ len = dt.length
52
+ case
53
+ when len <= 4; %q<float>
54
+ when len <= 8; %q<double>
55
+ else; raise "Invalid float length #{len}"
56
+ end
57
+ },
58
+ DataMetaDom::RAW => lambda{|dt| %q<bytes>},
59
+ DataMetaDom::STRING => lambda{|dt| %q<string>},
60
+ =begin
61
+ Unlike DataMeta DOM, Protobuf does not support temporal types such as date, time and datetime, therefore we export
62
+ DataMeta +datetime+ as +string+.
63
+ =end
64
+ DataMetaDom::DATETIME => lambda{|dt| %q<string>},
65
+ # No support for these in this release:
66
+ #NUMERIC => lambda{|t| "BigDecimal"}
67
+ }
68
+
69
+ =begin rdoc
70
+ Converts DataMeta DOM type to Protobuf IDL type.
71
+ =end
72
+ def protoType(dataMetaType, model, rec)
73
+ ty = dataMetaType.type
74
+ if model.records[ty]
75
+ nameSpace, base = assertNamespace(ty)
76
+ base
77
+ elsif model.enums[ty]
78
+ nameSpace, base = assertNamespace(ty)
79
+ base
80
+ else
81
+ renderer = PROTO_TYPES[dataMetaType.type]
82
+ raise "Unsupported type #{dataMetaType}" unless renderer
83
+ renderer.call(dataMetaType)
84
+ end
85
+ end
86
+
87
+ =begin rdoc
88
+ Splits the full name of a class into the namespace and the base, returns an array of
89
+ the namespace (empty string if there is no namespace on the name) and the base name.
90
+
91
+ Examples:
92
+ * <tt>'BaseNameAlone'</tt> -> <tt>['', 'BaseNameAlone']</tt>
93
+ * <tt>'one.package.another.pack.FinallyTheName'</tt> -> <tt>['one.package.another.pack', 'FinallyTheName']</tt>
94
+ =end
95
+ def assertNamespace(fullName)
96
+ ns, base = DataMetaDom::splitNameSpace(fullName)
97
+ [DataMetaDom.validNs?(ns, base) ? ns : '', base]
98
+ end
99
+
100
+ =begin rdoc
101
+ Generates the {Protobuf IDL}[https://developers.google.com/protocol-buffers/docs/proto], returns the source.
102
+
103
+ We decided against using the template and use simple string concatenation instead; because the way Protobuf IDL is
104
+ designed, it's easier to do the string concat. For example, rendering a message in a message in a message...
105
+ =end
106
+ def genSchema(model)
107
+ result = %<
108
+ // This Protobuf schema is generated by DataMeta exporter.
109
+
110
+ syntax = "proto3";
111
+ package #{model.sources[model.sources.doneKeys[0]].namespace};
112
+
113
+ >
114
+ model.enums.values.each { |e| renderEnum model, e, result }
115
+ model.records.values.each { |r| renderRec model, r, result }
116
+ result << "\n"
117
+ result
118
+ end
119
+
120
+ # Render a single record
121
+ def renderRec(model, rec, result)
122
+ nameSpace, base = assertNamespace(rec.name)
123
+ L.info("Rendering record #{rec.name}")
124
+ result << %<
125
+ message #{base} {
126
+ >
127
+ pbInt = 1
128
+ rec.fields.each_key { | fldId|
129
+ fld = rec.fields[fldId]
130
+ ty = fld.dataType.type
131
+ if fld.aggr && !fld.map?
132
+ result << "#{INDENT}repeated #{protoType(fld.dataType, model, rec)} #{fldId} = #{pbInt};\n"
133
+ elsif fld.map?
134
+ raise ArgumentError, %<Field "#{fldId}" of the rec "#{base}": for Protobuf, map can not be optional, it must be required> unless fld.isRequired
135
+ result << "#{INDENT}map<#{protoType(fld.dataType, model, rec)}, #{protoType(fld.trgType, model, rec)}> #{fldId} = #{pbInt};\n"
136
+ elsif model.enums[ty] && model.enums[ty].is_a?(DataMetaDom::Mapping)
137
+ raise ArgumentError, %<Field "#{fldId}" of the rec "#{base}": for Protobuf, map can not be optional, it must be required> unless fld.isRequired
138
+ result << "#{INDENT}map<#{protoType(model.enums[ty].fromT, model, rec)}, #{protoType(model.enums[ty].toT, model, rec)}> #{fldId} = #{pbInt};\n"
139
+ elsif fld.isRequired
140
+ result << "#{INDENT}#{protoType(fld.dataType, model, rec)} #{fldId} = #{pbInt};\n"
141
+ else
142
+ result << "#{INDENT}repeated #{protoType(fld.dataType, model, rec)} #{fldId} = #{pbInt};\n"
143
+ end
144
+ pbInt += 1
145
+ }
146
+ result << %q<}
147
+ >
148
+ end
149
+
150
+ # Render one single enum
151
+ def renderEnum(model, enm, result)
152
+ nameSpace, base = assertNamespace(enm.name)
153
+ L.info("Rendering enum #{enm.name} of the type #{enm.class}")
154
+ case enm
155
+ when DataMetaDom::Enum
156
+ values = enm.keys.map{|k| enm[k]} # sorted by ordinals to preserve the original order
157
+ pbInt = 0
158
+ result << %<
159
+ enum #{base} {
160
+ >
161
+ values.each{|v|
162
+ result << "#{INDENT}#{v} = #{pbInt};\n"
163
+ pbInt += 1
164
+ }
165
+ result << %q<}
166
+ >
167
+ when DataMetaDom::Mapping
168
+ # taken care of in renderDer
169
+ else
170
+ raise ArgumentError, %<Unsupported enum type "#{enm.class}" for the name "#{enm.name}">
171
+
172
+ end
173
+
174
+ end
175
+
176
+ # Shortcut to help for the Protobuf IDL generator
177
+ def helpProtobufGen(file, errorText=nil)
178
+ DataMetaDom::help(file, %<DataMeta DOM Protobuf IDL Generation ver #{VERSION}>, '<DataMeta DOM source file>', errorText)
179
+ end
180
+
181
+ module_function :helpProtobufGen, :genSchema, :assertNamespace, :protoType, :renderRec, :renderEnum
182
+ end
data/test/sample.dmDom ADDED
@@ -0,0 +1,68 @@
1
+ # DataMetaDOM with all field types accounted for testing.
2
+ namespace org.ebay.datameta.examples.conv.protobuf
3
+
4
+ ver 1.0.0
5
+
6
+ enum BaseColor
7
+ Red, Green, Blue
8
+ end
9
+
10
+ mapping Timings string[3] datetime
11
+ "now" => DateTime.now,
12
+ 'then' => DateTime.parse("2012-09-29T23:45:59Z")
13
+ end
14
+
15
+ mapping Depths string[16] float[3]
16
+ "shallow" => 0.1,
17
+ "medium" => 353.232,
18
+ "deep" => 787.0
19
+ end
20
+
21
+ record AllTypes
22
+ +int[4] id
23
+ +int[8] count
24
+ +bool isIt
25
+ +char[5] code
26
+ -float[4] width
27
+ +float[8] height
28
+ +string anyLength
29
+ +string[16] name
30
+ +set{string} aliases
31
+ +deque{datetime} accesses
32
+ +list{int[4]} quants
33
+ +map{string[32], int[4]} strToInt
34
+ +map{int[8], string[40]} longToString
35
+ +Depths depths
36
+ +Timings lengths
37
+ +BaseColor color
38
+ +Includable inclo
39
+ identity id
40
+ end
41
+
42
+ # Check how optional fields are rendered
43
+ record Optionals
44
+ +int[4] id
45
+ -int[8] count
46
+ -bool isIt
47
+ -char[5] code
48
+ -float[4] width
49
+ +float[8] height
50
+ -string anyLength
51
+ +string[16] name
52
+ -set{string} aliases
53
+ -deque{datetime} accesses
54
+ -list{int[4]} quants
55
+ +map{string[32], int[4]} strToInt
56
+ +Depths depths
57
+ +Timings lengths
58
+ -BaseColor color
59
+ -Includable inclo
60
+ identity id
61
+ end
62
+
63
+ record Includable
64
+ +int[8] incId
65
+ +string[32] name
66
+ +float[4] amount
67
+ end
68
+
@@ -0,0 +1,53 @@
1
+ # keep this underscore naming in the test subdir, it's easier to append files names to test
2
+ require './test/test_helper.rb'
3
+ require 'google/protobuf'
4
+ require 'open3'
5
+
6
+ # Unit test cases for the DataMetaProtobuf
7
+ # See for instance:
8
+ # - test_full
9
+ class TestNewGem < Test::Unit::TestCase
10
+ H1 ||= '*' * 15
11
+
12
+ L = Logger.new('dataMetaProtobufTests.log', 0, 10_000_000)
13
+ L.level = Logger::DEBUG
14
+ L.datetime_format = '%Y-%m-%d %H:%M:%S'
15
+
16
+ MODEL_BASE = 'sample'
17
+ GEN_TARGET = '.tmp'
18
+ PROTO_SRC = File.join(GEN_TARGET, "#{MODEL_BASE}.proto")
19
+
20
+ # an empty stub for now
21
+ def setup; end
22
+
23
+ # Smell-check the parsing
24
+ def test_parsing
25
+ model = DataMetaDom::Model.new
26
+ model.parse(File.join(File.dirname(__FILE__), 'sample.dmDom'), options={autoNsVer: true})
27
+ L.info(%<Model: #{model}>)
28
+ FileUtils.rmtree(GEN_TARGET) if File.exist?(GEN_TARGET)
29
+ FileUtils.mkpath GEN_TARGET
30
+ IO.write(PROTO_SRC, DataMetaProtobuf.genSchema(model), mode: 'wb')
31
+ cmd = "protoc --ruby_out=. #{PROTO_SRC}" # as counterintuitive it is, but . means actually the dir where the source is found
32
+ L.info(%<Running "#{cmd}">)
33
+ o,e,s= Open3.capture3(cmd, :binmode => true)
34
+ unless s.to_i == 0
35
+ $stderr.puts %|#{H1} OUT #{H1}
36
+ #{o}
37
+ #{H1} ERR #{H1}
38
+ #{e}
39
+ #{H1} state=#{s.inspect}
40
+ |
41
+ raise RuntimeError, %|ERRORS running "#{cmd}"|
42
+ end
43
+
44
+ L.info("Verifying schema #{PROTO_SRC}")
45
+ require "./#{GEN_TARGET}/#{MODEL_BASE}_pb"
46
+ # see if I can use those messages and the enum
47
+ optionals = Org::Ebay::Datameta::Examples::Conv::Protobuf::Optionals.new
48
+ allTypes = Org::Ebay::Datameta::Examples::Conv::Protobuf::AllTypes.new
49
+ red = Org::Ebay::Datameta::Examples::Conv::Protobuf::BaseColor::Red
50
+ blue = Org::Ebay::Datameta::Examples::Conv::Protobuf::BaseColor::Blue
51
+ L.info("Red = #{red}; Blue = #{blue}")
52
+ end
53
+ end
@@ -0,0 +1,8 @@
1
+ ## keep this underscore naming in the test subdir, it's easier to append files names to test
2
+
3
+ require 'test/unit'
4
+ require 'dataMetaDom'
5
+ require 'fileutils'
6
+
7
+ # this is expected to run from the project root, normally by the rake file
8
+ require './lib/dataMetaProtobuf'
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dataMetaProtobuf
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Bergens
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dataMetaDom
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: google-protobuf
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.2'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 3.2.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.2'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.2.0
53
+ description: DataMeta DOM to Protobuf IDL generator
54
+ email: michael.bergens@gmail.com
55
+ executables:
56
+ - dataMetaProtobufGen.rb
57
+ extensions: []
58
+ extra_rdoc_files: []
59
+ files:
60
+ - ".yardopts"
61
+ - History.md
62
+ - PostInstall.txt
63
+ - README.md
64
+ - Rakefile
65
+ - bin/dataMetaProtobufGen.rb
66
+ - lib/dataMetaProtobuf.rb
67
+ - test/sample.dmDom
68
+ - test/test_dataMetaProtobuf.rb
69
+ - test/test_helper.rb
70
+ homepage: https://github.com/eBayDataMeta
71
+ licenses:
72
+ - Apache-2.0
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 2.1.0
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements:
89
+ - No special requirements
90
+ rubyforge_project:
91
+ rubygems_version: 2.5.1
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: DataMeta Protobuf
95
+ test_files:
96
+ - test/test_dataMetaProtobuf.rb