From bf2617ed15f95e22bcf41fd9d08e7ce562464f46 Mon Sep 17 00:00:00 2001 From: jfarrell Date: Thu, 26 Jun 2014 22:53:01 -0400 Subject: [PATCH] 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 --- compiler/cpp/src/generate/t_rb_generator.cc | 64 +++++++++++++++++---- lib/rb/Rakefile | 7 ++- lib/rb/spec/Referenced.thrift | 44 ++++++++++++++ lib/rb/spec/ThriftNamespacedSpec.thrift | 53 +++++++++++++++++ lib/rb/spec/namespaced_spec.rb | 62 ++++++++++++++++++++ 5 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 lib/rb/spec/Referenced.thrift create mode 100644 lib/rb/spec/ThriftNamespacedSpec.thrift create mode 100644 lib/rb/spec/namespaced_spec.rb diff --git a/compiler/cpp/src/generate/t_rb_generator.cc b/compiler/cpp/src/generate/t_rb_generator.cc index 2a6a4721..dec53ddd 100644 --- a/compiler/cpp/src/generate/t_rb_generator.cc +++ b/compiler/cpp/src/generate/t_rb_generator.cc @@ -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 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& 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 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") diff --git a/lib/rb/Rakefile b/lib/rb/Rakefile index f533d068..ef00a9f5 100644 --- a/lib/rb/Rakefile +++ b/lib/rb/Rakefile @@ -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" diff --git a/lib/rb/spec/Referenced.thrift b/lib/rb/spec/Referenced.thrift new file mode 100644 index 00000000..98f183fe --- /dev/null +++ b/lib/rb/spec/Referenced.thrift @@ -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 +} diff --git a/lib/rb/spec/ThriftNamespacedSpec.thrift b/lib/rb/spec/ThriftNamespacedSpec.thrift new file mode 100644 index 00000000..02f28895 --- /dev/null +++ b/lib/rb/spec/ThriftNamespacedSpec.thrift @@ -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) +} diff --git a/lib/rb/spec/namespaced_spec.rb b/lib/rb/spec/namespaced_spec.rb new file mode 100644 index 00000000..8d4f88be --- /dev/null +++ b/lib/rb/spec/namespaced_spec.rb @@ -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 -- 2.17.1