prepor-beefcake 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: c84028ca26869888306b5bf5c48b48dfb79909b2
4
+ data.tar.gz: 7396b01b5432a3452ec5d59b3f19e3b140c66e19
5
+ SHA512:
6
+ metadata.gz: ffea398a17e0efa2bf8e6278c895caea789a086aff759ebb06bd57f25fff28da763fce39ac618c2cc13a6b814897079257322d6973bf330bc8e1ade0953a980a
7
+ data.tar.gz: 719b223605e7c5acaa0b795df704caf16576478cea4dd870225f7118d31613ac171f9ca62601fc242d0ba91d33942e9c919451d486038a6059f80defc8db4e67
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ bench/beefcake.prof.*
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.1.1
7
+ - 2.1.2
8
+ - jruby-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in beefcake.gemspec
4
+ gemspec
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 [![Build Status](https://travis-ci.org/protobuf-ruby/beefcake.png?branch=master)](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
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'beefcake/generator'
4
+
5
+ req = CodeGeneratorRequest.decode(STDIN.read)
6
+ res = Beefcake::Generator.compile(req)
7
+
8
+ # Send it out!
9
+ STDOUT.print(res.encode)
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