blob: 708cd42a85d2f0fbbd908a694ff702967c3386c8 [file] [log] [blame]
/*
* 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.
*/
#include <string>
#include <fstream>
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sstream>
#include <boost/tokenizer.hpp>
#include "t_oop_generator.h"
#include "platform.h"
using namespace std;
/**
* Ruby code generator.
*
*/
class t_rb_generator : public t_oop_generator {
public:
t_rb_generator(
t_program* program,
const std::map<std::string, std::string>& parsed_options,
const std::string& option_string)
: t_oop_generator(program)
{
out_dir_base_ = "gen-rb";
}
/**
* Init and close methods
*/
void init_generator();
void close_generator();
/**
* Program-level generation functions
*/
void generate_typedef (t_typedef* ttypedef);
void generate_enum (t_enum* tenum);
void generate_const (t_const* tconst);
void generate_struct (t_struct* tstruct);
void generate_xception (t_struct* txception);
void generate_service (t_service* tservice);
std::string render_const_value(t_type* type, t_const_value* value);
/**
* Struct generation code
*/
void generate_rb_struct(std::ofstream& out, t_struct* tstruct, bool is_exception);
void generate_rb_struct_required_validator(std::ofstream& out, t_struct* tstruct);
void generate_rb_function_helpers(t_function* tfunction);
void generate_rb_simple_constructor(std::ofstream& out, t_struct* tstruct);
void generate_rb_simple_exception_constructor(std::ofstream& out, t_struct* tstruct);
void generate_field_constants (std::ofstream& out, t_struct* tstruct);
void generate_accessors (std::ofstream& out, t_struct* tstruct);
void generate_field_defns (std::ofstream& out, t_struct* tstruct);
void generate_field_data (std::ofstream& out, t_type* field_type, const std::string& field_name, t_const_value* field_value, bool optional);
/**
* Service-level generation functions
*/
void generate_service_helpers (t_service* tservice);
void generate_service_interface (t_service* tservice);
void generate_service_client (t_service* tservice);
void generate_service_server (t_service* tservice);
void generate_process_function (t_service* tservice, t_function* tfunction);
/**
* Serialization constructs
*/
void generate_deserialize_field (std::ofstream &out,
t_field* tfield,
std::string prefix="",
bool inclass=false);
void generate_deserialize_struct (std::ofstream &out,
t_struct* tstruct,
std::string prefix="");
void generate_deserialize_container (std::ofstream &out,
t_type* ttype,
std::string prefix="");
void generate_deserialize_set_element (std::ofstream &out,
t_set* tset,
std::string prefix="");
void generate_deserialize_map_element (std::ofstream &out,
t_map* tmap,
std::string prefix="");
void generate_deserialize_list_element (std::ofstream &out,
t_list* tlist,
std::string prefix="");
void generate_serialize_field (std::ofstream &out,
t_field* tfield,
std::string prefix="");
void generate_serialize_struct (std::ofstream &out,
t_struct* tstruct,
std::string prefix="");
void generate_serialize_container (std::ofstream &out,
t_type* ttype,
std::string prefix="");
void generate_serialize_map_element (std::ofstream &out,
t_map* tmap,
std::string kiter,
std::string viter);
void generate_serialize_set_element (std::ofstream &out,
t_set* tmap,
std::string iter);
void generate_serialize_list_element (std::ofstream &out,
t_list* tlist,
std::string iter);
void generate_rdoc (std::ofstream& out,
t_doc* tdoc);
/**
* Helper rendering functions
*/
std::string rb_autogen_comment();
std::string render_includes();
std::string declare_field(t_field* tfield);
std::string type_name(t_type* ttype);
std::string full_type_name(t_type* ttype);
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::vector<std::string> ruby_modules(t_program* p) {
std::string ns = p->get_namespace("rb");
boost::tokenizer<> tok(ns);
std::vector<std::string> modules;
for(boost::tokenizer<>::iterator beg=tok.begin(); beg != tok.end(); ++beg) {
modules.push_back(capitalize(*beg));
}
return modules;
}
void begin_namespace(std::ofstream&, std::vector<std::string>);
void end_namespace(std::ofstream&, std::vector<std::string>);
private:
/**
* File streams
*/
std::ofstream f_types_;
std::ofstream f_consts_;
std::ofstream f_service_;
};
/**
* Prepares for file generation by opening up the necessary file output
* streams.
*
* @param tprogram The program to generate
*/
void t_rb_generator::init_generator() {
// Make output directory
MKDIR(get_out_dir().c_str());
// Make output file
string f_types_name = get_out_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";
f_consts_.open(f_consts_name.c_str());
// Print header
f_types_ <<
rb_autogen_comment() << endl <<
render_includes() << endl;
begin_namespace(f_types_, ruby_modules(program_));
f_consts_ <<
rb_autogen_comment() << endl <<
"require File.dirname(__FILE__) + '/" << underscore(program_name_) << "_types'" << endl <<
endl;
begin_namespace(f_consts_, ruby_modules(program_));
}
/**
* Renders all the imports necessary for including another Thrift program
*/
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";
}
if (includes.size() > 0) {
result += "\n";
}
return result;
}
/**
* Autogen'd comment
*/
string t_rb_generator::rb_autogen_comment() {
return
std::string("#\n") +
"# Autogenerated by Thrift\n" +
"#\n" +
"# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" +
"#\n";
}
/**
* Closes the type files
*/
void t_rb_generator::close_generator() {
// Close types file
end_namespace(f_types_, ruby_modules(program_));
end_namespace(f_consts_, ruby_modules(program_));
f_types_.close();
f_consts_.close();
}
/**
* Generates a typedef. This is not done in Ruby, types are all implicit.
*
* @param ttypedef The type definition
*/
void t_rb_generator::generate_typedef(t_typedef* ttypedef) {}
/**
* Generates code for an enumerated type. Done using a class to scope
* the values.
*
* @param tenum The enumeration
*/
void t_rb_generator::generate_enum(t_enum* tenum) {
indent(f_types_) <<
"module " << capitalize(tenum->get_name()) << endl;
indent_up();
vector<t_enum_value*> constants = tenum->get_constants();
vector<t_enum_value*>::iterator c_iter;
int value = -1;
for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
if ((*c_iter)->has_value()) {
value = (*c_iter)->get_value();
} else {
++value;
}
// Ruby class constants have to be capitalized... omg i am so on the fence
// about languages strictly enforcing capitalization why can't we just all
// agree and play nice.
string name = capitalize((*c_iter)->get_name());
f_types_ <<
indent() << name << " = " << value << endl;
}
// Create a set with valid values for this enum
indent(f_types_) << "VALID_VALUES = Set.new([";
bool first = true;
for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
// Populate the set
first ? first = false: f_types_ << ", ";
f_types_ << capitalize((*c_iter)->get_name());
}
f_types_ << "]).freeze" << endl;
indent_down();
indent(f_types_) <<
"end" << endl << endl;
}
/**
* Generate a constant value
*/
void t_rb_generator::generate_const(t_const* tconst) {
t_type* type = tconst->get_type();
string name = tconst->get_name();
t_const_value* value = tconst->get_value();
name[0] = toupper(name[0]);
indent(f_consts_) << name << " = " << render_const_value(type, value);
f_consts_ << endl << endl;
}
/**
* Prints the value of a constant with the given type. Note that type checking
* is NOT performed in this function as it is always run beforehand using the
* validate_types method in main.cc
*/
string t_rb_generator::render_const_value(t_type* type, t_const_value* value) {
type = get_true_type(type);
std::ostringstream out;
if (type->is_base_type()) {
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
switch (tbase) {
case t_base_type::TYPE_STRING:
out << "%q\"" << get_escaped_string(value) << '"';
break;
case t_base_type::TYPE_BOOL:
out << (value->get_integer() > 0 ? "true" : "false");
break;
case t_base_type::TYPE_BYTE:
case t_base_type::TYPE_I16:
case t_base_type::TYPE_I32:
case t_base_type::TYPE_I64:
out << value->get_integer();
break;
case t_base_type::TYPE_DOUBLE:
if (value->get_type() == t_const_value::CV_INTEGER) {
out << value->get_integer();
} else {
out << value->get_double();
}
break;
default:
throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
}
} else if (type->is_enum()) {
indent(out) << value->get_integer();
} else if (type->is_struct() || type->is_xception()) {
out << type->get_name() << ".new({" << endl;
indent_up();
const vector<t_field*>& fields = ((t_struct*)type)->get_members();
vector<t_field*>::const_iterator f_iter;
const map<t_const_value*, t_const_value*>& val = value->get_map();
map<t_const_value*, t_const_value*>::const_iterator v_iter;
for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
t_type* field_type = NULL;
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
if ((*f_iter)->get_name() == v_iter->first->get_string()) {
field_type = (*f_iter)->get_type();
}
}
if (field_type == NULL) {
throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
}
out << indent();
out << render_const_value(g_type_string, v_iter->first);
out << " => ";
out << render_const_value(field_type, v_iter->second);
out << "," << endl;
}
indent_down();
indent(out) << "})";
} else if (type->is_map()) {
t_type* ktype = ((t_map*)type)->get_key_type();
t_type* vtype = ((t_map*)type)->get_val_type();
out << "{" << endl;
indent_up();
const map<t_const_value*, t_const_value*>& val = value->get_map();
map<t_const_value*, t_const_value*>::const_iterator v_iter;
for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
out << indent();
out << render_const_value(ktype, v_iter->first);
out << " => ";
out << render_const_value(vtype, v_iter->second);
out << "," << endl;
}
indent_down();
indent(out) << "}";
} else if (type->is_list() || type->is_set()) {
t_type* etype;
if (type->is_list()) {
etype = ((t_list*)type)->get_elem_type();
} else {
etype = ((t_set*)type)->get_elem_type();
}
if (type->is_set()) {
out << "Set.new([";
} else {
out << "[" << endl;
}
indent_up();
const vector<t_const_value*>& val = value->get_list();
vector<t_const_value*>::const_iterator v_iter;
for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
out << indent();
out << render_const_value(etype, *v_iter);
out << "," << endl;
}
indent_down();
if (type->is_set()) {
indent(out) << "])";
} else {
indent(out) << "]";
}
} else {
throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
}
return out.str();
}
/**
* Generates a ruby struct
*/
void t_rb_generator::generate_struct(t_struct* tstruct) {
generate_rb_struct(f_types_, tstruct, false);
}
/**
* Generates a struct definition for a thrift exception. Basically the same
* as a struct but extends the Exception class.
*
* @param txception The struct definition
*/
void t_rb_generator::generate_xception(t_struct* txception) {
generate_rb_struct(f_types_, txception, true);
}
/**
* Generates a ruby struct
*/
void t_rb_generator::generate_rb_struct(std::ofstream& out, t_struct* tstruct, bool is_exception = false) {
generate_rdoc(out, tstruct);
indent(out) << "class " << type_name(tstruct);
if (is_exception) {
out << " < ::Thrift::Exception";
}
out << endl;
indent_up();
indent(out) << "include ::Thrift::Struct" << endl;
if (is_exception) {
generate_rb_simple_exception_constructor(out, tstruct);
}
generate_field_constants(out, tstruct);
generate_accessors(out, tstruct);
generate_field_defns(out, tstruct);
generate_rb_struct_required_validator(out, tstruct);
indent_down();
indent(out) << "end" << endl << endl;
}
void t_rb_generator::generate_rb_simple_exception_constructor(std::ofstream& out, t_struct* tstruct) {
const vector<t_field*>& members = tstruct->get_members();
if (members.size() == 1) {
vector<t_field*>::const_iterator m_iter = members.begin();
if ((*m_iter)->get_type()->is_string()) {
string name = (*m_iter)->get_name();
indent(out) << "def initialize(message=nil)" << endl;
indent_up();
indent(out) << "super()" << endl;
indent(out) << "self." << name << " = message" << endl;
indent_down();
indent(out) << "end" << endl << endl;
if (name != "message") {
indent(out) << "def message; " << name << " end" << endl << endl;
}
}
}
}
void t_rb_generator::generate_field_constants(std::ofstream& out, t_struct* tstruct) {
const vector<t_field*>& fields = tstruct->get_members();
vector<t_field*>::const_iterator f_iter;
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
std::string field_name = (*f_iter)->get_name();
std::string cap_field_name = upcase_string(field_name);
indent(out) << cap_field_name << " = " << (*f_iter)->get_key() << endl;
}
out << endl;
}
void t_rb_generator::generate_accessors(std::ofstream& out, t_struct* tstruct) {
const vector<t_field*>& members = tstruct->get_members();
vector<t_field*>::const_iterator m_iter;
if (members.size() > 0) {
indent(out) << "::Thrift::Struct.field_accessor self";
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
out << ", :" << (*m_iter)->get_name();
}
out << endl;
}
}
void t_rb_generator::generate_field_defns(std::ofstream& out, t_struct* tstruct) {
const vector<t_field*>& fields = tstruct->get_members();
vector<t_field*>::const_iterator f_iter;
indent(out) << "FIELDS = {" << endl;
indent_up();
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
if (f_iter != fields.begin()) {
out << "," << endl;
}
// generate the field docstrings within the FIELDS constant. no real better place...
generate_rdoc(out, *f_iter);
indent(out) <<
upcase_string((*f_iter)->get_name()) << " => ";
generate_field_data(out, (*f_iter)->get_type(), (*f_iter)->get_name(), (*f_iter)->get_value(),
(*f_iter)->get_req() == t_field::T_OPTIONAL);
}
indent_down();
out << endl;
indent(out) << "}" << endl << endl;
indent(out) << "def struct_fields; FIELDS; end" << endl << endl;
}
void t_rb_generator::generate_field_data(std::ofstream& out, t_type* field_type,
const std::string& field_name = "", t_const_value* field_value = NULL, bool optional = false) {
field_type = get_true_type(field_type);
// Begin this field's defn
out << "{:type => " << type_to_enum(field_type);
if (!field_name.empty()) {
out << ", :name => '" << field_name << "'";
}
if (field_value != NULL) {
out << ", :default => " << render_const_value(field_type, field_value);
}
if (!field_type->is_base_type()) {
if (field_type->is_struct() || field_type->is_xception()) {
out << ", :class => " << full_type_name((t_struct*)field_type);
} else if (field_type->is_list()) {
out << ", :element => ";
generate_field_data(out, ((t_list*)field_type)->get_elem_type());
} else if (field_type->is_map()) {
out << ", :key => ";
generate_field_data(out, ((t_map*)field_type)->get_key_type());
out << ", :value => ";
generate_field_data(out, ((t_map*)field_type)->get_val_type());
} else if (field_type->is_set()) {
out << ", :element => ";
generate_field_data(out, ((t_set*)field_type)->get_elem_type());
}
}
if(optional) {
out << ", :optional => true";
}
if (field_type->is_enum()) {
out << ", :enum_class => " << full_type_name(field_type);
}
// End of this field's defn
out << "}";
}
void t_rb_generator::begin_namespace(std::ofstream& out, vector<std::string> modules) {
for (vector<std::string>::iterator m_iter = modules.begin(); m_iter != modules.end(); ++m_iter) {
indent(out) << "module " << *m_iter << endl;
indent_up();
}
}
void t_rb_generator::end_namespace(std::ofstream& out, vector<std::string> modules) {
for (vector<std::string>::reverse_iterator m_iter = modules.rbegin(); m_iter != modules.rend(); ++m_iter) {
indent_down();
indent(out) << "end" << endl;
}
}
/**
* Generates a thrift service.
*
* @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";
f_service_.open(f_service_name.c_str());
f_service_ <<
rb_autogen_comment() << endl <<
"require 'thrift'" << endl;
if (tservice->get_extends() != NULL) {
f_service_ <<
"require '" << underscore(tservice->get_extends()->get_name()) << "'" << endl;
}
f_service_ <<
"require File.dirname(__FILE__) + '/" << underscore(program_name_) << "_types'" << endl <<
endl;
begin_namespace(f_service_, ruby_modules(tservice->get_program()));
indent(f_service_) << "module " << capitalize(tservice->get_name()) << endl;
indent_up();
// Generate the three main parts of the service (well, two for now in PHP)
generate_service_client(tservice);
generate_service_server(tservice);
generate_service_helpers(tservice);
indent_down();
indent(f_service_) << "end" << endl <<
endl;
end_namespace(f_service_, ruby_modules(tservice->get_program()));
// Close service file
f_service_.close();
}
/**
* Generates helper functions for a service.
*
* @param tservice The service to generate a header definition for
*/
void t_rb_generator::generate_service_helpers(t_service* tservice) {
vector<t_function*> functions = tservice->get_functions();
vector<t_function*>::iterator f_iter;
indent(f_service_) <<
"# HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
t_struct* ts = (*f_iter)->get_arglist();
generate_rb_struct(f_service_, ts);
generate_rb_function_helpers(*f_iter);
}
}
/**
* Generates a struct and helpers for a function.
*
* @param tfunction The function
*/
void t_rb_generator::generate_rb_function_helpers(t_function* tfunction) {
t_struct result(program_, tfunction->get_name() + "_result");
t_field success(tfunction->get_returntype(), "success", 0);
if (!tfunction->get_returntype()->is_void()) {
result.append(&success);
}
t_struct* xs = tfunction->get_xceptions();
const vector<t_field*>& fields = xs->get_members();
vector<t_field*>::const_iterator f_iter;
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
result.append(*f_iter);
}
generate_rb_struct(f_service_, &result);
}
/**
* Generates a service client definition.
*
* @param tservice The service to generate a server for.
*/
void t_rb_generator::generate_service_client(t_service* tservice) {
string extends = "";
string extends_client = "";
if (tservice->get_extends() != NULL) {
extends = full_type_name(tservice->get_extends());
extends_client = " < " + extends + "::Client ";
}
indent(f_service_) <<
"class Client" << extends_client << endl;
indent_up();
indent(f_service_) <<
"include ::Thrift::Client" << endl << endl;
// Generate client method implementations
vector<t_function*> functions = tservice->get_functions();
vector<t_function*>::const_iterator f_iter;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
t_struct* arg_struct = (*f_iter)->get_arglist();
const vector<t_field*>& fields = arg_struct->get_members();
vector<t_field*>::const_iterator fld_iter;
string funname = (*f_iter)->get_name();
// Open function
indent(f_service_) <<
"def " << function_signature(*f_iter) << endl;
indent_up();
indent(f_service_) <<
"send_" << funname << "(";
bool first = true;
for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
if (first) {
first = false;
} else {
f_service_ << ", ";
}
f_service_ << (*fld_iter)->get_name();
}
f_service_ << ")" << endl;
if (!(*f_iter)->is_oneway()) {
f_service_ << indent();
if (!(*f_iter)->get_returntype()->is_void()) {
f_service_ << "return ";
}
f_service_ <<
"recv_" << funname << "()" << endl;
}
indent_down();
indent(f_service_) << "end" << endl;
f_service_ << endl;
indent(f_service_) <<
"def send_" << function_signature(*f_iter) << endl;
indent_up();
std::string argsname = capitalize((*f_iter)->get_name() + "_args");
indent(f_service_) << "send_message('" << funname << "', " << argsname;
for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
f_service_ << ", :" << (*fld_iter)->get_name() << " => " << (*fld_iter)->get_name();
}
f_service_ << ")" << endl;
indent_down();
indent(f_service_) << "end" << endl;
if (!(*f_iter)->is_oneway()) {
std::string resultname = capitalize((*f_iter)->get_name() + "_result");
t_struct noargs(program_);
t_function recv_function((*f_iter)->get_returntype(),
string("recv_") + (*f_iter)->get_name(),
&noargs);
// Open function
f_service_ <<
endl <<
indent() << "def " << function_signature(&recv_function) << endl;
indent_up();
// TODO(mcslee): Validate message reply here, seq ids etc.
f_service_ <<
indent() << "result = receive_message(" << resultname << ")" << endl;
// Careful, only return _result if not a void function
if (!(*f_iter)->get_returntype()->is_void()) {
f_service_ <<
indent() << "return result.success unless result.success.nil?" << endl;
}
t_struct* xs = (*f_iter)->get_xceptions();
const std::vector<t_field*>& xceptions = xs->get_members();
vector<t_field*>::const_iterator x_iter;
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
indent(f_service_) <<
"raise result." << (*x_iter)->get_name() <<
" unless result." << (*x_iter)->get_name() << ".nil?" << endl;
}
// Careful, only return _result if not a void function
if ((*f_iter)->get_returntype()->is_void()) {
indent(f_service_) <<
"return" << endl;
} else {
f_service_ <<
indent() << "raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, '" << (*f_iter)->get_name() << " failed: unknown result')" << endl;
}
// Close function
indent_down();
indent(f_service_) << "end" << endl << endl;
}
}
indent_down();
indent(f_service_) << "end" << endl << endl;
}
/**
* Generates a service server definition.
*
* @param tservice The service to generate a server for.
*/
void t_rb_generator::generate_service_server(t_service* tservice) {
// Generate the dispatch methods
vector<t_function*> functions = tservice->get_functions();
vector<t_function*>::iterator f_iter;
string extends = "";
string extends_processor = "";
if (tservice->get_extends() != NULL) {
extends = full_type_name(tservice->get_extends());
extends_processor = " < " + extends + "::Processor ";
}
// Generate the header portion
indent(f_service_) <<
"class Processor" << extends_processor << endl;
indent_up();
f_service_ <<
indent() << "include ::Thrift::Processor" << endl <<
endl;
// Generate the process subfunctions
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
generate_process_function(tservice, *f_iter);
}
indent_down();
indent(f_service_) << "end" << endl << endl;
}
/**
* Generates a process function definition.
*
* @param tfunction The function to write a dispatcher for
*/
void t_rb_generator::generate_process_function(t_service* tservice,
t_function* tfunction) {
// Open function
indent(f_service_) <<
"def process_" << tfunction->get_name() <<
"(seqid, iprot, oprot)" << endl;
indent_up();
string argsname = capitalize(tfunction->get_name()) + "_args";
string resultname = capitalize(tfunction->get_name()) + "_result";
f_service_ <<
indent() << "args = read_args(iprot, " << argsname << ")" << endl;
t_struct* xs = tfunction->get_xceptions();
const std::vector<t_field*>& xceptions = xs->get_members();
vector<t_field*>::const_iterator x_iter;
// Declare result for non oneway function
if (!tfunction->is_oneway()) {
f_service_ <<
indent() << "result = " << resultname << ".new()" << endl;
}
// Try block for a function with exceptions
if (xceptions.size() > 0) {
f_service_ <<
indent() << "begin" << endl;
indent_up();
}
// Generate the function call
t_struct* arg_struct = tfunction->get_arglist();
const std::vector<t_field*>& fields = arg_struct->get_members();
vector<t_field*>::const_iterator f_iter;
f_service_ << indent();
if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
f_service_ << "result.success = ";
}
f_service_ <<
"@handler." << tfunction->get_name() << "(";
bool first = true;
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
if (first) {
first = false;
} else {
f_service_ << ", ";
}
f_service_ << "args." << (*f_iter)->get_name();
}
f_service_ << ")" << endl;
if (!tfunction->is_oneway() && xceptions.size() > 0) {
indent_down();
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
f_service_ <<
indent() << "rescue " << full_type_name((*x_iter)->get_type()) << " => " << (*x_iter)->get_name() << endl;
if (!tfunction->is_oneway()) {
indent_up();
f_service_ <<
indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() << endl;
indent_down();
}
}
indent(f_service_) << "end" << endl;
}
// Shortcut out here for oneway functions
if (tfunction->is_oneway()) {
f_service_ <<
indent() << "return" << endl;
indent_down();
indent(f_service_) << "end" << endl << endl;
return;
}
f_service_ <<
indent() << "write_result(result, oprot, '" << tfunction->get_name() << "', seqid)" << endl;
// Close function
indent_down();
indent(f_service_) << "end" << endl << endl;
}
/**
* Renders a function signature of the form 'type name(args)'
*
* @param tfunction Function definition
* @return String of rendered function definition
*/
string t_rb_generator::function_signature(t_function* tfunction,
string prefix) {
// TODO(mcslee): Nitpicky, no ',' if argument_list is empty
return
prefix + tfunction->get_name() +
"(" + argument_list(tfunction->get_arglist()) + ")";
}
/**
* Renders a field list
*/
string t_rb_generator::argument_list(t_struct* tstruct) {
string result = "";
const vector<t_field*>& fields = tstruct->get_members();
vector<t_field*>::const_iterator f_iter;
bool first = true;
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
if (first) {
first = false;
} else {
result += ", ";
}
result += (*f_iter)->get_name();
}
return result;
}
string t_rb_generator::type_name(t_type* ttype) {
string prefix = "";
string name = ttype->get_name();
if (ttype->is_struct() || ttype->is_xception() || ttype->is_enum()) {
name = capitalize(ttype->get_name());
}
return prefix + name;
}
string t_rb_generator::full_type_name(t_type* ttype) {
string prefix = "";
vector<std::string> modules = ruby_modules(ttype->get_program());
for (vector<std::string>::iterator m_iter = modules.begin();
m_iter != modules.end(); ++m_iter) {
prefix += *m_iter + "::";
}
return prefix + type_name(ttype);
}
/**
* Converts the parse type to a Ruby tyoe
*/
string t_rb_generator::type_to_enum(t_type* type) {
type = get_true_type(type);
if (type->is_base_type()) {
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
switch (tbase) {
case t_base_type::TYPE_VOID:
throw "NO T_VOID CONSTRUCT";
case t_base_type::TYPE_STRING:
return "::Thrift::Types::STRING";
case t_base_type::TYPE_BOOL:
return "::Thrift::Types::BOOL";
case t_base_type::TYPE_BYTE:
return "::Thrift::Types::BYTE";
case t_base_type::TYPE_I16:
return "::Thrift::Types::I16";
case t_base_type::TYPE_I32:
return "::Thrift::Types::I32";
case t_base_type::TYPE_I64:
return "::Thrift::Types::I64";
case t_base_type::TYPE_DOUBLE:
return "::Thrift::Types::DOUBLE";
}
} else if (type->is_enum()) {
return "::Thrift::Types::I32";
} else if (type->is_struct() || type->is_xception()) {
return "::Thrift::Types::STRUCT";
} else if (type->is_map()) {
return "::Thrift::Types::MAP";
} else if (type->is_set()) {
return "::Thrift::Types::SET";
} else if (type->is_list()) {
return "::Thrift::Types::LIST";
}
throw "INVALID TYPE IN type_to_enum: " + type->get_name();
}
void t_rb_generator::generate_rdoc(std::ofstream& out, t_doc* tdoc) {
if (tdoc->has_doc()) {
generate_docstring_comment(out,
"", "# ", tdoc->get_doc(), "");
}
}
void t_rb_generator::generate_rb_struct_required_validator(std::ofstream& out,
t_struct* tstruct) {
indent(out) << "def validate" << endl;
indent_up();
const vector<t_field*>& fields = tstruct->get_members();
vector<t_field*>::const_iterator f_iter;
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
t_field* field = (*f_iter);
if (field->get_req() == t_field::T_REQUIRED) {
indent(out) << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field " << field->get_name() << " is unset!')";
if (field->get_type()->is_bool()) {
out << " if @" << field->get_name() << ".nil?";
} else {
out << " unless @" << field->get_name();
}
out << endl;
}
}
// if field is an enum, check that its value is valid
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
t_field* field = (*f_iter);
if (field->get_type()->is_enum()){
indent(out) << "unless @" << field->get_name() << ".nil? || " << field->get_type()->get_name() << "::VALID_VALUES.include?(@" << field->get_name() << ")" << endl;
indent_up();
indent(out) << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field " << field->get_name() << "!')" << endl;
indent_down();
indent(out) << "end" << endl;
}
}
indent_down();
indent(out) << "end" << endl << endl;
}
THRIFT_REGISTER_GENERATOR(rb, "Ruby", "");