aboutsummaryrefslogtreecommitdiff
path: root/src/google/protobuf/compiler/ruby
diff options
context:
space:
mode:
authorJosh Haberman <jhaberman@gmail.com>2015-02-17 18:25:20 -0800
committerJosh Haberman <jhaberman@gmail.com>2015-02-17 18:25:20 -0800
commit55cf8abecbafc2b47c8060df88cc103859c67138 (patch)
tree1cc1c4303bc868f14870f29d0a4d2c594cd1cca8 /src/google/protobuf/compiler/ruby
parentcb3caf1e61126fd18fa63e2a0e91bf71ab4ac3c9 (diff)
parent2cb2358cba7eb296d6243912758b9e9c78d1502e (diff)
downloadprotobuf-55cf8abecbafc2b47c8060df88cc103859c67138.tar.gz
protobuf-55cf8abecbafc2b47c8060df88cc103859c67138.tar.bz2
protobuf-55cf8abecbafc2b47c8060df88cc103859c67138.zip
Merge branch 'master' of github.com:google/protobuf
Diffstat (limited to 'src/google/protobuf/compiler/ruby')
-rw-r--r--src/google/protobuf/compiler/ruby/ruby_generator.cc97
-rw-r--r--src/google/protobuf/compiler/ruby/ruby_generator.h3
-rw-r--r--src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc119
3 files changed, 209 insertions, 10 deletions
diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc
index 6fed1d46..a9b6837e 100644
--- a/src/google/protobuf/compiler/ruby/ruby_generator.cc
+++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc
@@ -100,15 +100,35 @@ std::string TypeName(const google::protobuf::FieldDescriptor* field) {
}
}
-void GenerateMessage(const google::protobuf::Descriptor* message,
- google::protobuf::io::Printer* printer) {
- printer->Print(
- "add_message \"$name$\" do\n",
- "name", message->full_name());
- printer->Indent();
+void GenerateField(const google::protobuf::FieldDescriptor* field,
+ google::protobuf::io::Printer* printer) {
+
+ if (field->is_map()) {
+ const FieldDescriptor* key_field =
+ field->message_type()->FindFieldByNumber(1);
+ const FieldDescriptor* value_field =
+ field->message_type()->FindFieldByNumber(2);
+
+ printer->Print(
+ "map :$name$, :$key_type$, :$value_type$, $number$",
+ "name", field->name(),
+ "key_type", TypeName(key_field),
+ "value_type", TypeName(value_field),
+ "number", IntToString(field->number()));
+
+ if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ printer->Print(
+ ", \"$subtype$\"\n",
+ "subtype", value_field->message_type()->full_name());
+ } else if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ printer->Print(
+ ", \"$subtype$\"\n",
+ "subtype", value_field->enum_type()->full_name());
+ } else {
+ printer->Print("\n");
+ }
+ } else {
- for (int i = 0; i < message->field_count(); i++) {
- const FieldDescriptor* field = message->field(i);
printer->Print(
"$label$ :$name$, ",
"label", LabelForField(field),
@@ -117,6 +137,7 @@ void GenerateMessage(const google::protobuf::Descriptor* message,
":$type$, $number$",
"type", TypeName(field),
"number", IntToString(field->number()));
+
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
printer->Print(
", \"$subtype$\"\n",
@@ -129,6 +150,49 @@ void GenerateMessage(const google::protobuf::Descriptor* message,
printer->Print("\n");
}
}
+}
+
+void GenerateOneof(const google::protobuf::OneofDescriptor* oneof,
+ google::protobuf::io::Printer* printer) {
+ printer->Print(
+ "oneof :$name$ do\n",
+ "name", oneof->name());
+ printer->Indent();
+
+ for (int i = 0; i < oneof->field_count(); i++) {
+ const FieldDescriptor* field = oneof->field(i);
+ GenerateField(field, printer);
+ }
+
+ printer->Outdent();
+ printer->Print("end\n");
+}
+
+void GenerateMessage(const google::protobuf::Descriptor* message,
+ google::protobuf::io::Printer* printer) {
+
+ // Don't generate MapEntry messages -- we use the Ruby extension's native
+ // support for map fields instead.
+ if (message->options().map_entry()) {
+ return;
+ }
+
+ printer->Print(
+ "add_message \"$name$\" do\n",
+ "name", message->full_name());
+ printer->Indent();
+
+ for (int i = 0; i < message->field_count(); i++) {
+ const FieldDescriptor* field = message->field(i);
+ if (!field->containing_oneof()) {
+ GenerateField(field, printer);
+ }
+ }
+
+ for (int i = 0; i < message->oneof_decl_count(); i++) {
+ const OneofDescriptor* oneof = message->oneof_decl(i);
+ GenerateOneof(oneof, printer);
+ }
printer->Outdent();
printer->Print("end\n");
@@ -185,6 +249,13 @@ void GenerateMessageAssignment(
const std::string& prefix,
const google::protobuf::Descriptor* message,
google::protobuf::io::Printer* printer) {
+
+ // Don't generate MapEntry messages -- we use the Ruby extension's native
+ // support for map fields instead.
+ if (message->options().map_entry()) {
+ return;
+ }
+
printer->Print(
"$prefix$$name$ = ",
"prefix", prefix,
@@ -261,7 +332,7 @@ void GenerateFile(const google::protobuf::FileDescriptor* file,
"filename", file->name());
printer->Print(
- "require 'protobuf'\n\n");
+ "require 'google/protobuf'\n\n");
for (int i = 0; i < file->dependency_count(); i++) {
const std::string& name = file->dependency(i)->name();
@@ -297,6 +368,14 @@ bool Generator::Generate(
const string& parameter,
GeneratorContext* generator_context,
string* error) const {
+
+ if (file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
+ *error =
+ "Can only generate Ruby code for proto3 .proto files.\n"
+ "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
+ return false;
+ }
+
std::string filename =
StripDotProto(file->name()) + ".rb";
scoped_ptr<io::ZeroCopyOutputStream> output(
diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.h b/src/google/protobuf/compiler/ruby/ruby_generator.h
index 48dbefd1..75555c31 100644
--- a/src/google/protobuf/compiler/ruby/ruby_generator.h
+++ b/src/google/protobuf/compiler/ruby/ruby_generator.h
@@ -40,7 +40,8 @@ namespace protobuf {
namespace compiler {
namespace ruby {
-class Generator : public google::protobuf::compiler::CodeGenerator {
+class LIBPROTOC_EXPORT Generator
+ : public google::protobuf::compiler::CodeGenerator {
virtual bool Generate(
const FileDescriptor* file,
const string& parameter,
diff --git a/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc b/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc
new file mode 100644
index 00000000..e35ca695
--- /dev/null
+++ b/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc
@@ -0,0 +1,119 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2014 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <memory>
+
+#include <google/protobuf/compiler/ruby/ruby_generator.h>
+#include <google/protobuf/compiler/command_line_interface.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/printer.h>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+#include <google/protobuf/testing/file.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace ruby {
+namespace {
+
+string FindRubyTestDir() {
+ // Inspired by TestSourceDir() in src/google/protobuf/testing/googletest.cc.
+ string prefix = ".";
+ while (!File::Exists(prefix + "/ruby/tests")) {
+ if (!File::Exists(prefix)) {
+ GOOGLE_LOG(FATAL)
+ << "Could not find Ruby test directory. Please run tests from "
+ "somewhere within the protobuf source package.";
+ }
+ prefix += "/..";
+ }
+ return prefix + "/ruby/tests";
+}
+
+// This test is a simple golden-file test over the output of the Ruby code
+// generator. When we make changes to the Ruby extension and alter the Ruby code
+// generator to use those changes, we should (i) manually test the output of the
+// code generator with the extension, and (ii) update the golden output above.
+// Some day, we may integrate build systems between protoc and the language
+// extensions to the point where we can do this test in a more automated way.
+
+TEST(RubyGeneratorTest, GeneratorTest) {
+ string ruby_tests = FindRubyTestDir();
+
+ google::protobuf::compiler::CommandLineInterface cli;
+ cli.SetInputsAreProtoPathRelative(true);
+
+ ruby::Generator ruby_generator;
+ cli.RegisterGenerator("--ruby_out", &ruby_generator, "");
+
+ // Copy generated_code.proto to the temporary test directory.
+ string test_input;
+ GOOGLE_CHECK_OK(File::GetContents(
+ ruby_tests + "/generated_code.proto",
+ &test_input,
+ true));
+ GOOGLE_CHECK_OK(File::SetContents(
+ TestTempDir() + "/generated_code.proto",
+ test_input,
+ true));
+
+ // Invoke the proto compiler (we will be inside TestTempDir() at this point).
+ string ruby_out = "--ruby_out=" + TestTempDir();
+ string proto_path = "--proto_path=" + TestTempDir();
+ const char* argv[] = {
+ "protoc",
+ ruby_out.c_str(),
+ proto_path.c_str(),
+ "generated_code.proto",
+ };
+
+ EXPECT_EQ(0, cli.Run(4, argv));
+
+ // Load the generated output and compare to the expected result.
+ string output;
+ GOOGLE_CHECK_OK(File::GetContents(
+ TestTempDir() + "/generated_code.rb",
+ &output,
+ true));
+ string expected_output;
+ GOOGLE_CHECK_OK(File::GetContents(
+ ruby_tests + "/generated_code.rb",
+ &expected_output,
+ true));
+ EXPECT_EQ(expected_output, output);
+}
+
+} // namespace
+} // namespace ruby
+} // namespace compiler
+} // namespace protobuf
+} // namespace google