c558aa75a3
This patch has almost no change in behaviour where users have not patched the implementation of new on either a specific proto object class, or `Google::Protobuf::MessageExts::ClassMethods`. The default implementation of `new`, and `rb_class_new_instance` have the same behaviour. By default when we call `new` on a class in Ruby, it goes to the `Class` class's implementation: ```ruby class Foo end >> Foo.method(:new).owner => Class ``` the `Class` implementation of `new` is (pseudocode, it's actually in c): ```ruby class Class def new(*args, &blk) instance = alloc instance.initialize(*args, &blk) instance end end ``` `rb_class_new_instance` does the same thing, it calls down to [`rb_class_s_new`](https://github.com/ruby/ruby/blob/v2_5_5/object.c#L2147), which calls `rb_class_alloc`, then `rb_obj_call_init`. `rb_funcall` is a variadic c function for calling a ruby method on an object, it takes: * A `VALUE` on to which the method should be called * An `ID` which should be an ID of a method, usually created with `rb_intern`, to get an ID from a string * An integer, the number of arguments calling the method with, * A number of `VALUE`s, to send to the method call. `rb_funcall` is the same as calling a method directly in Ruby, and will perform ancestor chain respecting method lookup. This means that in C extensions, if nobody has defined the `new` method on any classes or modules in a class's inheritance chain calling `rb_class_new_instance` is the same as calling `rb_funcall(klass, rb_intern("new"))`, *however* this removes the ability for users to define or monkey patch their own constructors in to the objects created by the C extension. In Ads, we define [`new`](https://git.io/JvFC9) on `Google::Protobuf::MessageExts::ClassMethods` to allow us to insert a monkeypatch which makes it possible to assign primitive values to wrapper type fields (e.g. Google::Protobuf::StringValue). The monkeypatch we apply works for objects that we create for the user via the `new` method. Before this commit, however, the patch does not work for the `decode` method, for the reasons outlined above. Before this commit, protobuf uses `rb_class_new_instance`. After this commit, we use `rb_funcall(klass, rb_intern("new"), 0);` to construct protobuf objects during decoding. While I haven't measured it this will have a very minor performance impact for decoding (`rb_funcall` will have to go to the method cache, which `rb_class_new_instance` will not). This does however do the "more rubyish" thing of respecting the protobuf object's inheritance chain to construct them during decoding. I have run both Ads and Cloud's test suites for Ruby libraries against this patch, as well as the protobuf Ruby gem's test suite locally. |
||
---|---|---|
.. | ||
compatibility_tests/v3.0.0 | ||
ext/google/protobuf_c | ||
lib/google | ||
src/main | ||
tests | ||
.gitignore | ||
Gemfile | ||
google-protobuf.gemspec | ||
pom.xml | ||
Rakefile | ||
README.md | ||
travis-test.sh |
This directory contains the Ruby extension that implements Protocol Buffers functionality in Ruby.
The Ruby extension makes use of generated Ruby code that defines message and enum types in a Ruby DSL. You may write definitions in this DSL directly, but we recommend using protoc's Ruby generation support with .proto files. The build process in this directory only installs the extension; you need to install protoc as well to have Ruby code generation functionality.
Installation from Gem
In Gemfile (Please check a version of Protocol Buffers you needed RubyGems):
gem 'google-protobuf'
Or for using this pre-packaged gem, simply install it as you would any other gem:
$ gem install [--prerelease] google-protobuf
Once the gem is installed, you may or may not need protoc
. If you write your
message type descriptions directly in the Ruby DSL, you do not need it.
However, if you wish to generate the Ruby DSL from a .proto
file, you will
also want to install Protocol Buffers itself, as described in this repository's
main README
file. The version of protoc
included in the latest release
supports the --ruby_out
option to generate Ruby code.
A simple example of using the Ruby extension follows. More extensive
documentation may be found in the RubyDoc comments (call-seq
tags) in the
source, and we plan to release separate, more detailed, documentation at a
later date.
require 'google/protobuf'
# generated from my_proto_types.proto with protoc:
# $ protoc --ruby_out=. my_proto_types.proto
require 'my_proto_types'
mymessage = MyTestMessage.new(:field1 => 42, :field2 => ["a", "b", "c"])
mymessage.field1 = 43
mymessage.field2.push("d")
mymessage.field3 = SubMessage.new(:foo => 100)
encoded_data = MyTestMessage.encode(mymessage)
decoded = MyTestMessage.decode(encoded_data)
assert decoded == mymessage
puts "JSON:"
puts MyTestMessage.encode_json(mymessage)
Installation from Source (Building Gem)
To build this Ruby extension, you will need:
- Rake
- Bundler
- Ruby development headers
- a C compiler
To Build the JRuby extension, you will need:
- Maven
- The latest version of the protobuf java library (see ../java/README.md)
- Install JRuby via rbenv or RVM
First switch to the desired platform with rbenv or RVM.
Then install the required Ruby gems:
$ gem install bundler
$ bundle
Then build the Gem:
$ rake
$ rake clobber_package gem
$ gem install `ls pkg/google-protobuf-*.gem`
To run the specs:
$ rake test
This gem includes the upb parsing and serialization library as a single-file
amalgamation. It is up-to-date with upb git commit
535bc2fe2f2b467f59347ffc9449e11e47791257
.
Version Number Scheme
We are using a version number scheme that is a hybrid of Protocol Buffers' overall version number and some Ruby-specific rules. Gem does not allow re-uploads of a gem with the same version number, so we add a sequence number ("upload version") to the version. We also format alphabetical tags (alpha, pre, ...) slightly differently, and we avoid hyphens. In more detail:
- First, we determine the prefix: a Protocol Buffers version "3.0.0-alpha-2" becomes "3.0.0.alpha.2". When we release 3.0.0, this prefix will be simply "3.0.0".
- We then append the upload version: "3.0.0.alpha.2.0" or "3.0.0.0". If we need to upload a new version of the gem to fix an issue, the version becomes "3.0.0.alpha.2.1" or "3.0.0.1".
- If we are working on a prerelease version, we append a prerelease tag: "3.0.0.alpha.3.0.pre". The prerelease tag comes at the end so that when version numbers are sorted, any prerelease builds are ordered between the prior version and current version.
These rules are designed to work with the sorting rules for Gem::Version: release numbers should sort in actual release order.