Thrift-THRIFT-2574: Compiler option to generate namespace directories for Ruby

Client: rb
Patch: Andrew Bloomgarden

Adds option to generate namespaced ruby classes.

Github: closes #140
This commit is contained in:
jfarrell 2014-06-26 22:53:01 -04:00
parent c9bdd50270
commit bf2617ed15
5 changed files with 219 additions and 11 deletions

View File

@ -82,6 +82,7 @@ class t_rb_generator : public t_oop_generator {
out_dir_base_ = "gen-rb";
require_rubygems_ = (parsed_options.find("rubygems") != parsed_options.end());
namespaced_ = (parsed_options.find("namespaced") != parsed_options.end());
}
/**
@ -201,7 +202,7 @@ class t_rb_generator : public t_oop_generator {
std::string function_signature(t_function* tfunction, std::string prefix="");
std::string argument_list(t_struct* tstruct);
std::string type_to_enum(t_type* ttype);
std::string rb_namespace_to_path_prefix(std::string rb_namespace);
std::vector<std::string> ruby_modules(t_program* p) {
@ -238,8 +239,14 @@ class t_rb_generator : public t_oop_generator {
t_rb_ofstream f_consts_;
t_rb_ofstream f_service_;
std::string namespace_dir_;
std::string require_prefix_;
/** If true, add a "require 'rubygems'" line to the top of each gen-rb file. */
bool require_rubygems_;
/** If true, generate files in idiomatic namespaced directories. */
bool namespaced_;
};
@ -250,14 +257,31 @@ class t_rb_generator : public t_oop_generator {
* @param tprogram The program to generate
*/
void t_rb_generator::init_generator() {
string subdir = get_out_dir();
// Make output directory
MKDIR(get_out_dir().c_str());
MKDIR(subdir.c_str());
if (namespaced_) {
require_prefix_ = rb_namespace_to_path_prefix(program_->get_namespace("rb"));
string dir = require_prefix_;
string::size_type loc;
while ((loc = dir.find("/")) != string::npos) {
subdir = subdir + dir.substr(0, loc) + "/";
MKDIR(subdir.c_str());
dir = dir.substr(loc+1);
}
}
namespace_dir_ = subdir;
// Make output file
string f_types_name = get_out_dir()+underscore(program_name_)+"_types.rb";
string f_types_name = namespace_dir_+underscore(program_name_)+"_types.rb";
f_types_.open(f_types_name.c_str());
string f_consts_name = get_out_dir()+underscore(program_name_)+"_constants.rb";
string f_consts_name = namespace_dir_+underscore(program_name_)+"_constants.rb";
f_consts_.open(f_consts_name.c_str());
// Print header
@ -268,7 +292,7 @@ void t_rb_generator::init_generator() {
f_consts_ <<
rb_autogen_comment() << endl << render_require_thrift() <<
"require '" << underscore(program_name_) << "_types'" << endl <<
"require '" << require_prefix_ << underscore(program_name_) << "_types'" << endl <<
endl;
begin_namespace(f_consts_, ruby_modules(program_));
@ -292,7 +316,10 @@ string t_rb_generator::render_includes() {
const vector<t_program*>& includes = program_->get_includes();
string result = "";
for (size_t i = 0; i < includes.size(); ++i) {
result += "require '" + underscore(includes[i]->get_name()) + "_types'\n";
t_program* included = includes[i];
std::string included_require_prefix = rb_namespace_to_path_prefix(included->get_namespace("rb"));
std::string included_name = included->get_name();
result += "require '" + included_require_prefix + underscore(included_name) + "_types'\n";
}
if (includes.size() > 0) {
result += "\n";
@ -728,7 +755,7 @@ void t_rb_generator::end_namespace(t_rb_ofstream& out, vector<std::string> modul
* @param tservice The service definition
*/
void t_rb_generator::generate_service(t_service* tservice) {
string f_service_name = get_out_dir()+underscore(service_name_)+".rb";
string f_service_name = namespace_dir_+underscore(service_name_)+".rb";
f_service_.open(f_service_name.c_str());
f_service_ <<
@ -736,11 +763,11 @@ void t_rb_generator::generate_service(t_service* tservice) {
if (tservice->get_extends() != NULL) {
f_service_ <<
"require '" << underscore(tservice->get_extends()->get_name()) << "'" << endl;
"require '" << require_prefix_ << underscore(tservice->get_extends()->get_name()) << "'" << endl;
}
f_service_ <<
"require '" << underscore(program_name_) << "_types'" << endl <<
"require '" << require_prefix_ << underscore(program_name_) << "_types'" << endl <<
endl;
begin_namespace(f_service_, ruby_modules(tservice->get_program()));
@ -1138,6 +1165,22 @@ string t_rb_generator::type_to_enum(t_type* type) {
throw "INVALID TYPE IN type_to_enum: " + type->get_name();
}
string t_rb_generator::rb_namespace_to_path_prefix(string rb_namespace) {
string namespaces_left = rb_namespace;
string::size_type loc;
string path_prefix = "";
while ((loc = namespaces_left.find(".")) != string::npos) {
path_prefix = path_prefix + underscore(namespaces_left.substr(0, loc)) + "/";
namespaces_left = namespaces_left.substr(loc+1);
}
if (namespaces_left.size() > 0) {
path_prefix = path_prefix + underscore(namespaces_left) + "/";
}
return path_prefix;
}
void t_rb_generator::generate_rdoc(t_rb_ofstream& out, t_doc* tdoc) {
if (tdoc->has_doc()) {
@ -1221,4 +1264,5 @@ void t_rb_generator::generate_rb_union_validator(t_rb_ofstream& out,
}
THRIFT_REGISTER_GENERATOR(rb, "Ruby",
" rubygems: Add a \"require 'rubygems'\" line to the top of each generated file.\n")
" rubygems: Add a \"require 'rubygems'\" line to the top of each generated file.\n"
" namespaced: Generate files in idiomatic namespaced directories.\n")

View File

@ -38,13 +38,18 @@ RSpec::Core::RakeTask.new(:'spec:rcov') do |t|
end
desc 'Compile the .thrift files for the specs'
task :'gen-rb' => [:'gen-rb:spec', :'gen-rb:benchmark', :'gen-rb:debug_proto']
task :'gen-rb' => [:'gen-rb:spec', :'gen-rb:namespaced_spec', :'gen-rb:benchmark', :'gen-rb:debug_proto']
namespace :'gen-rb' do
task :'spec' do
dir = File.dirname(__FILE__) + '/spec'
sh THRIFT, '--gen', 'rb', '-o', dir, "#{dir}/ThriftSpec.thrift"
end
task :'namespaced_spec' do
dir = File.dirname(__FILE__) + '/spec'
sh THRIFT, '--gen', 'rb:namespaced', '-recurse', '-o', dir, "#{dir}/ThriftNamespacedSpec.thrift"
end
task :'benchmark' do
dir = File.dirname(__FILE__) + '/benchmark'
sh THRIFT, '--gen', 'rb', '-o', dir, "#{dir}/Benchmark.thrift"

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
namespace rb OtherNamespace
enum SomeEnum {
ONE
TWO
}

View File

@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
namespace rb NamespacedSpecNamespace
include "Referenced.thrift"
struct Hello {
1: string greeting = "hello world"
}
service NamespacedNonblockingService {
Hello greeting(1:bool english)
bool block()
oneway void unblock(1:i32 n)
oneway void shutdown()
void sleep(1:double seconds)
}

View File

@ -0,0 +1,62 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
require 'spec_helper'
describe 'namespaced generation' do
before do
require 'namespaced_spec_namespace/namespaced_nonblocking_service'
end
it "generated the right files" do
prefix = File.expand_path("../gen-rb", __FILE__)
["namespaced_spec_namespace/namespaced_nonblocking_service.rb",
"namespaced_spec_namespace/thrift_namespaced_spec_constants.rb",
"namespaced_spec_namespace/thrift_namespaced_spec_types.rb",
"other_namespace/referenced_constants.rb",
"other_namespace/referenced_types.rb"
].each do |name|
File.exist?(File.join(prefix, name)).should be_true
end
end
it "did not generate the wrong files" do
prefix = File.expand_path("../gen-rb", __FILE__)
["namespaced_nonblocking_service.rb",
"thrift_namespaced_spec_constants.rb",
"thrift_namespaced_spec_types.rb",
"referenced_constants.rb",
"referenced_types.rb"
].each do |name|
File.exist?(File.join(prefix, name)).should_not be_true
end
end
it "has a service class in the right place" do
defined?(NamespacedSpecNamespace::NamespacedNonblockingService).should be_true
end
it "has a struct in the right place" do
defined?(NamespacedSpecNamespace::Hello).should be_true
end
it "required an included file" do
defined?(OtherNamespace::SomeEnum).should be_true
end
end