beefcake-1.0.0/0000755000175000017500000000000012410623537012466 5ustar debiandebianbeefcake-1.0.0/dat/0000755000175000017500000000000012410623537013236 5ustar debiandebianbeefcake-1.0.0/dat/code_generator_request.dat0000644000175000017500000000055412410623537020464 0ustar debiandebian addressbook.protozÖ addressbook.prototutorial"Ú Person name (  id ( email ( + phone ( 2.tutorial.Person.PhoneNumberM PhoneNumber number ( . type (2.tutorial.Person.PhoneType:HOME"+ PhoneType MOBILE HOME WORK"/ AddressBook person ( 2.tutorial.PersonB) com.example.tutorialBAddressBookProtosbeefcake-1.0.0/RELEASE_NOTES.md0000644000175000017500000000241012410623537015035 0ustar debiandebian# Beefcake Release Notes # 0.1.0 - 2014-09-05 Release 1.0.0 includes changes and improvements. * Version numbering now properly semantic. * Ruby 1.8 is no longer supported. * Field number re-use raises a `DuplicateFieldNumber` error. * Checking to see if a type is encodable is much faster. * Fields named `fields` are now supported. * String read and decoding are benchmarked during testing. * `string` fields now decode with a `UTF-8` encoding. # 0.5.0 - 2013-12-20 Release 0.5.0 corrects a few behaviors. * Drastically revised README, written by Tobias "grobie" Schmidt * Output fewer newlines in generated files, fixed by Tobias "grobie" Schmidt * Don't crash when attempting to reencode frozen strings, found thanks to Kyle "Aphyr" Kingsbury * Return `nil` instead of raising a generic Ruby error when trying to decode a zero-length buffer, fixed by Tobias "grobie" Schmidt # 0.4.0 - 2013-10-10 Release 0.4.0 is the first with new maintainers. * Modernize tests * Add Travis CI monitoring * Support varint-encoded length-delimited buffers * Support generation with recursive definitions * Support Ruby 2.0 * Support encoded buffers * Support false but non-nil values * Support top-level enums, added by Kim Altintop: https://github.com/protobuf-ruby/beefcake/pull/23 beefcake-1.0.0/metadata.yml0000644000175000017500000000463312410623537014777 0ustar debiandebian--- !ruby/object:Gem::Specification name: beefcake version: !ruby/object:Gem::Version version: 1.0.0 platform: ruby authors: - Blake Mizerany - Matt Proud - Bryce Kerley autorequire: bindir: bin cert_chain: [] date: 2014-09-05 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 10.1.0 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 10.1.0 - !ruby/object:Gem::Dependency name: minitest requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '5.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '5.3' description: A sane protobuf library for Ruby email: - blake.mizerany@gmail.com - matt.proud@gmail.com - bkerley@brycekerley.net executables: - protoc-gen-beefcake extensions: [] extra_rdoc_files: [] files: - ".gitignore" - ".travis.yml" - Gemfile - LICENSE - README.md - RELEASE_NOTES.md - Rakefile - beefcake.gemspec - bench/simple.rb - bin/protoc-gen-beefcake - dat/code_generator_request.dat - lib/beefcake.rb - lib/beefcake/buffer.rb - lib/beefcake/buffer/base.rb - lib/beefcake/buffer/decode.rb - lib/beefcake/buffer/encode.rb - lib/beefcake/generator.rb - lib/beefcake/version.rb - test/benchmark_test.rb - test/buffer_decode_test.rb - test/buffer_encode_test.rb - test/buffer_test.rb - test/generator_test.rb - test/message_test.rb homepage: https://github.com/protobuf-ruby/beefcake licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 1.9.3 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.3.0 signing_key: specification_version: 4 summary: A sane protobuf library for Ruby test_files: - test/benchmark_test.rb - test/buffer_decode_test.rb - test/buffer_encode_test.rb - test/buffer_test.rb - test/generator_test.rb - test/message_test.rb has_rdoc: beefcake-1.0.0/Rakefile0000644000175000017500000000036012410623537014132 0ustar debiandebianrequire 'rake' require 'rake/testtask' require 'bundler/gem_tasks' Bundler::GemHelper.install_tasks Rake::TestTask.new do |t| t.libs << 'minitest' t.test_files = FileList['test/*_test.rb'] t.verbose = true end task :default => :testbeefcake-1.0.0/bin/0000755000175000017500000000000012410623537013236 5ustar debiandebianbeefcake-1.0.0/bin/protoc-gen-beefcake0000755000175000017500000000037312410623537016767 0ustar debiandebian#!/usr/bin/env ruby require 'rubygems' require 'beefcake/generator' ns = (ENV["BEEFCAKE_NAMESPACE"] || "").split("::") req = CodeGeneratorRequest.decode(STDIN.read) res = Beefcake::Generator.compile(ns, req) # Send it out! STDOUT.print(res.encode) beefcake-1.0.0/test/0000755000175000017500000000000012410623537013445 5ustar debiandebianbeefcake-1.0.0/test/buffer_test.rb0000644000175000017500000000233312410623537016303 0ustar debiandebianrequire 'minitest/autorun' require 'beefcake/buffer' class BufferTest < Minitest::Test def setup @buf = Beefcake::Buffer.new end def test_simple assert_equal "", @buf.to_s @buf << "asdf" assert_equal "asdf", @buf.to_s assert_equal "asdf", @buf.to_str @buf.buf = "" assert_equal "", @buf.to_s end def test_wire_for assert_equal 0, Beefcake::Buffer.wire_for(:int32) assert_equal 0, Beefcake::Buffer.wire_for(:uint32) assert_equal 0, Beefcake::Buffer.wire_for(:sint32) assert_equal 0, Beefcake::Buffer.wire_for(:int64) assert_equal 0, Beefcake::Buffer.wire_for(:uint64) assert_equal 0, Beefcake::Buffer.wire_for(:sint64) assert_equal 1, Beefcake::Buffer.wire_for(:fixed64) assert_equal 1, Beefcake::Buffer.wire_for(:sfixed64) assert_equal 1, Beefcake::Buffer.wire_for(:double) assert_equal 2, Beefcake::Buffer.wire_for(:string) assert_equal 2, Beefcake::Buffer.wire_for(:bytes) assert_equal 5, Beefcake::Buffer.wire_for(:fixed32) assert_equal 5, Beefcake::Buffer.wire_for(:sfixed32) assert_equal 5, Beefcake::Buffer.wire_for(:float) assert_raises Beefcake::Buffer::UnknownType do Beefcake::Buffer.wire_for(:asdf) end end end beefcake-1.0.0/test/buffer_decode_test.rb0000644000175000017500000000463712410623537017617 0ustar debiandebianrequire 'minitest/autorun' require 'beefcake/buffer' class BufferDecodeTest < Minitest::Test B = Beefcake::Buffer def setup @buf = B.new end def test_read_info @buf.append_info(1, 2) assert_equal [1, 2], @buf.read_info @buf.append_info(2, 5) assert_equal [2, 5], @buf.read_info end def test_read_string @buf.append_string("testing") decoded = @buf.read_string assert_equal "testing", decoded assert_equal Encoding.find('utf-8'), decoded.encoding end def test_read_fixed32 @buf.append_fixed32(123) assert_equal 123, @buf.read_fixed32 end def test_read_fixed64 @buf.append_fixed64(456) assert_equal 456, @buf.read_fixed64 end def test_read_uint32 @buf.append_uint32(1) assert_equal 1, @buf.read_uint32 end def test_read_int32 @buf.append_int32(999) assert_equal 999, @buf.read_int32 @buf.append_int32(-999) assert_equal -999, @buf.read_int32 end def test_read_int64 @buf.append_int64(999) assert_equal 999, @buf.read_int64 @buf.append_int64(-999) assert_equal -999, @buf.read_int64 end def test_read_uint64 @buf.append_uint64(1) assert_equal 1, @buf.read_uint64 end def test_read_float @buf.append_float(0.5) assert_equal 0.5, @buf.read_float end def test_read_double @buf.append_double(Math::PI) assert_equal Math::PI, @buf.read_double end def test_read_bool @buf.append_bool(true) assert_equal true, @buf.read_bool @buf.append_bool(false) assert_equal false, @buf.read_bool end def test_read_sint32 @buf.append_sint32(B::MinInt32) assert_equal B::MinInt32, @buf.read_sint32 @buf.buf = "" @buf.append_sint32(B::MaxInt32) assert_equal B::MaxInt32, @buf.read_sint32 end def test_read_sfixed32 @buf.append_sfixed32(B::MinInt32) assert_equal B::MinInt32, @buf.read_sfixed32 @buf.buf = "" @buf.append_sfixed32(B::MaxInt32) assert_equal B::MaxInt32, @buf.read_sfixed32 end def test_read_sint64 @buf.append_sint64(B::MinInt64) assert_equal B::MinInt64, @buf.read_sint64 @buf.buf = "" @buf.append_sint64(B::MaxInt64) assert_equal B::MaxInt64, @buf.read_sint64 end def test_read_sfixed64 @buf.append_sfixed64(B::MinInt64) assert_equal B::MinInt64, @buf.read_sfixed64 @buf.buf = "" @buf.append_sfixed64(B::MaxInt64) assert_equal B::MaxInt64, @buf.read_sfixed64 end end beefcake-1.0.0/test/benchmark_test.rb0000644000175000017500000000146312410623537016767 0ustar debiandebianrequire 'minitest/autorun' require 'minitest/benchmark' require 'beefcake' class Inner include Beefcake::Message required :content, :string, 1 end class Outer include Beefcake::Message repeated :inner, Inner, 1 end class BenchmarkTest < Minitest::Benchmark def setup @buf = Beefcake::Buffer.new end def bench_read_string assert_performance_linear 0.75 do |n| candidate = 'x' * (n * 1_000) @buf.append_string candidate assert_equal candidate, @buf.read_string end end def bench_decode assert_performance_linear 0.75 do |n| inners = n.times.map{ Inner.new :content => 'x' * 100 } outer = Outer.new :inner => inners encoded = outer.encode.to_s decoded = Outer.decode encoded assert_equal n, decoded.inner.length end end end beefcake-1.0.0/test/message_test.rb0000644000175000017500000002351312410623537016461 0ustar debiandebianrequire 'minitest/autorun' require 'beefcake' class NumericsMessage include Beefcake::Message required :int32, :int32, 1 required :uint32, :uint32, 2 required :sint32, :sint32, 3 required :fixed32, :fixed32, 4 required :sfixed32, :sfixed32, 5 required :int64, :int64, 6 required :uint64, :uint64, 7 required :sint64, :sint64, 8 required :fixed64, :fixed64, 9 required :sfixed64, :sfixed64, 10 end class LendelsMessage include Beefcake::Message required :string, :string, 1 required :bytes, :bytes, 2 end class SimpleMessage include Beefcake::Message optional :a, :int32, 1 optional :b, :string, 2 end class CompositeMessage include Beefcake::Message required :encodable, SimpleMessage, 1 end class EnumsMessage include Beefcake::Message module X A = 1 end required :a, X, 1 end class EnumsDefaultMessage include Beefcake::Message module X A = 1 B = 2 end optional :a, X, 1, :default => X::B end class RepeatedMessage include Beefcake::Message repeated :a, :int32, 1 end class PackedRepeatedMessage include Beefcake::Message repeated :a, :int32, 1, :packed => true end class RepeatedNestedMessage include Beefcake::Message repeated :simple, SimpleMessage, 1 end class BoolMessage include Beefcake::Message required :bool, :bool, 1 end class LargeFieldNumberMessage include Beefcake::Message required :field_1, :string, 1 required :field_2, :string, 100 end class FieldsMessage include Beefcake::Message repeated :fields, :string, 1 end class MessageTest < Minitest::Test B = Beefcake::Buffer ## Encoding def test_encode_numerics buf = Beefcake::Buffer.new buf.append(:int32, B::MaxInt32, 1) buf.append(:uint32, B::MaxUint32, 2) buf.append(:sint32, B::MinInt32, 3) buf.append(:fixed32, B::MaxInt32, 4) buf.append(:sfixed32, B::MinInt32, 5) buf.append(:int64, B::MaxInt64, 6) buf.append(:uint64, B::MaxUint64, 7) buf.append(:sint64, B::MinInt64, 8) buf.append(:fixed64, B::MaxInt64, 9) buf.append(:sfixed64, B::MinInt64, 10) msg = NumericsMessage.new({ :int32 => B::MaxInt32, :uint32 => B::MaxUint32, :sint32 => B::MinInt32, :fixed32 => B::MaxInt32, :sfixed32 => B::MinInt32, :int64 => B::MaxInt64, :uint64 => B::MaxUint64, :sint64 => B::MinInt64, :fixed64 => B::MaxInt64, :sfixed64 => B::MinInt64 }) assert_equal buf.to_s, msg.encode.to_s end def test_encode_strings buf = Beefcake::Buffer.new buf.append(:string, "test\ning", 1) buf.append(:bytes, "unixisawesome", 2) msg = LendelsMessage.new({ :string => "test\ning", :bytes => "unixisawesome" }) assert_equal buf.to_s, msg.encode.to_s end def test_encode_string_composite buf1 = Beefcake::Buffer.new buf1.append(:int32, 123, 1) buf2 = Beefcake::Buffer.new buf2.append(:string, buf1, 1) msg = CompositeMessage.new( :encodable => SimpleMessage.new(:a => 123) ) assert_equal buf2.to_s, msg.encode.to_s end def test_encode_to_string msg = SimpleMessage.new :a => 123 str = "" msg.encode(str) assert_equal "\b{", str end def test_delimited_end_to_end msg = SimpleMessage.new :a => 123, :b => "hi mom!" str = "" 1000.times do msg.write_delimited(str) end 1000.times do dec = SimpleMessage.read_delimited(str) assert_equal msg, dec end end def test_empty_buffer_delimited_read assert_equal SimpleMessage.read_delimited(""), nil end def test_encode_enum buf = Beefcake::Buffer.new buf.append(:int32, 2, 1) msg = EnumsMessage.new :a => EnumsMessage::X::A assert_equal "\b\001", msg.encode.to_s end def test_encode_invalid_enum_value assert_raises Beefcake::Message::InvalidValueError do EnumsMessage.new(:a => 99).encode end end def test_encode_unset_required_field assert_raises Beefcake::Message::RequiredFieldNotSetError do NumericsMessage.new.encode end end def test_decode_required_bool msg = BoolMessage.new :bool => false enc = msg.encode dec = BoolMessage.decode(enc) assert_equal false, dec.bool end def test_encode_repeated_field buf = Beefcake::Buffer.new buf.append(:int32, 1, 1) buf.append(:int32, 2, 1) buf.append(:int32, 3, 1) buf.append(:int32, 4, 1) buf.append(:int32, 5, 1) msg = RepeatedMessage.new :a => [1, 2, 3, 4, 5] assert_equal buf.to_s, msg.encode.to_s end def test_encode_packed_repeated_field buf = Beefcake::Buffer.new # Varint buf.append_info(1, 0) # Give size in bytes buf.append_uint64 5 # Values buf.append_int32 1 buf.append_int32 2 buf.append_int32 3 buf.append_int32 4 buf.append_int32 5 msg = PackedRepeatedMessage.new :a => [1, 2, 3, 4, 5] assert_equal buf.to_s, msg.encode.to_s end def test_encode_large_field_number buf = Beefcake::Buffer.new buf.append(:string, "abc", 1) buf.append(:string, "123", 100) msg = LargeFieldNumberMessage.new msg.field_1 = "abc" msg.field_2 = "123" assert_equal buf.to_s, msg.encode.to_s end def test_encode_unknown_field msg = SimpleMessage.new :mystery_field => 'asdf' msg.encode.to_s end ## Decoding def test_decode_numerics msg = NumericsMessage.new({ :int32 => B::MaxInt32, :uint32 => B::MaxUint32, :sint32 => B::MinInt32, :fixed32 => B::MaxInt32, :sfixed32 => B::MinInt32, :int64 => B::MaxInt64, :uint64 => B::MaxUint64, :sint64 => B::MinInt64, :fixed64 => B::MaxInt64, :sfixed64 => B::MinInt64 }) got = NumericsMessage.decode(msg.encode) msg.__beefcake_fields__.values.each do |fld| assert_equal msg[fld.name], got[fld.name], fld.name end end def test_wire_does_not_match_decoded_info buf = Beefcake::Buffer.new buf.append(:string, "testing", 1) assert_raises Beefcake::Message::WrongTypeError do SimpleMessage.decode(buf) end end def test_decode_unknown_field_number buf = Beefcake::Buffer.new buf.append(:string, "testing", 2) msg = SimpleMessage.decode(buf) assert_equal nil, msg.a end def test_decode_repeated_field msg = RepeatedMessage.new :a => [1, 2, 3, 4, 5] got = RepeatedMessage.decode(msg.encode) assert_equal msg.a, got.a end def test_decode_packed_repeated_field msg = PackedRepeatedMessage.new :a => [1, 2, 3, 4, 5] got = PackedRepeatedMessage.decode(msg.encode) assert_equal msg.a, got.a end def test_decode_merge a = SimpleMessage.new :a => 1 b = SimpleMessage.new :a => 2 x = SimpleMessage.decode(a.encode) SimpleMessage.decode(b.encode, x) assert_equal b.a, x.a end def test_decode_default got = EnumsDefaultMessage.decode("") assert_equal EnumsDefaultMessage.fields[1].opts[:default], got.a end def test_decode_unset_required_fields assert_raises Beefcake::Message::RequiredFieldNotSetError do NumericsMessage.decode("") end end def test_decode_enum msg = EnumsMessage.new(:a => 1).encode got = EnumsMessage.decode(msg) assert_equal 1, got.a end def test_decode_repeated_nested simple = [ SimpleMessage.new(:a => 1), SimpleMessage.new(:b => "hello") ] msg = RepeatedNestedMessage.new(:simple => simple).encode got = RepeatedNestedMessage.decode(msg) assert_equal 2, got.simple.size assert_equal 1, got.simple[0].a assert_equal "hello", got.simple[1].b end def test_equality a = SimpleMessage.new :a => 1 b = SimpleMessage.new :a => 1 assert_equal a, b c = SimpleMessage.new :a => 2 refute_equal b, c d = EnumsMessage.new :a => 5 e = EnumsDefaultMessage.new :a => 5 refute_equal d, e refute_equal d, :symbol refute_equal :symbol, d end def test_inspect msg = SimpleMessage.new :a => 1 assert_equal "", msg.inspect msg.b = "testing" assert_equal "", msg.inspect msg.a = nil assert_equal "", msg.inspect end def test_inspect_enums msg = EnumsMessage.new :a => 1 assert_equal "", msg.inspect msg.a = 2 assert_equal "", msg.inspect end def test_inspect_nested_types msg = CompositeMessage.new(:encodable => SimpleMessage.new(:a => 1)) assert_equal ">", msg.inspect end def test_decode_nested_types msg = CompositeMessage.new(:encodable => SimpleMessage.new(:a => 1)) enc = msg.encode dec = CompositeMessage.decode(enc) assert_equal ">", dec.inspect end def test_to_hash msg = SimpleMessage.new :a => 1 exp = { :a => 1 } assert_equal(exp, msg.to_hash) end def test_duplicate_index assert_raises Beefcake::Message::DuplicateFieldNumber do Class.new do include Beefcake::Message required :clever_name, :int32, 1 required :naughty_field, :string, 1 end end end def test_bool_to_hash true_message = BoolMessage.new :bool => true true_expectation = { :bool => true } assert_equal true_expectation, true_message.to_hash false_message = BoolMessage.new :bool => false false_expectation = { :bool => false } assert_equal false_expectation, false_message.to_hash end def test_fields_named_fields contents = %w{fields named fields} msg = FieldsMessage.new fields: contents assert_equal 1, msg.__beefcake_fields__.length assert_equal contents, msg.fields assert_equal msg, FieldsMessage.decode(msg.encode) assert_equal "", msg.inspect end end beefcake-1.0.0/test/generator_test.rb0000644000175000017500000000275312410623537017026 0ustar debiandebianrequire 'minitest/autorun' require 'beefcake/generator' class GeneratorTest < Minitest::Test def setup # Load up the generator request for the addressbook.proto example dat = File.dirname(__FILE__) + "/../dat/code_generator_request.dat" mock_request = File.read(dat) @req = CodeGeneratorRequest.decode(mock_request) end def test_request_has_filenames_as_string @req.proto_file.each do |file| assert_equal Encoding.find("UTF-8"), file.name.encoding end end def test_generate_empty_namespace @res = Beefcake::Generator.compile([], @req) assert_equal(CodeGeneratorResponse, @res.class) end def test_generate_top_namespace @res = Beefcake::Generator.compile(["Top"], @req) assert_equal(CodeGeneratorResponse, @res.class) assert_match(/module Top/, @res.file.first.content) end def test_generate_two_level_namespace @res = Beefcake::Generator.compile(["Top", "Bottom"], @req) assert_equal(CodeGeneratorResponse, @res.class) assert_match(/module Top\s*\n\s*module Bottom/m, @res.file.first.content) end # Covers the regression of encoding a CodeGeneratorResponse under 1.9.2-p136 raising # Encoding::CompatibilityError: incompatible character encodings: ASCII-8BIT and US-ASCII def test_encode_decode_generated_response @res = Beefcake::Generator.compile([], @req) assert_equal(CodeGeneratorResponse, @res.class) round_trip = CodeGeneratorResponse.decode(@res.encode) assert_equal round_trip, @res end end beefcake-1.0.0/test/buffer_encode_test.rb0000644000175000017500000001261412410623537017623 0ustar debiandebian# encoding: ASCII-8BIT require 'minitest/autorun' require 'beefcake/buffer/encode' class BufferEncodeTest < Minitest::Test B = Beefcake::Buffer def setup @buf = B.new end def test_append_info @buf.append_info(1, 0) assert_equal "\010", @buf.to_s @buf.buf = "" @buf.append_info(2, 1) assert_equal "\021", @buf.to_s end def test_append_string @buf.append_string("testing") assert_equal "\007testing", @buf.to_s end def test_append_fixed32 @buf.append_fixed32(1) assert_equal "\001\0\0\0", @buf.to_s @buf.buf = "" @buf.append_fixed32(B::MinUint32) assert_equal "\0\0\0\0", @buf.to_s @buf.buf = "" @buf.append_fixed32(B::MaxUint32) assert_equal "\377\377\377\377", @buf.to_s assert_raises B::OutOfRangeError do @buf.append_fixed32(B::MinUint32 - 1) end assert_raises B::OutOfRangeError do @buf.append_fixed32(B::MaxUint32 + 1) end end def test_append_fixed64 @buf.append_fixed64(1) assert_equal "\001\0\0\0\0\0\0\0", @buf.to_s @buf.buf = "" @buf.append_fixed64(B::MinUint64) assert_equal "\000\0\0\0\0\0\0\0", @buf.to_s @buf.buf = "" @buf.append_fixed64(B::MaxUint64) assert_equal "\377\377\377\377\377\377\377\377", @buf.to_s assert_raises B::OutOfRangeError do @buf.append_fixed64(B::MinUint64 - 1) end assert_raises B::OutOfRangeError do @buf.append_fixed64(B::MaxUint64 + 1) end end def test_append_uint32 @buf.append_uint32(1) assert_equal "\001", @buf.to_s @buf.buf = "" @buf.append_uint32(B::MinUint32) assert_equal "\000", @buf.to_s @buf.buf = "" @buf.append_uint32(B::MaxUint32) assert_equal "\377\377\377\377\017", @buf.to_s assert_raises B::OutOfRangeError do @buf.append_uint32(B::MinUint32 - 1) end assert_raises B::OutOfRangeError do @buf.append_uint32(B::MaxUint32 + 1) end end def test_append_int32 @buf.append_int32(1) assert_equal "\001", @buf.to_s @buf.buf = "" @buf.append_int32(-1) assert_equal "\377\377\377\377\377\377\377\377\377\001", @buf.to_s @buf.buf = "" @buf.append_int32(B::MinInt32) assert_equal "\200\200\200\200\370\377\377\377\377\001", @buf.to_s @buf.buf = "" @buf.append_int32(B::MaxInt32) assert_equal "\377\377\377\377\007", @buf.to_s assert_raises B::OutOfRangeError do @buf.append_int32(B::MinInt32 - 1) end assert_raises B::OutOfRangeError do @buf.append_int32(B::MaxInt32 + 1) end end def test_append_int64 @buf.append_int64(1) assert_equal "\001", @buf.to_s @buf.buf = "" @buf.append_int64(-1) assert_equal "\377\377\377\377\377\377\377\377\377\001", @buf.to_s @buf.buf = "" @buf.append_int64(B::MinInt64) assert_equal "\200\200\200\200\200\200\200\200\200\001", @buf.to_s @buf.buf = "" @buf.append_int64(B::MaxInt64) assert_equal "\377\377\377\377\377\377\377\377\177", @buf.to_s assert_raises B::OutOfRangeError do @buf.append_int64(B::MinInt64 - 1) end assert_raises B::OutOfRangeError do @buf.append_int64(B::MaxInt64 + 1) end end def test_append_uint64 @buf.append_uint64(1) assert_equal "\001", @buf.to_s @buf.buf = "" @buf.append_uint64(B::MinUint64) assert_equal "\000", @buf.to_s @buf.buf = "" @buf.append_uint64(B::MaxUint64) assert_equal "\377\377\377\377\377\377\377\377\377\001", @buf.to_s assert_raises B::OutOfRangeError do @buf.append_uint64(B::MinUint64 - 1) end assert_raises B::OutOfRangeError do @buf.append_uint64(B::MaxUint64 + 1) end end def test_append_float @buf.append_float(3.14) assert_equal "\303\365H@", @buf.to_s end def test_append_double @buf.buf = "" @buf.append_float(Math::PI) assert_equal "\333\017I@", @buf.to_s end def test_append_bool @buf.append_bool(true) @buf.append_bool(false) assert_equal "\001\000", @buf.to_s end def test_append_sint32 @buf.append_sint32(-2) assert_equal "\003", @buf.to_s @buf.buf = "" @buf.append_sint32(B::MaxInt32) assert_equal "\376\377\377\377\017", @buf.to_s @buf.buf = "" @buf.append_sint32(B::MinInt32) assert_equal "\377\377\377\377\017", @buf.to_s end def test_append_sfixed32 @buf.append_sfixed32(-2) assert_equal "\003\000\000\000", @buf.to_s end def test_append_sint64 @buf.append_sint64(-2) assert_equal "\003", @buf.to_s @buf.buf = "" @buf.append_sint64(B::MaxInt64) assert_equal "\376\377\377\377\377\377\377\377\377\001", @buf.to_s @buf.buf = "" @buf.append_sint64(B::MinInt64) assert_equal "\377\377\377\377\377\377\377\377\377\001", @buf.to_s end def test_append_sfixed64 @buf.append_sfixed64(B::MaxInt64) assert_equal "\376\377\377\377\377\377\377\377", @buf.to_s end def test_append_unicode_string ingest = "\u{1f63a}" * 5 assert_equal 5, ingest.chars.to_a.length expected = ingest.bytes.to_a.length.chr + ingest @buf.append_string(ingest) actual = @buf.to_s assert_equal expected.bytes.to_a.length, actual.bytes.to_a.length assert_equal expected.bytes.to_a, actual.bytes.to_a end def test_append_bytes @buf.append_bytes("testing") assert_equal "\007testing", @buf.to_s end def test_append_frozen_string @buf.append_string "testing".freeze assert_equal "\007testing", @buf.to_s end end beefcake-1.0.0/lib/0000755000175000017500000000000012410623537013234 5ustar debiandebianbeefcake-1.0.0/lib/beefcake.rb0000644000175000017500000001420212410623537015305 0ustar debiandebianrequire 'beefcake/buffer' module Beefcake module Message class WrongTypeError < StandardError def initialize(name, exp, got) super("Wrong type `#{got}` given for (#{name}). Expected #{exp}") end end class InvalidValueError < StandardError def initialize(name, val) super("Invalid Value given for `#{name}`: #{val.inspect}") end end class RequiredFieldNotSetError < StandardError def initialize(name) super("Field #{name} is required but nil") end end class DuplicateFieldNumber < StandardError def initialize(num, name) super("Field number #{num} (#{name}) was already used") end end class Field < Struct.new(:rule, :name, :type, :fn, :opts) def <=>(o) fn <=> o.fn end end module Dsl def required(name, type, fn, opts={}) field(:required, name, type, fn, opts) end def repeated(name, type, fn, opts={}) field(:repeated, name, type, fn, opts) end def optional(name, type, fn, opts={}) field(:optional, name, type, fn, opts) end def field(rule, name, type, fn, opts) if fields.include?(fn) raise DuplicateFieldNumber.new(fn, name) end fields[fn] = Field.new(rule, name, type, fn, opts) attr_accessor name end def fields @fields ||= {} end end module Encode def encode(buf = Buffer.new) validate! if ! buf.respond_to?(:<<) raise ArgumentError, "buf doesn't respond to `<<`" end if ! buf.is_a?(Buffer) buf = Buffer.new(buf) end # TODO: Error if any required fields at nil __beefcake_fields__.values.sort.each do |fld| if fld.opts[:packed] bytes = encode!(Buffer.new, fld, 0) buf.append_info(fld.fn, Buffer.wire_for(fld.type)) buf.append_uint64(bytes.length) buf << bytes else encode!(buf, fld, fld.fn) end end buf end def encode!(buf, fld, fn) v = self[fld.name] v = v.is_a?(Array) ? v : [v] v.compact.each do |val| case fld.type when Class # encodable # TODO: raise error if type != val.class buf.append(:string, val.encode, fn) when Module # enum if ! valid_enum?(fld.type, val) raise InvalidValueError.new(fld.name, val) end buf.append(:int32, val, fn) else buf.append(fld.type, val, fn) end end buf end def write_delimited(buf = Buffer.new) if ! buf.respond_to?(:<<) raise ArgumentError, "buf doesn't respond to `<<`" end if ! buf.is_a?(Buffer) buf = Buffer.new(buf) end buf.append_bytes(encode) buf end def valid_enum?(mod, val) !!name_for(mod, val) end def name_for(mod, val) mod.constants.each do |name| if mod.const_get(name) == val return name end end nil end def validate! __beefcake_fields__.values.each do |fld| if fld.rule == :required && self[fld.name].nil? raise RequiredFieldNotSetError, fld.name end end end end module Decode def decode(buf, o=self.new) if ! buf.is_a?(Buffer) buf = Buffer.new(buf) end # TODO: test for incomplete buffer while buf.length > 0 fn, wire = buf.read_info fld = fields[fn] # We don't have a field for with index fn. # Ignore this data and move on. if fld.nil? buf.skip(wire) next end exp = Buffer.wire_for(fld.type) if wire != exp raise WrongTypeError.new(fld.name, exp, wire) end if fld.rule == :repeated && fld.opts[:packed] len = buf.read_uint64 tmp = Buffer.new(buf.read(len)) o[fld.name] ||= [] while tmp.length > 0 o[fld.name] << tmp.read(fld.type) end elsif fld.rule == :repeated val = buf.read(fld.type) (o[fld.name] ||= []) << val else val = buf.read(fld.type) o[fld.name] = val end end # Set defaults fields.values.each do |f| next if o[f.name] == false o[f.name] ||= f.opts[:default] end o.validate! o end def read_delimited(buf, o=self.new) if ! buf.is_a?(Buffer) buf = Buffer.new(buf) end return if buf.length == 0 n = buf.read_int64 tmp = Buffer.new(buf.read(n)) decode(tmp, o) end end def self.included(o) o.extend Dsl o.extend Decode o.send(:include, Encode) end def initialize(attrs={}) __beefcake_fields__.values.each do |fld| self[fld.name] = attrs[fld.name] end end def __beefcake_fields__ self.class.fields end def [](k) __send__(k) end def []=(k, v) __send__("#{k}=", v) end def ==(o) return false if (o == nil) || (o == false) return false unless o.is_a? self.class __beefcake_fields__.values.all? {|fld| self[fld.name] == o[fld.name] } end def inspect set = __beefcake_fields__.values.select {|fld| self[fld.name] != nil } flds = set.map do |fld| val = self[fld.name] case fld.type when Class "#{fld.name}: #{val.inspect}" when Module title = name_for(fld.type, val) || "-NA-" "#{fld.name}: #{title}(#{val.inspect})" else "#{fld.name}: #{val.inspect}" end end "<#{self.class.name} #{flds.join(", ")}>" end def to_hash __beefcake_fields__.values.inject({}) do |h, fld| value = self[fld.name] unless value.nil? h[fld.name] = value end h end end end end beefcake-1.0.0/lib/beefcake/0000755000175000017500000000000012410623537014761 5ustar debiandebianbeefcake-1.0.0/lib/beefcake/buffer.rb0000644000175000017500000000010212410623537016550 0ustar debiandebianrequire 'beefcake/buffer/encode' require 'beefcake/buffer/decode' beefcake-1.0.0/lib/beefcake/generator.rb0000644000175000017500000001652412410623537017304 0ustar debiandebian# encoding: ASCII-8BIT # The above line allows concatenation of constant strings like ".pb.rb" to # maintain the internal format of the buffers, rather than converting the # buffer to US-ASCII require 'beefcake' require 'stringio' class CodeGeneratorRequest include Beefcake::Message class FieldDescriptorProto include Beefcake::Message module Type ## 0 is reserved for errors. ## Order is weird for historical reasons. TYPE_DOUBLE = 1 TYPE_FLOAT = 2 TYPE_INT64 = 3 ## Not ZigZag encoded. Negative numbers ## take 10 bytes. Use TYPE_SINT64 if negative ## values are likely. TYPE_UINT64 = 4 TYPE_INT32 = 5 ## Not ZigZag encoded. Negative numbers ## take 10 bytes. Use TYPE_SINT32 if negative ## values are likely. TYPE_FIXED64 = 6 TYPE_FIXED32 = 7 TYPE_BOOL = 8 TYPE_STRING = 9 TYPE_GROUP = 10 ## Tag-delimited aggregate. TYPE_MESSAGE = 11 ## Length-delimited aggregate. ## New in version 2. TYPE_BYTES = 12 TYPE_UINT32 = 13 TYPE_ENUM = 14 TYPE_SFIXED32 = 15 TYPE_SFIXED64 = 16 TYPE_SINT32 = 17 ## Uses ZigZag encoding. TYPE_SINT64 = 18 ## Uses ZigZag encoding. end module Label LABEL_OPTIONAL = 1 LABEL_REQUIRED = 2 LABEL_REPEATED = 3 end optional :name, :string, 1 optional :number, :int32, 3 optional :label, Label, 4 ## If type_name is set, this need not be set. If both this and type_name ## are set, this must be either TYPE_ENUM or TYPE_MESSAGE. optional :type, Type, 5 ## For message and enum types, this is the name of the type. If the name ## starts with a '.', it is fully-qualified. Otherwise, C++-like scoping ## rules are used to find the type (i.e. first the nested types within this ## message are searched, then within the parent, on up to the root ## namespace). optional :type_name, :string, 6 ## For extensions, this is the name of the type being extended. It is ## resolved in the same manner as type_name. optional :extended, :string, 2 ## For numeric types, contains the original text representation of the value. ## For booleans, "true" or "false". ## For strings, contains the default text contents (not escaped in any way). ## For bytes, contains the C escaped value. All bytes >= 128 are escaped. optional :default_value, :string, 7 end class EnumValueDescriptorProto include Beefcake::Message optional :name, :string, 1 optional :number, :int32, 2 # optional EnumValueOptions options = 3; end class EnumDescriptorProto include Beefcake::Message optional :name, :string, 1 repeated :value, EnumValueDescriptorProto, 2 # optional :options, EnumOptions, 3 end class DescriptorProto include Beefcake::Message optional :name, :string, 1 repeated :field, FieldDescriptorProto, 2 repeated :extended, FieldDescriptorProto, 6 repeated :nested_type, DescriptorProto, 3 repeated :enum_type, EnumDescriptorProto, 4 end class FileDescriptorProto include Beefcake::Message optional :name, :string, 1 # file name, relative to root of source tree optional :package, :string, 2 # e.g. "foo", "foo.bar", etc. repeated :message_type, DescriptorProto, 4; repeated :enum_type, EnumDescriptorProto, 5; end repeated :file_to_generate, :string, 1 optional :parameter, :string, 2 repeated :proto_file, FileDescriptorProto, 15 end class CodeGeneratorResponse include Beefcake::Message class File include Beefcake::Message optional :name, :string, 1 optional :content, :string, 15 end repeated :file, File, 15 end module Beefcake class Generator L = CodeGeneratorRequest::FieldDescriptorProto::Label T = CodeGeneratorRequest::FieldDescriptorProto::Type def self.compile(ns, req) file = req.proto_file.map do |file| g = new(StringIO.new) g.compile(ns, file) g.c.rewind CodeGeneratorResponse::File.new( :name => File.basename(file.name, ".proto") + ".pb.rb", :content => g.c.read ) end CodeGeneratorResponse.new(:file => file) end attr_reader :c def initialize(c) @c = c @n = 0 end def indent(&blk) @n += 1 blk.call @n -= 1 end def indent!(n) @n = n end def define!(mt) puts puts "class #{mt.name}" indent do puts "include Beefcake::Message" ## Enum Types Array(mt.enum_type).each do |et| enum!(et) end ## Nested Types Array(mt.nested_type).each do |nt| define!(nt) end end puts "end" end def message!(pkg, mt) puts puts "class #{mt.name}" indent do ## Generate Types Array(mt.nested_type).each do |nt| message!(pkg, nt) end ## Generate Fields Array(mt.field).each do |f| field!(pkg, f) end end puts "end" end def enum!(et) puts puts "module #{et.name}" indent do et.value.each do |v| puts "%s = %d" % [v.name, v.number] end end puts "end" end def field!(pkg, f) # Turn the label into Ruby label = name_for(f, L, f.label) # Turn the name into a Ruby name = ":#{f.name}" # Determine the type-name and convert to Ruby type = if f.type_name # We have a type_name so we will use it after converting to a # Ruby friendly version t = f.type_name if pkg t = t.gsub(pkg, "") # Remove the leading package name end t = t.gsub(/^\.*/, "") # Remove leading `.`s t.gsub(".", "::") # Convert to Ruby namespacing syntax else ":#{name_for(f, T, f.type)}" end # Finally, generate the declaration out = "%s %s, %s, %d" % [label, name, type, f.number] if f.default_value v = case f.type when T::TYPE_ENUM "%s::%s" % [type, f.default_value] when T::TYPE_STRING, T::TYPE_BYTES '"%s"' % [f.default_value.gsub('"', '\"')] else f.default_value end out += ", :default => #{v}" end puts out end # Determines the name for a def name_for(b, mod, val) b.name_for(mod, val).to_s.gsub(/.*_/, "").downcase end def compile(ns, file) puts "## Generated from #{file.name} for #{file.package}" puts "require \"beefcake\"" puts ns!(ns) do Array(file.enum_type).each do |et| enum!(et) end file.message_type.each do |mt| define! mt end file.message_type.each do |mt| message!(file.package, mt) end end end def ns!(modules, &blk) if modules.empty? blk.call else puts "module #{modules.first}" indent do ns!(modules[1..-1], &blk) end puts "end" end end def puts(msg=nil) if msg c.puts((" " * @n) + msg) else c.puts end end end end beefcake-1.0.0/lib/beefcake/version.rb0000644000175000017500000000005012410623537016766 0ustar debiandebianmodule Beefcake VERSION = "1.0.0" end beefcake-1.0.0/lib/beefcake/buffer/0000755000175000017500000000000012410623537016232 5ustar debiandebianbeefcake-1.0.0/lib/beefcake/buffer/decode.rb0000644000175000017500000000321212410623537020000 0ustar debiandebianrequire 'beefcake/buffer/base' module Beefcake class Buffer def read_info n = read_uint64 fn = n >> 3 wire = n & 0x7 [fn, wire] end def read_bytes read(read_uint64) end def read_string read_bytes.force_encoding Encoding.find('utf-8') end def read_fixed32 bytes = read(4) bytes.unpack("V").first end def read_fixed64 bytes = read(8) x, y = bytes.unpack("VV") x + (y << 32) end def read_int64 n = read_uint64 if n > MaxInt64 n -= (1 << 64) end n end alias :read_int32 :read_int64 def read_uint64 n = shift = 0 while true if shift >= 64 raise BufferOverflowError, "varint" end b = read(1).ord n |= ((b & 0x7F) << shift) shift += 7 if (b & 0x80) == 0 return n end end end alias :read_uint32 :read_uint64 def read_sint64 decode_zigzag(read_uint64) end alias :read_sint32 :read_sint64 def read_sfixed32 decode_zigzag(read_fixed32) end def read_sfixed64 decode_zigzag(read_fixed64) end def read_float bytes = read(4) bytes.unpack("e").first end def read_double bytes = read(8) bytes.unpack("E").first end def read_bool read_int32 != 0 end def skip(wire) case wire when 0 then read_uint64 when 1 then read_fixed64 when 2 then read_string when 5 then read_fixed32 end end private def decode_zigzag(n) (n >> 1) ^ -(n & 1) end end end beefcake-1.0.0/lib/beefcake/buffer/base.rb0000644000175000017500000000403512410623537017473 0ustar debiandebianmodule Beefcake class Buffer MinUint32 = 0 MaxUint32 = (1 << 32)-1 MinInt32 = -(1 << 31) MaxInt32 = (1 << 31)-1 MinUint64 = 0 MaxUint64 = (1 << 64)-1 MinInt64 = -(1 << 63) MaxInt64 = (1 << 63)-1 WIRES = { :int32 => 0, :uint32 => 0, :sint32 => 0, :int64 => 0, :uint64 => 0, :sint64 => 0, :bool => 0, :fixed64 => 1, :sfixed64 => 1, :double => 1, :string => 2, :bytes => 2, :fixed32 => 5, :sfixed32 => 5, :float => 5, } def self.wire_for(type) wire = WIRES[type] if wire wire elsif Class === type && encodable?(type) 2 elsif Module === type 0 else raise UnknownType, type end end def self.encodable?(type) return false if ! type.is_a?(Class) type.public_method_defined?(:encode) end attr_reader :buf alias :to_s :buf alias :to_str :buf class OutOfRangeError < StandardError def initialize(n) super("Value of of range: %d" % [n]) end end class BufferOverflowError < StandardError def initialize(s) super("Too many bytes read for %s" % [s]) end end class UnknownType < StandardError def initialize(s) super("Unknown type '%s'" % [s]) end end def initialize(buf="") self.buf = buf end def buf=(new_buf) @buf = new_buf.force_encoding('BINARY') @cursor = 0 end def length remain = buf.slice(@cursor..-1) remain.bytesize end def <<(bytes) bytes = bytes.force_encoding('BINARY') if bytes.respond_to? :force_encoding buf << bytes end def read(n) case n when Class n.decode(read_string) when Symbol __send__("read_#{n}") when Module read_uint64 else read_slice = buf.byteslice(@cursor, n) @cursor += n return read_slice end end end end beefcake-1.0.0/lib/beefcake/buffer/encode.rb0000644000175000017500000000424512410623537020021 0ustar debiandebianrequire 'beefcake/buffer/base' module Beefcake class Buffer def append(type, val, fn) if fn != 0 wire = Buffer.wire_for(type) append_info(fn, wire) end __send__("append_#{type}", val) end def append_info(fn, wire) append_uint32((fn << 3) | wire) end def append_fixed32(n, tag=false) if n < MinUint32 || n > MaxUint32 raise OutOfRangeError, n end self << [n].pack("V") end def append_fixed64(n) if n < MinUint64 || n > MaxUint64 raise OutOfRangeError, n end self << [n & 0xFFFFFFFF, n >> 32].pack("VV") end def append_int32(n) if n < MinInt32 || n > MaxInt32 raise OutOfRangeError, n end append_int64(n) end def append_uint32(n) if n < MinUint32 || n > MaxUint32 raise OutOfRangeError, n end append_uint64(n) end def append_int64(n) if n < MinInt64 || n > MaxInt64 raise OutOfRangeError, n end if n < 0 n += (1 << 64) end append_uint64(n) end def append_sint32(n) append_uint32((n << 1) ^ (n >> 31)) end def append_sfixed32(n) append_fixed32((n << 1) ^ (n >> 31)) end def append_sint64(n) append_uint64((n << 1) ^ (n >> 63)) end def append_sfixed64(n) append_fixed64((n << 1) ^ (n >> 63)) end def append_uint64(n) if n < MinUint64 || n > MaxUint64 raise OutOfRangeError, n end while true bits = n & 0x7F n >>= 7 if n == 0 return self << bits end self << (bits | 0x80) end end def append_float(n) self << [n].pack("e") end def append_double(n) self << [n].pack("E") end def append_bool(n) append_int64(n ? 1 : 0) end def append_string(s) actual_string = thaw_string s encoded = actual_string.force_encoding 'binary' append_uint64(encoded.length) self << encoded end alias :append_bytes :append_string private def thaw_string(s) if s.frozen? s = s.dup end s.to_s end end end beefcake-1.0.0/bench/0000755000175000017500000000000012410623537013545 5ustar debiandebianbeefcake-1.0.0/bench/simple.rb0000644000175000017500000000522612410623537015370 0ustar debiandebian$:.unshift File.expand_path('../../lib', __FILE__) require 'beefcake' class MyMessage include Beefcake::Message required :number, :int32, 1 required :chars, :string, 2 required :raw, :bytes, 3 required :bool, :bool, 4 required :float, :float, 5 end ITERS = 100_000 case ARGV[0] when 'pprof' # profile message creation/encoding/decoding w/ perftools.rb # works on 1.8 and 1.9 # ruby bench/simple.rb pprof # open bench/beefcake.prof.gif ENV['CPUPROFILE_FREQUENCY'] = '4000' require 'rubygems' require 'perftools' PerfTools::CpuProfiler.start(File.expand_path("../beefcake.prof", __FILE__)) do ITERS.times do str = MyMessage.new( :number => 12345, :chars => 'hello', :raw => 'world', :bool => true, :float => 1.2345 ).encode MyMessage.decode(str) end end Dir.chdir(File.dirname(__FILE__)) do `pprof.rb beefcake.prof --gif > beefcake.prof.gif` end when 'ruby-prof' # profile message creation/encoding/decoding w/ ruby-prof # works on 1.8 and 1.9 # ruby bench/simple.rb ruby-prof # open bench/beefcake.prof.html require 'ruby-prof' result = RubyProf.profile do ITERS.times do str = MyMessage.new( :number => 12345, :chars => 'hello', :raw => 'world', :bool => true, :float => 1.2345 ).encode MyMessage.decode(str) end end filename = File.expand_path('beefcake.prof.html', File.dirname(__FILE__)) File.open(filename, 'w') do |file| RubyProf::GraphHtmlPrinter.new(result).print(file) end else # benchmark message creation/encoding/decoding # rvm install 1.8.7 1.9.2 jruby rbx # rvm 1.8.7,1.9.2,jruby,rbx ruby bench/simple.rb require 'benchmark' Benchmark.bmbm do |x| x.report 'object creation' do ITERS.times do Object.new end end x.report 'message creation' do ITERS.times do MyMessage.new( :number => 12345, :chars => 'hello', :raw => 'world', :bool => true, :float => 1.2345 ) end end x.report 'message encoding' do m = MyMessage.new( :number => 12345, :chars => 'hello', :raw => 'world', :bool => true, :float => 1.2345 ) ITERS.times do m.encode end end x.report 'message decoding' do str = MyMessage.new( :number => 12345, :chars => 'hello', :raw => 'world', :bool => true, :float => 1.2345 ).encode.to_s ITERS.times do MyMessage.decode(str.dup) end end end end beefcake-1.0.0/.travis.yml0000644000175000017500000000012712410623537014577 0ustar debiandebianlanguage: ruby rvm: - 1.9.3 - 2.0.0 - 2.1.0 - 2.1.1 - 2.1.2 - jruby-19mode beefcake-1.0.0/Gemfile0000644000175000017500000000013512410623537013760 0ustar debiandebiansource "https://rubygems.org" # Specify your gem's dependencies in beefcake.gemspec gemspec beefcake-1.0.0/README.md0000644000175000017500000000731312410623537013751 0ustar debiandebian# Beefcake A sane Google Protocol Buffers library for Ruby. It's all about being Buf; ProtoBuf. ## Installation ```shell gem install beefcake ``` ## Usage ```ruby require 'beefcake' class Variety include Beefcake::Message # Required required :x, :int32, 1 required :y, :int32, 2 # Optional optional :tag, :string, 3 # Repeated repeated :ary, :fixed64, 4 repeated :pary, :fixed64, 5, :packed => true # Enums - Simply use a Module (NOTE: defaults are optional) module Foonum A = 1 B = 2 end # As per the spec, defaults are only set at the end # of decoding a message, not on object creation. optional :foo, Foonum, 6, :default => Foonum::B end # You can create a new message with hash arguments: x = Variety.new(:x => 1, :y => 2) # You can set fields individually using accessor methods: x = Variety.new x.x = 1 x.y = 2 # And you can access fields using Hash syntax: x[:x] # => 1 x[:y] = 4 x # => ``` ### Encoding Any object responding to `<<` can accept encoding ```ruby # see code example above for the definition of Variety x = Variety.new(:x => 1, :y => 2) # For example, you can encode into a String: s = "" x.encode(s) s # => "\b\x01\x10\x02)\0" # If you don't encode into anything, a new Beefcake::Buffer will be returned: x.encode # => # # And that buffer can be converted to a String: x.encode.to_s # => "\b\x01\x10\x02)\0" ``` ### Decoding ```ruby # see code example above for the definition of Variety x = Variety.new(:x => 1, :y => 2) # You can decode from a Beefcake::Buffer encoded = x.encode Variety.decode(encoded) # => # Decoding from a String works the same way: Variety.decode(encoded.to_s) # => # You can update a Beefcake::Message instance with new data too: new_data = Variety.new(x: 12345, y: 2).encode Variety.decoded(new_data, x) x # => ``` ### Generate code from `.proto` file ```shell protoc --beefcake_out output/path -I path/to/proto/files/dir path/to/file.proto ``` You can set the `BEEFCAKE_NAMESPACE` variable to generate the classes under a desired namespace. (i.e. App::Foo::Bar) ## About Ruby deserves and needs first-class ProtoBuf support. Other libs didn't feel very "Ruby" to me and were hard to parse. This library was built with EventMachine in mind. Not just blocking-IO. Source: https://github.com/protobuf-ruby/beefcake ### Support Features * Optional fields * Required fields * Repeated fields * Packed Repeated Fields * Varint fields * 32-bit fields * 64-bit fields * Length-delimited fields * Embedded Messages * Unknown fields are ignored (as per spec) * Enums * Defaults (i.e. `optional :foo, :string, :default => "bar"`) * Varint-encoded length-delimited message streams ### Future * Imports * Use package in generation * Groups (would be nice for accessing older protos) ### Further Reading http://code.google.com/apis/protocolbuffers/docs/encoding.html ## Testing rake test Beefcake conducts continuous integration on [Travis CI](http://travis-ci.org). 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). All pull requests automatically trigger a build request. Please ensure that tests succeed. Currently Beefcake is tested and working on: * Ruby 1.9.3 * Ruby 2.0.0 * Ruby 2.1.0 * Ruby 2.1.1 * Ruby 2.1.2 * JRuby in 1.9 mode ## Thank You * Keith Rarick (kr) for help with encoding/decoding. * Aman Gupta (tmm1) for help with cross VM support and performance enhancements. beefcake-1.0.0/LICENSE0000644000175000017500000000204212410623537013471 0ustar debiandebianCopyright (c) 2011 Blake Mizerany Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. beefcake-1.0.0/beefcake.gemspec0000644000175000017500000000172012410623537015560 0ustar debiandebian# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "beefcake/version" Gem::Specification.new do |s| s.name = "beefcake" s.version = Beefcake::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Blake Mizerany", "Matt Proud", "Bryce Kerley"] s.email = ["blake.mizerany@gmail.com", "matt.proud@gmail.com", "bkerley@brycekerley.net"] s.homepage = "https://github.com/protobuf-ruby/beefcake" s.summary = %q{A sane protobuf library for Ruby} s.description = %q{A sane protobuf library for Ruby} s.license = 'MIT' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] s.required_ruby_version = '>= 1.9.3' s.add_development_dependency('rake', '~> 10.1.0') s.add_development_dependency('minitest', '~> 5.3') end beefcake-1.0.0/.gitignore0000644000175000017500000000005112410623537014452 0ustar debiandebian*.gem Gemfile.lock bench/beefcake.prof.*