prepor-beefcake 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 +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +161 -0
- data/RELEASE_NOTES.md +38 -0
- data/Rakefile +12 -0
- data/beefcake.gemspec +25 -0
- data/bench/simple.rb +116 -0
- data/bin/protoc-gen-beefcake +9 -0
- data/dat/code_generator_request.dat +0 -0
- data/lib/beefcake.rb +283 -0
- data/lib/beefcake/buffer.rb +2 -0
- data/lib/beefcake/buffer/base.rb +111 -0
- data/lib/beefcake/buffer/decode.rb +105 -0
- data/lib/beefcake/buffer/encode.rb +125 -0
- data/lib/beefcake/generator.rb +313 -0
- data/lib/beefcake/version.rb +3 -0
- data/test/benchmark_test.rb +42 -0
- data/test/buffer_decode_test.rb +117 -0
- data/test/buffer_encode_test.rb +234 -0
- data/test/buffer_test.rb +45 -0
- data/test/generator_test.rb +46 -0
- data/test/message_test.rb +446 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c84028ca26869888306b5bf5c48b48dfb79909b2
|
4
|
+
data.tar.gz: 7396b01b5432a3452ec5d59b3f19e3b140c66e19
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ffea398a17e0efa2bf8e6278c895caea789a086aff759ebb06bd57f25fff28da763fce39ac618c2cc13a6b814897079257322d6973bf330bc8e1ade0953a980a
|
7
|
+
data.tar.gz: 719b223605e7c5acaa0b795df704caf16576478cea4dd870225f7118d31613ac171f9ca62601fc242d0ba91d33942e9c919451d486038a6059f80defc8db4e67
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2011 Blake Mizerany
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
# Beefcake
|
2
|
+
|
3
|
+
A sane Google Protocol Buffers library for Ruby. It's all about being Buf;
|
4
|
+
ProtoBuf.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
```shell
|
9
|
+
gem install beefcake
|
10
|
+
```
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
require 'beefcake'
|
16
|
+
|
17
|
+
class Variety
|
18
|
+
include Beefcake::Message
|
19
|
+
|
20
|
+
# Required
|
21
|
+
required :x, :int32, 1
|
22
|
+
required :y, :int32, 2
|
23
|
+
|
24
|
+
# Optional
|
25
|
+
optional :tag, :string, 3
|
26
|
+
|
27
|
+
# Repeated
|
28
|
+
repeated :ary, :fixed64, 4
|
29
|
+
repeated :pary, :fixed64, 5, :packed => true
|
30
|
+
|
31
|
+
# Enums - Simply use a Module (NOTE: defaults are optional)
|
32
|
+
module Foonum
|
33
|
+
A = 1
|
34
|
+
B = 2
|
35
|
+
end
|
36
|
+
|
37
|
+
# As per the spec, defaults are only set at the end
|
38
|
+
# of decoding a message, not on object creation.
|
39
|
+
optional :foo, Foonum, 6, :default => Foonum::B
|
40
|
+
end
|
41
|
+
|
42
|
+
# You can create a new message with hash arguments:
|
43
|
+
x = Variety.new(:x => 1, :y => 2)
|
44
|
+
|
45
|
+
# You can set fields individually using accessor methods:
|
46
|
+
x = Variety.new
|
47
|
+
x.x = 1
|
48
|
+
x.y = 2
|
49
|
+
|
50
|
+
# And you can access fields using Hash syntax:
|
51
|
+
x[:x] # => 1
|
52
|
+
x[:y] = 4
|
53
|
+
x # => <Variety x: 1, y: 4>
|
54
|
+
```
|
55
|
+
|
56
|
+
### Encoding
|
57
|
+
|
58
|
+
Any object responding to `<<` can accept encoding
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
# see code example above for the definition of Variety
|
62
|
+
x = Variety.new(:x => 1, :y => 2)
|
63
|
+
|
64
|
+
# For example, you can encode into a String:
|
65
|
+
s = ""
|
66
|
+
x.encode(s)
|
67
|
+
s # => "\b\x01\x10\x02)\0"
|
68
|
+
|
69
|
+
# If you don't encode into anything, a new Beefcake::Buffer will be returned:
|
70
|
+
x.encode # => #<Beefcake::Buffer:0x007fbfe1867ab0 @buf="\b\x01\x10\x02)\0">
|
71
|
+
|
72
|
+
# And that buffer can be converted to a String:
|
73
|
+
x.encode.to_s # => "\b\x01\x10\x02)\0"
|
74
|
+
```
|
75
|
+
|
76
|
+
### Decoding
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
# see code example above for the definition of Variety
|
80
|
+
x = Variety.new(:x => 1, :y => 2)
|
81
|
+
|
82
|
+
# You can decode from a Beefcake::Buffer
|
83
|
+
encoded = x.encode
|
84
|
+
Variety.decode(encoded) # => <Variety x: 1, y: 2, pary: [], foo: B(2)>
|
85
|
+
|
86
|
+
# Decoding from a String works the same way:
|
87
|
+
Variety.decode(encoded.to_s) # => <Variety x: 1, y: 2, pary: [], foo: B(2)>
|
88
|
+
|
89
|
+
# You can update a Beefcake::Message instance with new data too:
|
90
|
+
new_data = Variety.new(x: 12345, y: 2).encode
|
91
|
+
Variety.decoded(new_data, x)
|
92
|
+
x # => <Variety x: 12345, y: 2, pary: [], foo: B(2)>
|
93
|
+
```
|
94
|
+
|
95
|
+
### Generate code from `.proto` file
|
96
|
+
|
97
|
+
```shell
|
98
|
+
protoc --beefcake_out output/path -I path/to/proto/files/dir path/to/file.proto
|
99
|
+
```
|
100
|
+
|
101
|
+
You can set the `BEEFCAKE_NAMESPACE` variable to generate the classes under a
|
102
|
+
desired namespace. (i.e. App::Foo::Bar)
|
103
|
+
|
104
|
+
## About
|
105
|
+
|
106
|
+
Ruby deserves and needs first-class ProtoBuf support. Other libs didn't feel
|
107
|
+
very "Ruby" to me and were hard to parse.
|
108
|
+
|
109
|
+
This library was built with EventMachine in mind. Not just blocking-IO.
|
110
|
+
|
111
|
+
Source: https://github.com/protobuf-ruby/beefcake
|
112
|
+
|
113
|
+
### Support Features
|
114
|
+
|
115
|
+
* Optional fields
|
116
|
+
* Required fields
|
117
|
+
* Repeated fields
|
118
|
+
* Packed Repeated Fields
|
119
|
+
* Varint fields
|
120
|
+
* 32-bit fields
|
121
|
+
* 64-bit fields
|
122
|
+
* Length-delimited fields
|
123
|
+
* Embedded Messages
|
124
|
+
* Unknown fields are ignored (as per spec)
|
125
|
+
* Enums
|
126
|
+
* Defaults (i.e. `optional :foo, :string, :default => "bar"`)
|
127
|
+
* Varint-encoded length-delimited message streams
|
128
|
+
|
129
|
+
### Future
|
130
|
+
|
131
|
+
* Imports
|
132
|
+
* Use package in generation
|
133
|
+
* Groups (would be nice for accessing older protos)
|
134
|
+
|
135
|
+
### Further Reading
|
136
|
+
|
137
|
+
http://code.google.com/apis/protocolbuffers/docs/encoding.html
|
138
|
+
|
139
|
+
## Testing
|
140
|
+
|
141
|
+
rake test
|
142
|
+
|
143
|
+
Beefcake conducts continuous integration on [Travis CI](http://travis-ci.org).
|
144
|
+
The current build status for HEAD is [](https://travis-ci.org/protobuf-ruby/beefcake).
|
145
|
+
|
146
|
+
All pull requests automatically trigger a build request. Please ensure that
|
147
|
+
tests succeed.
|
148
|
+
|
149
|
+
Currently Beefcake is tested and working on:
|
150
|
+
|
151
|
+
* Ruby 1.9.3
|
152
|
+
* Ruby 2.0.0
|
153
|
+
* Ruby 2.1.0
|
154
|
+
* Ruby 2.1.1
|
155
|
+
* Ruby 2.1.2
|
156
|
+
* JRuby in 1.9 mode
|
157
|
+
|
158
|
+
## Thank You
|
159
|
+
|
160
|
+
* Keith Rarick (kr) for help with encoding/decoding.
|
161
|
+
* Aman Gupta (tmm1) for help with cross VM support and performance enhancements.
|
data/RELEASE_NOTES.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Beefcake Release Notes
|
2
|
+
|
3
|
+
# 0.1.0 - 2014-09-05
|
4
|
+
|
5
|
+
Release 1.0.0 includes changes and improvements.
|
6
|
+
|
7
|
+
* Version numbering now properly semantic.
|
8
|
+
* Ruby 1.8 is no longer supported.
|
9
|
+
* Field number re-use raises a `DuplicateFieldNumber` error.
|
10
|
+
* Checking to see if a type is encodable is much faster.
|
11
|
+
* Fields named `fields` are now supported.
|
12
|
+
* String read and decoding are benchmarked during testing.
|
13
|
+
* `string` fields now decode with a `UTF-8` encoding.
|
14
|
+
|
15
|
+
# 0.5.0 - 2013-12-20
|
16
|
+
|
17
|
+
Release 0.5.0 corrects a few behaviors.
|
18
|
+
|
19
|
+
* Drastically revised README, written by Tobias "grobie" Schmidt
|
20
|
+
* Output fewer newlines in generated files, fixed by Tobias "grobie" Schmidt
|
21
|
+
* Don't crash when attempting to reencode frozen strings,
|
22
|
+
found thanks to Kyle "Aphyr" Kingsbury
|
23
|
+
* Return `nil` instead of raising a generic Ruby error when trying to
|
24
|
+
decode a zero-length buffer, fixed by Tobias "grobie" Schmidt
|
25
|
+
|
26
|
+
# 0.4.0 - 2013-10-10
|
27
|
+
|
28
|
+
Release 0.4.0 is the first with new maintainers.
|
29
|
+
|
30
|
+
* Modernize tests
|
31
|
+
* Add Travis CI monitoring
|
32
|
+
* Support varint-encoded length-delimited buffers
|
33
|
+
* Support generation with recursive definitions
|
34
|
+
* Support Ruby 2.0
|
35
|
+
* Support encoded buffers
|
36
|
+
* Support false but non-nil values
|
37
|
+
* Support top-level enums, added by Kim Altintop:
|
38
|
+
https://github.com/protobuf-ruby/beefcake/pull/23
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << 'minitest'
|
8
|
+
t.test_files = FileList['test/*_test.rb']
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :test
|
data/beefcake.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "beefcake/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "prepor-beefcake"
|
7
|
+
s.version = Beefcake::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Blake Mizerany", "Matt Proud", "Bryce Kerley"]
|
10
|
+
s.email = ["blake.mizerany@gmail.com", "matt.proud@gmail.com", "bkerley@brycekerley.net"]
|
11
|
+
s.homepage = "https://github.com/protobuf-ruby/beefcake"
|
12
|
+
s.summary = %q{A sane protobuf library for Ruby}
|
13
|
+
s.description = %q{A sane protobuf library for Ruby}
|
14
|
+
s.license = 'MIT'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.required_ruby_version = '>= 1.9.3'
|
22
|
+
|
23
|
+
s.add_development_dependency('rake', '~> 10.1.0')
|
24
|
+
s.add_development_dependency('minitest', '~> 5.3')
|
25
|
+
end
|
data/bench/simple.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'beefcake'
|
3
|
+
|
4
|
+
class MyMessage
|
5
|
+
include Beefcake::Message
|
6
|
+
required :number, :int32, 1
|
7
|
+
required :chars, :string, 2
|
8
|
+
required :raw, :bytes, 3
|
9
|
+
required :bool, :bool, 4
|
10
|
+
required :float, :float, 5
|
11
|
+
end
|
12
|
+
|
13
|
+
ITERS = 100_000
|
14
|
+
|
15
|
+
case ARGV[0]
|
16
|
+
when 'pprof'
|
17
|
+
# profile message creation/encoding/decoding w/ perftools.rb
|
18
|
+
# works on 1.8 and 1.9
|
19
|
+
# ruby bench/simple.rb pprof
|
20
|
+
# open bench/beefcake.prof.gif
|
21
|
+
|
22
|
+
ENV['CPUPROFILE_FREQUENCY'] = '4000'
|
23
|
+
require 'rubygems'
|
24
|
+
require 'perftools'
|
25
|
+
PerfTools::CpuProfiler.start(File.expand_path("../beefcake.prof", __FILE__)) do
|
26
|
+
ITERS.times do
|
27
|
+
str = MyMessage.new(
|
28
|
+
:number => 12345,
|
29
|
+
:chars => 'hello',
|
30
|
+
:raw => 'world',
|
31
|
+
:bool => true,
|
32
|
+
:float => 1.2345
|
33
|
+
).encode
|
34
|
+
MyMessage.decode(str)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
Dir.chdir(File.dirname(__FILE__)) do
|
38
|
+
`pprof.rb beefcake.prof --gif > beefcake.prof.gif`
|
39
|
+
end
|
40
|
+
|
41
|
+
when 'ruby-prof'
|
42
|
+
# profile message creation/encoding/decoding w/ ruby-prof
|
43
|
+
# works on 1.8 and 1.9
|
44
|
+
# ruby bench/simple.rb ruby-prof
|
45
|
+
# open bench/beefcake.prof.html
|
46
|
+
|
47
|
+
require 'ruby-prof'
|
48
|
+
result = RubyProf.profile do
|
49
|
+
ITERS.times do
|
50
|
+
str = MyMessage.new(
|
51
|
+
:number => 12345,
|
52
|
+
:chars => 'hello',
|
53
|
+
:raw => 'world',
|
54
|
+
:bool => true,
|
55
|
+
:float => 1.2345
|
56
|
+
).encode
|
57
|
+
MyMessage.decode(str)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
filename = File.expand_path('beefcake.prof.html', File.dirname(__FILE__))
|
62
|
+
File.open(filename, 'w') do |file|
|
63
|
+
RubyProf::GraphHtmlPrinter.new(result).print(file)
|
64
|
+
end
|
65
|
+
|
66
|
+
else
|
67
|
+
# benchmark message creation/encoding/decoding
|
68
|
+
# rvm install 1.8.7 1.9.2 jruby rbx
|
69
|
+
# rvm 1.8.7,1.9.2,jruby,rbx ruby bench/simple.rb
|
70
|
+
|
71
|
+
require 'benchmark'
|
72
|
+
|
73
|
+
Benchmark.bmbm do |x|
|
74
|
+
x.report 'object creation' do
|
75
|
+
ITERS.times do
|
76
|
+
Object.new
|
77
|
+
end
|
78
|
+
end
|
79
|
+
x.report 'message creation' do
|
80
|
+
ITERS.times do
|
81
|
+
MyMessage.new(
|
82
|
+
:number => 12345,
|
83
|
+
:chars => 'hello',
|
84
|
+
:raw => 'world',
|
85
|
+
:bool => true,
|
86
|
+
:float => 1.2345
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
x.report 'message encoding' do
|
91
|
+
m = MyMessage.new(
|
92
|
+
:number => 12345,
|
93
|
+
:chars => 'hello',
|
94
|
+
:raw => 'world',
|
95
|
+
:bool => true,
|
96
|
+
:float => 1.2345
|
97
|
+
)
|
98
|
+
ITERS.times do
|
99
|
+
m.encode
|
100
|
+
end
|
101
|
+
end
|
102
|
+
x.report 'message decoding' do
|
103
|
+
str = MyMessage.new(
|
104
|
+
:number => 12345,
|
105
|
+
:chars => 'hello',
|
106
|
+
:raw => 'world',
|
107
|
+
:bool => true,
|
108
|
+
:float => 1.2345
|
109
|
+
).encode.to_s
|
110
|
+
ITERS.times do
|
111
|
+
MyMessage.decode(str.dup)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
Binary file
|
data/lib/beefcake.rb
ADDED
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'beefcake/buffer'
|
2
|
+
|
3
|
+
module Beefcake
|
4
|
+
module Message
|
5
|
+
|
6
|
+
class WrongTypeError < StandardError
|
7
|
+
def initialize(name, exp, got)
|
8
|
+
super("Wrong type `#{got}` given for (#{name}). Expected #{exp}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
class InvalidValueError < StandardError
|
14
|
+
def initialize(name, val)
|
15
|
+
super("Invalid Value given for `#{name}`: #{val.inspect}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
class RequiredFieldNotSetError < StandardError
|
21
|
+
def initialize(name)
|
22
|
+
super("Field #{name} is required but nil")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class DuplicateFieldNumber < StandardError
|
27
|
+
def initialize(num, name)
|
28
|
+
super("Field number #{num} (#{name}) was already used")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Field < Struct.new(:rule, :name, :type, :fn, :opts)
|
33
|
+
def <=>(o)
|
34
|
+
fn <=> o.fn
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
module Dsl
|
40
|
+
def required(name, type, fn, opts={})
|
41
|
+
field(:required, name, type, fn, opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
def repeated(name, type, fn, opts={})
|
45
|
+
field(:repeated, name, type, fn, opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
def optional(name, type, fn, opts={})
|
49
|
+
field(:optional, name, type, fn, opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
def field(rule, name, type, fn, opts)
|
53
|
+
if fields.include?(fn)
|
54
|
+
raise DuplicateFieldNumber.new(fn, name)
|
55
|
+
end
|
56
|
+
fields[fn] = Field.new(rule, name, type, fn, opts)
|
57
|
+
attr_accessor name
|
58
|
+
end
|
59
|
+
|
60
|
+
def fields
|
61
|
+
@fields ||= {}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module Encode
|
66
|
+
|
67
|
+
def encode(buf = Buffer.new)
|
68
|
+
validate!
|
69
|
+
|
70
|
+
if ! buf.respond_to?(:<<)
|
71
|
+
raise ArgumentError, "buf doesn't respond to `<<`"
|
72
|
+
end
|
73
|
+
|
74
|
+
if ! buf.is_a?(Buffer)
|
75
|
+
buf = Buffer.new(buf)
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: Error if any required fields at nil
|
79
|
+
|
80
|
+
__beefcake_fields__.values.sort.each do |fld|
|
81
|
+
if fld.opts[:packed]
|
82
|
+
bytes = encode!(Buffer.new, fld, 0)
|
83
|
+
buf.append_info(fld.fn, Buffer.wire_for(fld.type))
|
84
|
+
buf.append_uint64(bytes.length)
|
85
|
+
buf << bytes
|
86
|
+
else
|
87
|
+
encode!(buf, fld, fld.fn)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
buf
|
92
|
+
end
|
93
|
+
|
94
|
+
def encode!(buf, fld, fn)
|
95
|
+
v = self[fld.name]
|
96
|
+
v = v.is_a?(Array) ? v : [v]
|
97
|
+
|
98
|
+
v.compact.each do |val|
|
99
|
+
case fld.type
|
100
|
+
when Class # encodable
|
101
|
+
# TODO: raise error if type != val.class
|
102
|
+
buf.append(:string, val.encode, fn)
|
103
|
+
when Module # enum
|
104
|
+
if ! valid_enum?(fld.type, val)
|
105
|
+
raise InvalidValueError.new(fld.name, val)
|
106
|
+
end
|
107
|
+
|
108
|
+
buf.append(:int32, val, fn)
|
109
|
+
else
|
110
|
+
buf.append(fld.type, val, fn)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
buf
|
115
|
+
end
|
116
|
+
|
117
|
+
def write_delimited(buf = Buffer.new)
|
118
|
+
if ! buf.respond_to?(:<<)
|
119
|
+
raise ArgumentError, "buf doesn't respond to `<<`"
|
120
|
+
end
|
121
|
+
|
122
|
+
if ! buf.is_a?(Buffer)
|
123
|
+
buf = Buffer.new(buf)
|
124
|
+
end
|
125
|
+
|
126
|
+
buf.append_bytes(encode)
|
127
|
+
|
128
|
+
buf
|
129
|
+
end
|
130
|
+
|
131
|
+
def valid_enum?(mod, val)
|
132
|
+
!!name_for(mod, val)
|
133
|
+
end
|
134
|
+
|
135
|
+
def name_for(mod, val)
|
136
|
+
mod.constants.each do |name|
|
137
|
+
if mod.const_get(name) == val
|
138
|
+
return name
|
139
|
+
end
|
140
|
+
end
|
141
|
+
nil
|
142
|
+
end
|
143
|
+
|
144
|
+
def validate!
|
145
|
+
__beefcake_fields__.values.each do |fld|
|
146
|
+
if fld.rule == :required && self[fld.name].nil?
|
147
|
+
raise RequiredFieldNotSetError, fld.name
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
module Decode
|
156
|
+
def decode(buf, o=self.new)
|
157
|
+
if ! buf.is_a?(Buffer)
|
158
|
+
buf = Buffer.new(buf)
|
159
|
+
end
|
160
|
+
|
161
|
+
# TODO: test for incomplete buffer
|
162
|
+
while buf.length > 0
|
163
|
+
fn, wire = buf.read_info
|
164
|
+
|
165
|
+
fld = fields[fn]
|
166
|
+
|
167
|
+
# We don't have a field for with index fn.
|
168
|
+
# Ignore this data and move on.
|
169
|
+
if fld.nil?
|
170
|
+
buf.skip(wire)
|
171
|
+
next
|
172
|
+
end
|
173
|
+
|
174
|
+
exp = Buffer.wire_for(fld.type)
|
175
|
+
if wire != exp
|
176
|
+
raise WrongTypeError.new(fld.name, exp, wire)
|
177
|
+
end
|
178
|
+
|
179
|
+
if fld.rule == :repeated && fld.opts[:packed]
|
180
|
+
len = buf.read_uint64
|
181
|
+
tmp = Buffer.new(buf.read(len))
|
182
|
+
o[fld.name] ||= []
|
183
|
+
while tmp.length > 0
|
184
|
+
o[fld.name] << tmp.read(fld.type)
|
185
|
+
end
|
186
|
+
elsif fld.rule == :repeated
|
187
|
+
val = buf.read(fld.type)
|
188
|
+
(o[fld.name] ||= []) << val
|
189
|
+
else
|
190
|
+
val = buf.read(fld.type)
|
191
|
+
o[fld.name] = val
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Set defaults
|
196
|
+
fields.values.each do |f|
|
197
|
+
next if o[f.name] == false
|
198
|
+
o[f.name] ||= f.opts[:default]
|
199
|
+
end
|
200
|
+
|
201
|
+
o.validate!
|
202
|
+
|
203
|
+
o
|
204
|
+
end
|
205
|
+
|
206
|
+
def read_delimited(buf, o=self.new)
|
207
|
+
if ! buf.is_a?(Buffer)
|
208
|
+
buf = Buffer.new(buf)
|
209
|
+
end
|
210
|
+
|
211
|
+
return if buf.length == 0
|
212
|
+
|
213
|
+
n = buf.read_int64
|
214
|
+
tmp = Buffer.new(buf.read(n))
|
215
|
+
|
216
|
+
decode(tmp, o)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
def self.included(o)
|
222
|
+
o.extend Dsl
|
223
|
+
o.extend Decode
|
224
|
+
o.send(:include, Encode)
|
225
|
+
end
|
226
|
+
|
227
|
+
def initialize(attrs={})
|
228
|
+
__beefcake_fields__.values.each do |fld|
|
229
|
+
self[fld.name] = attrs[fld.name]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def __beefcake_fields__
|
234
|
+
self.class.fields
|
235
|
+
end
|
236
|
+
|
237
|
+
def [](k)
|
238
|
+
__send__(k)
|
239
|
+
end
|
240
|
+
|
241
|
+
def []=(k, v)
|
242
|
+
__send__("#{k}=", v)
|
243
|
+
end
|
244
|
+
|
245
|
+
def ==(o)
|
246
|
+
return false if (o == nil) || (o == false)
|
247
|
+
return false unless o.is_a? self.class
|
248
|
+
__beefcake_fields__.values.all? {|fld| self[fld.name] == o[fld.name] }
|
249
|
+
end
|
250
|
+
|
251
|
+
def inspect
|
252
|
+
set = __beefcake_fields__.values.select {|fld| self[fld.name] != nil }
|
253
|
+
|
254
|
+
flds = set.map do |fld|
|
255
|
+
val = self[fld.name]
|
256
|
+
|
257
|
+
case fld.type
|
258
|
+
when Class
|
259
|
+
"#{fld.name}: #{val.inspect}"
|
260
|
+
when Module
|
261
|
+
title = name_for(fld.type, val) || "-NA-"
|
262
|
+
"#{fld.name}: #{title}(#{val.inspect})"
|
263
|
+
else
|
264
|
+
"#{fld.name}: #{val.inspect}"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
"<#{self.class.name} #{flds.join(", ")}>"
|
269
|
+
end
|
270
|
+
|
271
|
+
def to_hash
|
272
|
+
__beefcake_fields__.values.inject({}) do |h, fld|
|
273
|
+
value = self[fld.name]
|
274
|
+
unless value.nil?
|
275
|
+
h[fld.name] = value
|
276
|
+
end
|
277
|
+
h
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|