dataMetaProtobuf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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