ruby_protobuf 0.0.1

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.
@@ -0,0 +1,133 @@
1
+ require 'stringio'
2
+ require 'protobuf/decoder'
3
+ require 'protobuf/encoder'
4
+ require 'protobuf/field'
5
+
6
+ module Protobuf
7
+ OPTIONS = {}
8
+
9
+ class Message
10
+ class <<self
11
+ attr_reader :fields
12
+
13
+ def extensions(range)
14
+ raise NotImplementedError('TODO')
15
+ end
16
+
17
+ def required(type, name, tag, opts={})
18
+ define_field :required, type, name, tag, opts
19
+ end
20
+
21
+ def optional(type, name, tag, opts={})
22
+ define_field :optional, type, name, tag, opts
23
+ end
24
+
25
+ def repeated(type, name, tag, opts={})
26
+ define_field :repeated, type, name, tag, opts
27
+ end
28
+
29
+ def define_field(rule, type, name, tag, opts={})
30
+ (@fields ||= {})[tag] = Protobuf::Field.build self, rule, type, name, tag, opts
31
+ end
32
+
33
+ def get_field_by_name(name)
34
+ @fields.values.find {|field| field.name == name.to_sym}
35
+ end
36
+
37
+ def get_field_by_tag(tag)
38
+ @fields[tag]
39
+ end
40
+
41
+ def get_field(tag_or_name)
42
+ case tag_or_name
43
+ when Integer
44
+ get_field_by_tag tag_or_name
45
+ when String, Symbol
46
+ get_field_by_name tag_or_name
47
+ else
48
+ raise TypeError
49
+ end
50
+ end
51
+ end
52
+
53
+ def initialize
54
+ fields.each do |tag, field|
55
+ field.define_accessor self
56
+ end
57
+ end
58
+
59
+ def parse_from_string(string)
60
+ parse_from StringIO.new(string)
61
+ end
62
+
63
+ def parse_from_file(filename)
64
+ if filename.is_a? File
65
+ parse_from filename
66
+ else
67
+ File.open(filename, 'r') do |f|
68
+ parse_from f
69
+ end
70
+ end
71
+ end
72
+
73
+ def parse_from(stream)
74
+ Protobuf::Decoder.decode stream, self
75
+ end
76
+
77
+ def serialize_to_string(string='')
78
+ io = StringIO.new string
79
+ serialize_to io
80
+ io.string
81
+ end
82
+
83
+ def serialize_to_file(filename)
84
+ if filename.is_a? File
85
+ serialize_to filename
86
+ else
87
+ File.open(filename, 'w') do |f|
88
+ serialize_to f
89
+ end
90
+ end
91
+ end
92
+
93
+ def serialize_to(stream)
94
+ Protobuf::Encoder.encode stream, self
95
+ end
96
+
97
+ def set_field(tag, bytes)
98
+ get_field_by_tag(tag).set self, bytes
99
+ end
100
+
101
+ def merge_field(tag, value)
102
+ # TODO
103
+ #get_field_by_tag(tag).merge self, bytes
104
+ end
105
+
106
+ def [](tag_or_name)
107
+ if field = get_field(tag_or_name)
108
+ send field.name
109
+ else
110
+ raise NoMethodError.new("No such method: #{tag_or_name}")
111
+ end
112
+ end
113
+
114
+ def []=(tag_or_name, value)
115
+ if field = get_field(tag_or_name) and not field.repeated?
116
+ send "#{field.name}=", value
117
+ else
118
+ raise NoMethodError.new("No such method: #{tag_or_name}=")
119
+ end
120
+ end
121
+
122
+ def fields; self.class.fields end
123
+ def get_field_by_name(name); self.class.get_field_by_name(name) end
124
+ def get_field_by_tag(tag); self.class.get_field_by_tag(tag) end
125
+ def get_field(tag_or_name); self.class.get_field(tag_or_name) end
126
+
127
+ def each_field(&block)
128
+ fields.to_a.sort{|(t1, f1), (t2, f2)| t1 <=> t2}.each do |tag, field|
129
+ block.call field, self[tag]
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,138 @@
1
+ class Protobuf::Parser
2
+ rule
3
+ stmt_list : stmt
4
+ | stmt_list stmt
5
+
6
+ stmt : package_stmt
7
+ | import_stmt
8
+ | option_stmt
9
+ | message_def
10
+ | extend_def
11
+ | enum_def
12
+ | service_def
13
+
14
+ package_stmt : PACKAGE fqcn_package ';'
15
+
16
+ fqcn_package : ident
17
+ | fqcn_package '.' ident
18
+
19
+ import_stmt : IMPORT STRING ';'
20
+
21
+ option_stmt : OPTION ident '=' STRING ';'
22
+ | OPTION ident '=' ident ';'
23
+
24
+ message_def : MESSAGE ident '{' message_item_list '}'
25
+
26
+ message_item_list : message_item
27
+ | message_item_list message_item
28
+
29
+ message_item : RULE type ident '=' INTEGER ';'
30
+ | RULE type ident '=' INTEGER '[' DEFAULT '=' ident ']' ';'
31
+ | RULE type ident '=' INTEGER '[' DEFAULT '=' number ']' ';'
32
+ | option_stmt
33
+ | extend_stmt
34
+ | message_def
35
+ | extend_def
36
+ | enum_def
37
+
38
+ type : TYPE | IDENT
39
+
40
+ number : INTEGER | FLOAT
41
+
42
+ extend_stmt | EXTENSIONS INTEGER TO INTEGER ';'
43
+
44
+ extend_def : EXTEND ident '{' message_item_list '}'
45
+
46
+ enum_def : ENUM ident '{' enum_item_list '}'
47
+
48
+ enum_item_list : enum_item
49
+ | enum_item_list enum_item
50
+
51
+ enum_item : ident '=' INTEGER ';'
52
+
53
+ service_def : SERVICE ident '{' RPC ident '(' ident ')' RETURNS '(' ident ')' '}' ';'
54
+
55
+ ident : PACKAGE | IDENT | MESSAGE | RULE | TYPE | EXTENSIONS | EXTEND | ENUM | SERVICE | RPC | RETURNS | TO | DEFAULT
56
+
57
+ ---- inner
58
+
59
+ def initialize
60
+ @indent = 0
61
+ end
62
+
63
+ RESERVED = {
64
+ 'package' => :PACKAGE,
65
+ 'message' => :MESSAGE,
66
+ 'extensions' => :EXTENSIONS,
67
+ 'extend' => :EXTEND,
68
+ 'enum' => :ENUM,
69
+ 'service' => :SERVICE,
70
+ 'rpc' => :RPC,
71
+ 'returns' => :RETURNS,
72
+ 'to' => :TO,
73
+ 'default' => :DEFAULT,
74
+ 'required' => :RULE,
75
+ 'optional' => :RULE,
76
+ 'repeated' => :RULE,
77
+ 'double' => :TYPE,
78
+ 'float' => :TYPE,
79
+ 'int32' => :TYPE,
80
+ 'int64' => :TYPE,
81
+ 'uint32' => :TYPE,
82
+ 'uint64' => :TYPE,
83
+ 'sint32' => :TYPE,
84
+ 'sint64' => :TYPE,
85
+ 'fixed32' => :TYPE,
86
+ 'fixed64' => :TYPE,
87
+ 'sfixed32' => :TYPE,
88
+ 'sfixed64' => :TYPE,
89
+ 'bool' => :TYPE,
90
+ 'string' => :TYPE,
91
+ 'bytes' => :TYPE,
92
+ }
93
+
94
+ def parse(f)
95
+ @q = []
96
+ lineno = 1
97
+ f.each do |line|
98
+ line.strip!
99
+ until line.empty? do
100
+ case line
101
+ when /\A\s+/, /\A\/\/.*/
102
+ ;
103
+ when /\A[a-zA-Z_]\w*/
104
+ word = $&
105
+ @q.push [RESERVED[word] || :IDENT, [lineno, word.to_sym]]
106
+ when /\A\d+\.\d+/
107
+ @q.push [:FLOAT, [lineno, $&.to_f]]
108
+ when /\A\d+/
109
+ @q.push [:INTEGER, [lineno, $&.to_i]]
110
+ when /\A"(?:[^"\\]+|\\.)*"/, /\A'(?:[^'\\]+|\\.)*'/
111
+ @q.push [:STRING, [lineno, eval($&)]]
112
+ when /\A./
113
+ @q.push [$&, [lineno, $&]]
114
+ else
115
+ raise RuntimeError, 'must not happen'
116
+ end
117
+ line = $'
118
+ end
119
+ lineno += 1
120
+ end
121
+ @q.push [false, '$']
122
+
123
+ do_parse
124
+ end
125
+
126
+ def next_token
127
+ @q.shift
128
+ end
129
+
130
+ def on_error(t, v, values)
131
+ raise Racc::ParseError, "syntax error on #{v[1].inspect} at line.#{v[0]}"
132
+ end
133
+
134
+ ---- footer
135
+
136
+ File.open(ARGV.shift, 'r') do |f|
137
+ Protobuf::Parser.new.parse f
138
+ end
@@ -0,0 +1,7 @@
1
+ module Protobuf
2
+ class Service
3
+ def self.rpc(hash)
4
+ raise NotImplementedError('TODO')
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module Protobuf
2
+ class WireType
3
+ VARINT = 0
4
+ FIXED64 = 1
5
+ LENGTH_DELIMITED = 2
6
+ START_GROUP = 3
7
+ END_GROUP = 4
8
+ FIXED32 = 5
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ require 'protobuf/compiler'
2
+
3
+ class RubyProtobuf
4
+ VERSION = '0.0.1'
5
+
6
+ def start(proto_file, options)
7
+ unless File.exist?(proto_file)
8
+ if File.exist? "#{proto_file}.proto"
9
+ proto_file = "#{proto_file}.proto"
10
+ else
11
+ raise ArgumentError.new("#{proto_file} does not exist.")
12
+ end
13
+ end
14
+ rb_filename = File.basename proto_file
15
+ rb_filename += '.rb' unless rb_filename.sub!(/.\w+$/, '.rb')
16
+ rb_filepath = "#{options[:out] || '.'}/#{rb_filename}"
17
+ puts "#{rb_filepath} writting..."
18
+ File.open(rb_filepath, 'w') do |f|
19
+ f.write Protobuf::Compiler.compile(proto_file)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ package tutorial;
2
+
3
+ message Person {
4
+ required string name = 1;
5
+ required int32 id = 2;
6
+ optional string email = 3;
7
+
8
+ enum PhoneType {
9
+ MOBILE = 0;
10
+ HOME = 1;
11
+ WORK = 2;
12
+ }
13
+
14
+ message PhoneNumber {
15
+ required string number = 1;
16
+ optional PhoneType type = 2 [default = HOME];
17
+ }
18
+
19
+ repeated PhoneNumber phone = 4;
20
+ }
21
+
22
+ message AddressBook {
23
+ repeated Person person = 1;
24
+ }
@@ -0,0 +1,41 @@
1
+ require 'protobuf/message'
2
+ require 'protobuf/enum'
3
+ require 'protobuf/service'
4
+ require 'protobuf/extend'
5
+
6
+ module Tutorial
7
+ class Person < Protobuf::Message
8
+ required :string, :name, 1
9
+ required :int32, :id, 2
10
+ optional :string, :email, 3
11
+
12
+ class PhoneType < Protobuf::Enum
13
+ MOBILE = 0
14
+ HOME = 1
15
+ WORK = 2
16
+ end
17
+
18
+ class PhoneNumber < Protobuf::Message
19
+ required :string, :number, 1
20
+ optional :PhoneType, :type, 2, {:default => :HOME}
21
+ end
22
+
23
+ repeated :PhoneNumber, :phone, 4
24
+
25
+ #extensions 100..199
26
+ #class Foo < Protobuf::Extend
27
+ # optional :int32, :bar, 126
28
+ #end
29
+ end
30
+
31
+ class AddressBook < Protobuf::Message
32
+ repeated :Person, :person, 1
33
+ end
34
+
35
+ #class SearchService < Protobuf::Service
36
+ # rpc :Search => :SearchRequest, :returns => :SearchResponse
37
+ #end
38
+
39
+ #Protobuf::OPTIONS[:optimize_for] = :SPEED
40
+ #Protobuf::OPTIONS[:java_package] = :'com.example.foo'
41
+ end
@@ -0,0 +1,3 @@
1
+
2
+ John Doe� jdoe@example.com"
3
+ 555-4321
@@ -0,0 +1,14 @@
1
+ import addressbook_pb2
2
+ import sys
3
+
4
+ person = addressbook_pb2.Person()
5
+ person.id = 1234
6
+ person.name = "John Doe"
7
+ person.email = "jdoe@example.com"
8
+ phone = person.phone.add()
9
+ phone.number = "555-4321"
10
+ phone.type = addressbook_pb2.Person.HOME
11
+
12
+ f = open('data.bin', 'wb')
13
+ f.write(person.SerializeToString())
14
+ f.close()
Binary file
@@ -0,0 +1,22 @@
1
+ import types_pb2
2
+ import sys
3
+
4
+ types = types_pb2.TestTypes()
5
+ types.type1 = 0.01
6
+ types.type2 = 0.1
7
+ types.type3 = 1
8
+ types.type4 = 10
9
+ types.type5 = 100
10
+ types.type6 = 1000
11
+ types.type7 = -1
12
+ types.type8 = -10
13
+ types.type9 = 10000
14
+ types.type10 = 100000
15
+ types.type11 = False
16
+ types.type12 = 'hello all types'
17
+ # TODO test type13
18
+ #types.type13 =
19
+
20
+ f = open('types.bin', 'wb')
21
+ f.write(types.SerializeToString())
22
+ f.close()
@@ -0,0 +1,41 @@
1
+ require 'test/unit'
2
+ require 'protobuf/message'
3
+ require 'protobuf/enum'
4
+ require 'test/addressbook'
5
+
6
+ class AddressbookTest < Test::Unit::TestCase
7
+ def test_enum
8
+ phone_number = Tutorial::Person::PhoneNumber.new
9
+ phone_number.type = Tutorial::Person::PhoneType::MOBILE
10
+ assert_equal 0, phone_number.type
11
+ phone_number.type = Tutorial::Person::PhoneType::HOME
12
+ assert_equal 1, phone_number.type
13
+ phone_number.type = Tutorial::Person::PhoneType::WORK
14
+ assert_equal 2, phone_number.type
15
+ assert_raise TypeError do
16
+ phone_number.type = 3
17
+ end
18
+ end
19
+
20
+ def test_initial_value
21
+ person = Tutorial::Person.new
22
+ assert_equal '', person.name
23
+ assert_equal 0, person.id
24
+ assert_equal [], person.phone
25
+ end
26
+
27
+ def test_repeatable
28
+ address_book = Tutorial::AddressBook.new
29
+ assert_equal [], address_book.person
30
+ assert_instance_of Protobuf::Field::FieldArray, address_book.person
31
+ address_book.person << Tutorial::Person.new
32
+ assert_equal 1, address_book.person.size
33
+ assert_raise TypeError do
34
+ address_book.person << 1
35
+ end
36
+ assert_equal 1, address_book.person.size
37
+ address_book.person << Tutorial::Person.new
38
+ assert_equal 2, address_book.person.size
39
+ end
40
+ end
41
+