2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
19 * Contains some contributions under the Thrift Software License.
20 * Please see doc/old-thrift-license.txt in the Thrift distribution for
32 #include <sys/types.h>
35 #include "thrift/platform.h"
36 #include "thrift/version.h"
37 #include "thrift/generate/t_oop_generator.h"
41 using std::ostringstream
;
43 using std::stringstream
;
46 static const string endl
= "\n"; // avoid ostream << std::endl flushes
49 * A subclass of std::ofstream that includes indenting functionality.
51 class t_rb_ofstream
: public std::ofstream
{
56 t_rb_ofstream() : std::ofstream(), indent_(0) {}
57 explicit t_rb_ofstream(const char* filename
,
58 ios_base::openmode mode
= ios_base::out
,
60 : std::ofstream(filename
, mode
), indent_(indent
) {}
62 t_rb_ofstream
& indent() {
63 for (int i
= 0; i
< indent_
; ++i
) {
69 void indent_up() { indent_
++; }
70 void indent_down() { indent_
--; }
74 * Ruby code generator.
77 class t_rb_generator
: public t_oop_generator
{
79 t_rb_generator(t_program
* program
,
80 const std::map
<std::string
, std::string
>& parsed_options
,
81 const std::string
& option_string
)
82 : t_oop_generator(program
) {
84 std::map
<std::string
, std::string
>::const_iterator iter
;
86 require_rubygems_
= false;
88 for( iter
= parsed_options
.begin(); iter
!= parsed_options
.end(); ++iter
) {
89 if( iter
->first
.compare("rubygems") == 0) {
90 require_rubygems_
= true;
91 } else if( iter
->first
.compare("namespaced") == 0) {
94 throw "unknown option ruby:" + iter
->first
;
98 out_dir_base_
= "gen-rb";
102 * Init and close methods
105 void init_generator() override
;
106 void close_generator() override
;
109 * Program-level generation functions
112 void generate_typedef(t_typedef
* ttypedef
) override
;
113 void generate_enum(t_enum
* tenum
) override
;
114 void generate_const(t_const
* tconst
) override
;
115 void generate_struct(t_struct
* tstruct
) override
;
116 void generate_forward_declaration(t_struct
* tstruct
) override
;
117 void generate_union(t_struct
* tunion
);
118 void generate_xception(t_struct
* txception
) override
;
119 void generate_service(t_service
* tservice
) override
;
121 t_rb_ofstream
& render_const_value(t_rb_ofstream
& out
, t_type
* type
, t_const_value
* value
);
124 * Struct generation code
127 void generate_rb_struct_declaration(t_rb_ofstream
& out
, t_struct
* tstruct
, bool is_exception
);
128 void generate_rb_struct(t_rb_ofstream
& out
, t_struct
* tstruct
, bool is_exception
);
129 void generate_rb_struct_required_validator(t_rb_ofstream
& out
, t_struct
* tstruct
);
130 void generate_rb_union(t_rb_ofstream
& out
, t_struct
* tstruct
, bool is_exception
);
131 void generate_rb_union_validator(t_rb_ofstream
& out
, t_struct
* tstruct
);
132 void generate_rb_function_helpers(t_function
* tfunction
);
133 void generate_rb_simple_constructor(t_rb_ofstream
& out
, t_struct
* tstruct
);
134 void generate_rb_simple_exception_constructor(t_rb_ofstream
& out
, t_struct
* tstruct
);
135 void generate_field_constants(t_rb_ofstream
& out
, t_struct
* tstruct
);
136 void generate_field_constructors(t_rb_ofstream
& out
, t_struct
* tstruct
);
137 void generate_field_defns(t_rb_ofstream
& out
, t_struct
* tstruct
);
138 void generate_field_data(t_rb_ofstream
& out
,
140 const std::string
& field_name
,
141 t_const_value
* field_value
,
145 * Service-level generation functions
148 void generate_service_helpers(t_service
* tservice
);
149 void generate_service_interface(t_service
* tservice
);
150 void generate_service_client(t_service
* tservice
);
151 void generate_service_server(t_service
* tservice
);
152 void generate_process_function(t_service
* tservice
, t_function
* tfunction
);
155 * Serialization constructs
158 void generate_deserialize_field(t_rb_ofstream
& out
,
160 std::string prefix
= "",
161 bool inclass
= false);
163 void generate_deserialize_struct(t_rb_ofstream
& out
, t_struct
* tstruct
, std::string prefix
= "");
165 void generate_deserialize_container(t_rb_ofstream
& out
, t_type
* ttype
, std::string prefix
= "");
167 void generate_deserialize_set_element(t_rb_ofstream
& out
, t_set
* tset
, std::string prefix
= "");
169 void generate_deserialize_map_element(t_rb_ofstream
& out
, t_map
* tmap
, std::string prefix
= "");
171 void generate_deserialize_list_element(t_rb_ofstream
& out
,
173 std::string prefix
= "");
175 void generate_serialize_field(t_rb_ofstream
& out
, t_field
* tfield
, std::string prefix
= "");
177 void generate_serialize_struct(t_rb_ofstream
& out
, t_struct
* tstruct
, std::string prefix
= "");
179 void generate_serialize_container(t_rb_ofstream
& out
, t_type
* ttype
, std::string prefix
= "");
181 void generate_serialize_map_element(t_rb_ofstream
& out
,
186 void generate_serialize_set_element(t_rb_ofstream
& out
, t_set
* tmap
, std::string iter
);
188 void generate_serialize_list_element(t_rb_ofstream
& out
, t_list
* tlist
, std::string iter
);
190 void generate_rdoc(t_rb_ofstream
& out
, t_doc
* tdoc
);
193 * Helper rendering functions
196 std::string
rb_autogen_comment();
197 std::string
render_require_thrift();
198 std::string
render_includes();
199 std::string
declare_field(t_field
* tfield
);
200 std::string
type_name(const t_type
* ttype
);
201 std::string
full_type_name(const t_type
* ttype
);
202 std::string
function_signature(t_function
* tfunction
, std::string prefix
= "");
203 std::string
argument_list(t_struct
* tstruct
);
204 std::string
type_to_enum(t_type
* ttype
);
205 std::string
rb_namespace_to_path_prefix(std::string rb_namespace
);
207 std::vector
<std::string
> ruby_modules(const t_program
* p
) {
208 std::string ns
= p
->get_namespace("rb");
209 std::vector
<std::string
> modules
;
214 std::string::iterator pos
= ns
.begin();
216 std::string::iterator delim
= std::find(pos
, ns
.end(), '.');
217 modules
.push_back(capitalize(std::string(pos
, delim
)));
219 if (pos
== ns
.end()) {
228 void begin_namespace(t_rb_ofstream
&, std::vector
<std::string
>);
229 void end_namespace(t_rb_ofstream
&, std::vector
<std::string
>);
236 t_rb_ofstream f_types_
;
237 t_rb_ofstream f_consts_
;
238 t_rb_ofstream f_service_
;
240 std::string namespace_dir_
;
241 std::string require_prefix_
;
243 /** If true, add a "require 'rubygems'" line to the top of each gen-rb file. */
244 bool require_rubygems_
;
246 /** If true, generate files in idiomatic namespaced directories. */
251 * Prepares for file generation by opening up the necessary file output
254 * @param tprogram The program to generate
256 void t_rb_generator::init_generator() {
257 string subdir
= get_out_dir();
259 // Make output directory
260 MKDIR(subdir
.c_str());
263 require_prefix_
= rb_namespace_to_path_prefix(program_
->get_namespace("rb"));
265 string dir
= require_prefix_
;
266 string::size_type loc
;
268 while ((loc
= dir
.find("/")) != string::npos
) {
269 subdir
= subdir
+ dir
.substr(0, loc
) + "/";
270 MKDIR(subdir
.c_str());
271 dir
= dir
.substr(loc
+ 1);
275 namespace_dir_
= subdir
;
278 string f_types_name
= namespace_dir_
+ underscore(program_name_
) + "_types.rb";
279 f_types_
.open(f_types_name
.c_str());
281 string f_consts_name
= namespace_dir_
+ underscore(program_name_
) + "_constants.rb";
282 f_consts_
.open(f_consts_name
.c_str());
285 f_types_
<< rb_autogen_comment() << endl
<< render_require_thrift() << render_includes() << endl
;
286 begin_namespace(f_types_
, ruby_modules(program_
));
288 f_consts_
<< rb_autogen_comment() << endl
<< render_require_thrift() << "require '"
289 << require_prefix_
<< underscore(program_name_
) << "_types'" << endl
<< endl
;
290 begin_namespace(f_consts_
, ruby_modules(program_
));
294 * Renders the require of thrift itself, and possibly of the rubygems dependency.
296 string
t_rb_generator::render_require_thrift() {
297 if (require_rubygems_
) {
298 return "require 'rubygems'\nrequire 'thrift'\n";
300 return "require 'thrift'\n";
305 * Renders all the imports necessary for including another Thrift program
307 string
t_rb_generator::render_includes() {
308 const vector
<t_program
*>& includes
= program_
->get_includes();
310 for (auto include
: includes
) {
312 t_program
* included
= include
;
313 std::string included_require_prefix
314 = rb_namespace_to_path_prefix(included
->get_namespace("rb"));
315 std::string included_name
= included
->get_name();
316 result
+= "require '" + included_require_prefix
+ underscore(included_name
) + "_types'\n";
318 result
+= "require '" + underscore(include
->get_name()) + "_types'\n";
321 if (includes
.size() > 0) {
330 string
t_rb_generator::rb_autogen_comment() {
331 return std::string("#\n") + "# Autogenerated by Thrift Compiler (" + THRIFT_VERSION
+ ")\n"
332 + "#\n" + "# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "#\n";
336 * Closes the type files
338 void t_rb_generator::close_generator() {
340 end_namespace(f_types_
, ruby_modules(program_
));
341 end_namespace(f_consts_
, ruby_modules(program_
));
347 * Generates a typedef. This is not done in Ruby, types are all implicit.
349 * @param ttypedef The type definition
351 void t_rb_generator::generate_typedef(t_typedef
* ttypedef
) {
356 * Generates code for an enumerated type. Done using a class to scope
359 * @param tenum The enumeration
361 void t_rb_generator::generate_enum(t_enum
* tenum
) {
362 f_types_
.indent() << "module " << capitalize(tenum
->get_name()) << endl
;
363 f_types_
.indent_up();
365 vector
<t_enum_value
*> constants
= tenum
->get_constants();
366 vector
<t_enum_value
*>::iterator c_iter
;
367 for (c_iter
= constants
.begin(); c_iter
!= constants
.end(); ++c_iter
) {
368 int value
= (*c_iter
)->get_value();
370 // Ruby class constants have to be capitalized... omg i am so on the fence
371 // about languages strictly enforcing capitalization why can't we just all
372 // agree and play nice.
373 string name
= capitalize((*c_iter
)->get_name());
375 generate_rdoc(f_types_
, *c_iter
);
376 f_types_
.indent() << name
<< " = " << value
<< endl
;
379 // Create a hash mapping values back to their names (as strings) since ruby has no native enum
381 f_types_
.indent() << "VALUE_MAP = {";
382 for (c_iter
= constants
.begin(); c_iter
!= constants
.end(); ++c_iter
) {
384 int value
= (*c_iter
)->get_value();
385 if (c_iter
!= constants
.begin())
387 f_types_
<< value
<< " => \"" << capitalize((*c_iter
)->get_name()) << "\"";
389 f_types_
<< "}" << endl
;
391 // Create a set with valid values for this enum
392 f_types_
.indent() << "VALID_VALUES = Set.new([";
393 for (c_iter
= constants
.begin(); c_iter
!= constants
.end(); ++c_iter
) {
395 if (c_iter
!= constants
.begin())
397 f_types_
<< capitalize((*c_iter
)->get_name());
399 f_types_
<< "]).freeze" << endl
;
401 f_types_
.indent_down();
402 f_types_
.indent() << "end" << endl
<< endl
;
406 * Generate a constant value
408 void t_rb_generator::generate_const(t_const
* tconst
) {
409 t_type
* type
= tconst
->get_type();
410 string name
= tconst
->get_name();
411 t_const_value
* value
= tconst
->get_value();
413 name
[0] = toupper(name
[0]);
415 f_consts_
.indent() << name
<< " = ";
416 render_const_value(f_consts_
, type
, value
) << endl
<< endl
;
420 * Prints the value of a constant with the given type. Note that type checking
421 * is NOT performed in this function as it is always run beforehand using the
422 * validate_types method in main.cc
424 t_rb_ofstream
& t_rb_generator::render_const_value(t_rb_ofstream
& out
,
426 t_const_value
* value
) {
427 type
= get_true_type(type
);
428 if (type
->is_base_type()) {
429 t_base_type::t_base tbase
= ((t_base_type
*)type
)->get_base();
431 case t_base_type::TYPE_STRING
:
432 out
<< "%q\"" << get_escaped_string(value
) << '"';
434 case t_base_type::TYPE_BOOL
:
435 out
<< (value
->get_integer() > 0 ? "true" : "false");
437 case t_base_type::TYPE_I8
:
438 case t_base_type::TYPE_I16
:
439 case t_base_type::TYPE_I32
:
440 case t_base_type::TYPE_I64
:
441 out
<< value
->get_integer();
443 case t_base_type::TYPE_DOUBLE
:
444 if (value
->get_type() == t_const_value::CV_INTEGER
) {
445 out
<< value
->get_integer();
447 out
<< value
->get_double();
451 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase
);
453 } else if (type
->is_enum()) {
454 out
.indent() << value
->get_integer();
455 } else if (type
->is_struct() || type
->is_xception()) {
456 out
<< full_type_name(type
) << ".new({" << endl
;
458 const vector
<t_field
*>& fields
= ((t_struct
*)type
)->get_members();
459 vector
<t_field
*>::const_iterator f_iter
;
460 const map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
>& val
= value
->get_map();
461 map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
>::const_iterator v_iter
;
462 for (v_iter
= val
.begin(); v_iter
!= val
.end(); ++v_iter
) {
463 t_type
* field_type
= NULL
;
464 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
465 if ((*f_iter
)->get_name() == v_iter
->first
->get_string()) {
466 field_type
= (*f_iter
)->get_type();
469 if (field_type
== NULL
) {
470 throw "type error: " + type
->get_name() + " has no field " + v_iter
->first
->get_string();
473 render_const_value(out
, g_type_string
, v_iter
->first
) << " => ";
474 render_const_value(out
, field_type
, v_iter
->second
) << "," << endl
;
477 out
.indent() << "})";
478 } else if (type
->is_map()) {
479 t_type
* ktype
= ((t_map
*)type
)->get_key_type();
480 t_type
* vtype
= ((t_map
*)type
)->get_val_type();
483 const map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
>& val
= value
->get_map();
484 map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
>::const_iterator v_iter
;
485 for (v_iter
= val
.begin(); v_iter
!= val
.end(); ++v_iter
) {
487 render_const_value(out
, ktype
, v_iter
->first
) << " => ";
488 render_const_value(out
, vtype
, v_iter
->second
) << "," << endl
;
492 } else if (type
->is_list() || type
->is_set()) {
494 if (type
->is_list()) {
495 etype
= ((t_list
*)type
)->get_elem_type();
497 etype
= ((t_set
*)type
)->get_elem_type();
499 if (type
->is_set()) {
500 out
<< "Set.new([" << endl
;
505 const vector
<t_const_value
*>& val
= value
->get_list();
506 vector
<t_const_value
*>::const_iterator v_iter
;
507 for (v_iter
= val
.begin(); v_iter
!= val
.end(); ++v_iter
) {
509 render_const_value(out
, etype
, *v_iter
) << "," << endl
;
512 if (type
->is_set()) {
513 out
.indent() << "])";
518 throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type
->get_name();
524 * Generates a ruby struct
526 void t_rb_generator::generate_struct(t_struct
* tstruct
) {
527 if (tstruct
->is_union()) {
528 generate_rb_union(f_types_
, tstruct
, false);
530 generate_rb_struct(f_types_
, tstruct
, false);
536 * Generates the "forward declarations" for ruby structs.
537 * These are simply a declaration of each class with proper inheritance.
538 * The rest of the struct is still generated in generate_struct as has
539 * always been the case. These declarations allow thrift to generate valid
540 * ruby in cases where thrift structs rely on recursive definitions.
542 void t_rb_generator::generate_forward_declaration(t_struct
* tstruct
) {
543 generate_rb_struct_declaration(f_types_
, tstruct
, tstruct
->is_xception());
546 void t_rb_generator::generate_rb_struct_declaration(t_rb_ofstream
& out
, t_struct
* tstruct
, bool is_exception
) {
547 out
.indent() << "class " << type_name(tstruct
);
548 if (tstruct
->is_union()) {
549 out
<< " < ::Thrift::Union";
552 out
<< " < ::Thrift::Exception";
554 out
<< "; end" << endl
<< endl
;
558 * Generates a struct definition for a thrift exception. Basically the same
559 * as a struct but extends the Exception class.
561 * @param txception The struct definition
563 void t_rb_generator::generate_xception(t_struct
* txception
) {
564 generate_rb_struct(f_types_
, txception
, true);
568 * Generates a ruby struct
570 void t_rb_generator::generate_rb_struct(t_rb_ofstream
& out
,
572 bool is_exception
= false) {
573 generate_rdoc(out
, tstruct
);
574 out
.indent() << "class " << type_name(tstruct
);
576 out
<< " < ::Thrift::Exception";
581 out
.indent() << "include ::Thrift::Struct, ::Thrift::Struct_Union" << endl
;
584 generate_rb_simple_exception_constructor(out
, tstruct
);
587 generate_field_constants(out
, tstruct
);
588 generate_field_defns(out
, tstruct
);
589 generate_rb_struct_required_validator(out
, tstruct
);
591 out
.indent() << "::Thrift::Struct.generate_accessors self" << endl
;
594 out
.indent() << "end" << endl
<< endl
;
598 * Generates a ruby union
600 void t_rb_generator::generate_rb_union(t_rb_ofstream
& out
,
602 bool is_exception
= false) {
604 generate_rdoc(out
, tstruct
);
605 out
.indent() << "class " << type_name(tstruct
) << " < ::Thrift::Union" << endl
;
608 out
.indent() << "include ::Thrift::Struct_Union" << endl
;
610 generate_field_constructors(out
, tstruct
);
612 generate_field_constants(out
, tstruct
);
613 generate_field_defns(out
, tstruct
);
614 generate_rb_union_validator(out
, tstruct
);
616 out
.indent() << "::Thrift::Union.generate_accessors self" << endl
;
619 out
.indent() << "end" << endl
<< endl
;
622 void t_rb_generator::generate_field_constructors(t_rb_ofstream
& out
, t_struct
* tstruct
) {
624 out
.indent() << "class << self" << endl
;
627 const vector
<t_field
*>& fields
= tstruct
->get_members();
628 vector
<t_field
*>::const_iterator f_iter
;
630 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
631 if (f_iter
!= fields
.begin()) {
634 std::string field_name
= (*f_iter
)->get_name();
636 out
.indent() << "def " << field_name
<< "(val)" << endl
;
637 out
.indent() << " " << tstruct
->get_name() << ".new(:" << field_name
<< ", val)" << endl
;
638 out
.indent() << "end" << endl
;
642 out
.indent() << "end" << endl
;
647 void t_rb_generator::generate_rb_simple_exception_constructor(t_rb_ofstream
& out
,
649 const vector
<t_field
*>& members
= tstruct
->get_members();
651 if (members
.size() == 1) {
652 vector
<t_field
*>::const_iterator m_iter
= members
.begin();
654 if ((*m_iter
)->get_type()->is_string()) {
655 string name
= (*m_iter
)->get_name();
657 out
.indent() << "def initialize(message=nil)" << endl
;
659 out
.indent() << "super()" << endl
;
660 out
.indent() << "self." << name
<< " = message" << endl
;
662 out
.indent() << "end" << endl
<< endl
;
664 if (name
!= "message") {
665 out
.indent() << "def message; " << name
<< " end" << endl
<< endl
;
671 void t_rb_generator::generate_field_constants(t_rb_ofstream
& out
, t_struct
* tstruct
) {
672 const vector
<t_field
*>& fields
= tstruct
->get_members();
673 vector
<t_field
*>::const_iterator f_iter
;
675 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
676 std::string field_name
= (*f_iter
)->get_name();
677 std::string cap_field_name
= upcase_string(field_name
);
679 out
.indent() << cap_field_name
<< " = " << (*f_iter
)->get_key() << endl
;
684 void t_rb_generator::generate_field_defns(t_rb_ofstream
& out
, t_struct
* tstruct
) {
685 const vector
<t_field
*>& fields
= tstruct
->get_members();
686 vector
<t_field
*>::const_iterator f_iter
;
688 out
.indent() << "FIELDS = {" << endl
;
690 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
691 if (f_iter
!= fields
.begin()) {
695 // generate the field docstrings within the FIELDS constant. no real better place...
696 generate_rdoc(out
, *f_iter
);
698 out
.indent() << upcase_string((*f_iter
)->get_name()) << " => ";
700 generate_field_data(out
,
701 (*f_iter
)->get_type(),
702 (*f_iter
)->get_name(),
703 (*f_iter
)->get_value(),
704 (*f_iter
)->get_req() == t_field::T_OPTIONAL
);
708 out
.indent() << "}" << endl
<< endl
;
710 out
.indent() << "def struct_fields; FIELDS; end" << endl
<< endl
;
713 void t_rb_generator::generate_field_data(t_rb_ofstream
& out
,
715 const std::string
& field_name
= "",
716 t_const_value
* field_value
= NULL
,
717 bool optional
= false) {
718 field_type
= get_true_type(field_type
);
720 // Begin this field's defn
721 out
<< "{:type => " << type_to_enum(field_type
);
723 if (!field_name
.empty()) {
724 out
<< ", :name => '" << field_name
<< "'";
727 if (field_value
!= NULL
) {
728 out
<< ", :default => ";
729 render_const_value(out
, field_type
, field_value
);
732 if (!field_type
->is_base_type()) {
733 if (field_type
->is_struct() || field_type
->is_xception()) {
734 out
<< ", :class => " << full_type_name((t_struct
*)field_type
);
735 } else if (field_type
->is_list()) {
736 out
<< ", :element => ";
737 generate_field_data(out
, ((t_list
*)field_type
)->get_elem_type());
738 } else if (field_type
->is_map()) {
740 generate_field_data(out
, ((t_map
*)field_type
)->get_key_type());
741 out
<< ", :value => ";
742 generate_field_data(out
, ((t_map
*)field_type
)->get_val_type());
743 } else if (field_type
->is_set()) {
744 out
<< ", :element => ";
745 generate_field_data(out
, ((t_set
*)field_type
)->get_elem_type());
748 if (((t_base_type
*)field_type
)->is_binary()) {
749 out
<< ", :binary => true";
754 out
<< ", :optional => true";
757 if (field_type
->is_enum()) {
758 out
<< ", :enum_class => " << full_type_name(field_type
);
761 // End of this field's defn
765 void t_rb_generator::begin_namespace(t_rb_ofstream
& out
, vector
<std::string
> modules
) {
766 for (auto & module
: modules
) {
767 out
.indent() << "module " << module
<< endl
;
772 void t_rb_generator::end_namespace(t_rb_ofstream
& out
, vector
<std::string
> modules
) {
773 for (vector
<std::string
>::reverse_iterator m_iter
= modules
.rbegin(); m_iter
!= modules
.rend();
776 out
.indent() << "end" << endl
;
781 * Generates a thrift service.
783 * @param tservice The service definition
785 void t_rb_generator::generate_service(t_service
* tservice
) {
786 string f_service_name
= namespace_dir_
+ underscore(service_name_
) + ".rb";
787 f_service_
.open(f_service_name
.c_str());
789 f_service_
<< rb_autogen_comment() << endl
<< render_require_thrift();
791 if (tservice
->get_extends() != NULL
) {
793 f_service_
<< "require '" << rb_namespace_to_path_prefix(
794 tservice
->get_extends()->get_program()->get_namespace("rb"))
795 << underscore(tservice
->get_extends()->get_name()) << "'" << endl
;
797 f_service_
<< "require '" << require_prefix_
798 << underscore(tservice
->get_extends()->get_name()) << "'" << endl
;
802 f_service_
<< "require '" << require_prefix_
<< underscore(program_name_
) << "_types'" << endl
805 begin_namespace(f_service_
, ruby_modules(tservice
->get_program()));
807 f_service_
.indent() << "module " << capitalize(tservice
->get_name()) << endl
;
808 f_service_
.indent_up();
810 // Generate the three main parts of the service (well, two for now in PHP)
811 generate_service_client(tservice
);
812 generate_service_server(tservice
);
813 generate_service_helpers(tservice
);
815 f_service_
.indent_down();
816 f_service_
.indent() << "end" << endl
<< endl
;
818 end_namespace(f_service_
, ruby_modules(tservice
->get_program()));
820 // Close service file
825 * Generates helper functions for a service.
827 * @param tservice The service to generate a header definition for
829 void t_rb_generator::generate_service_helpers(t_service
* tservice
) {
830 vector
<t_function
*> functions
= tservice
->get_functions();
831 vector
<t_function
*>::iterator f_iter
;
833 f_service_
.indent() << "# HELPER FUNCTIONS AND STRUCTURES" << endl
<< endl
;
835 for (f_iter
= functions
.begin(); f_iter
!= functions
.end(); ++f_iter
) {
836 t_struct
* ts
= (*f_iter
)->get_arglist();
837 generate_rb_struct(f_service_
, ts
);
838 generate_rb_function_helpers(*f_iter
);
843 * Generates a struct and helpers for a function.
845 * @param tfunction The function
847 void t_rb_generator::generate_rb_function_helpers(t_function
* tfunction
) {
848 t_struct
result(program_
, tfunction
->get_name() + "_result");
849 t_field
success(tfunction
->get_returntype(), "success", 0);
850 if (!tfunction
->get_returntype()->is_void()) {
851 result
.append(&success
);
854 t_struct
* xs
= tfunction
->get_xceptions();
855 const vector
<t_field
*>& fields
= xs
->get_members();
856 vector
<t_field
*>::const_iterator f_iter
;
857 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
858 result
.append(*f_iter
);
860 generate_rb_struct(f_service_
, &result
);
864 * Generates a service client definition.
866 * @param tservice The service to generate a server for.
868 void t_rb_generator::generate_service_client(t_service
* tservice
) {
870 string extends_client
= "";
871 if (tservice
->get_extends() != NULL
) {
872 extends
= full_type_name(tservice
->get_extends());
873 extends_client
= " < " + extends
+ "::Client ";
876 f_service_
.indent() << "class Client" << extends_client
<< endl
;
877 f_service_
.indent_up();
879 f_service_
.indent() << "include ::Thrift::Client" << endl
<< endl
;
881 // Generate client method implementations
882 vector
<t_function
*> functions
= tservice
->get_functions();
883 vector
<t_function
*>::const_iterator f_iter
;
884 for (f_iter
= functions
.begin(); f_iter
!= functions
.end(); ++f_iter
) {
885 t_struct
* arg_struct
= (*f_iter
)->get_arglist();
886 const vector
<t_field
*>& fields
= arg_struct
->get_members();
887 vector
<t_field
*>::const_iterator fld_iter
;
888 string funname
= (*f_iter
)->get_name();
891 f_service_
.indent() << "def " << function_signature(*f_iter
) << endl
;
892 f_service_
.indent_up();
893 f_service_
.indent() << "send_" << funname
<< "(";
896 for (fld_iter
= fields
.begin(); fld_iter
!= fields
.end(); ++fld_iter
) {
902 f_service_
<< (*fld_iter
)->get_name();
904 f_service_
<< ")" << endl
;
906 if (!(*f_iter
)->is_oneway()) {
908 if (!(*f_iter
)->get_returntype()->is_void()) {
909 f_service_
<< "return ";
911 f_service_
<< "recv_" << funname
<< "()" << endl
;
913 f_service_
.indent_down();
914 f_service_
.indent() << "end" << endl
;
917 f_service_
.indent() << "def send_" << function_signature(*f_iter
) << endl
;
918 f_service_
.indent_up();
920 std::string argsname
= capitalize((*f_iter
)->get_name() + "_args");
921 std::string messageSendProc
= (*f_iter
)->is_oneway() ? "send_oneway_message" : "send_message";
923 f_service_
.indent() << messageSendProc
<< "('" << funname
<< "', " << argsname
;
925 for (fld_iter
= fields
.begin(); fld_iter
!= fields
.end(); ++fld_iter
) {
926 f_service_
<< ", :" << (*fld_iter
)->get_name() << " => " << (*fld_iter
)->get_name();
929 f_service_
<< ")" << endl
;
931 f_service_
.indent_down();
932 f_service_
.indent() << "end" << endl
;
934 if (!(*f_iter
)->is_oneway()) {
935 std::string resultname
= capitalize((*f_iter
)->get_name() + "_result");
936 t_struct
noargs(program_
);
938 t_function
recv_function((*f_iter
)->get_returntype(),
939 string("recv_") + (*f_iter
)->get_name(),
943 f_service_
.indent() << "def " << function_signature(&recv_function
) << endl
;
944 f_service_
.indent_up();
946 // TODO(mcslee): Validate message reply here, seq ids etc.
948 f_service_
.indent() << "result = receive_message(" << resultname
<< ")" << endl
;
950 // Careful, only return _result if not a void function
951 if (!(*f_iter
)->get_returntype()->is_void()) {
952 f_service_
.indent() << "return result.success unless result.success.nil?" << endl
;
955 t_struct
* xs
= (*f_iter
)->get_xceptions();
956 const std::vector
<t_field
*>& xceptions
= xs
->get_members();
957 vector
<t_field
*>::const_iterator x_iter
;
958 for (x_iter
= xceptions
.begin(); x_iter
!= xceptions
.end(); ++x_iter
) {
959 f_service_
.indent() << "raise result." << (*x_iter
)->get_name() << " unless result."
960 << (*x_iter
)->get_name() << ".nil?" << endl
;
963 // Careful, only return _result if not a void function
964 if ((*f_iter
)->get_returntype()->is_void()) {
965 f_service_
.indent() << "return" << endl
;
967 f_service_
.indent() << "raise "
968 "::Thrift::ApplicationException.new(::Thrift::ApplicationException::"
969 "MISSING_RESULT, '" << (*f_iter
)->get_name()
970 << " failed: unknown result')" << endl
;
974 f_service_
.indent_down();
975 f_service_
.indent() << "end" << endl
<< endl
;
979 f_service_
.indent_down();
980 f_service_
.indent() << "end" << endl
<< endl
;
984 * Generates a service server definition.
986 * @param tservice The service to generate a server for.
988 void t_rb_generator::generate_service_server(t_service
* tservice
) {
989 // Generate the dispatch methods
990 vector
<t_function
*> functions
= tservice
->get_functions();
991 vector
<t_function
*>::iterator f_iter
;
994 string extends_processor
= "";
995 if (tservice
->get_extends() != NULL
) {
996 extends
= full_type_name(tservice
->get_extends());
997 extends_processor
= " < " + extends
+ "::Processor ";
1000 // Generate the header portion
1001 f_service_
.indent() << "class Processor" << extends_processor
<< endl
;
1002 f_service_
.indent_up();
1004 f_service_
.indent() << "include ::Thrift::Processor" << endl
<< endl
;
1006 // Generate the process subfunctions
1007 for (f_iter
= functions
.begin(); f_iter
!= functions
.end(); ++f_iter
) {
1008 generate_process_function(tservice
, *f_iter
);
1011 f_service_
.indent_down();
1012 f_service_
.indent() << "end" << endl
<< endl
;
1016 * Generates a process function definition.
1018 * @param tfunction The function to write a dispatcher for
1020 void t_rb_generator::generate_process_function(t_service
* tservice
, t_function
* tfunction
) {
1023 f_service_
.indent() << "def process_" << tfunction
->get_name() << "(seqid, iprot, oprot)" << endl
;
1024 f_service_
.indent_up();
1026 string argsname
= capitalize(tfunction
->get_name()) + "_args";
1027 string resultname
= capitalize(tfunction
->get_name()) + "_result";
1029 f_service_
.indent() << "args = read_args(iprot, " << argsname
<< ")" << endl
;
1031 t_struct
* xs
= tfunction
->get_xceptions();
1032 const std::vector
<t_field
*>& xceptions
= xs
->get_members();
1033 vector
<t_field
*>::const_iterator x_iter
;
1035 // Declare result for non oneway function
1036 if (!tfunction
->is_oneway()) {
1037 f_service_
.indent() << "result = " << resultname
<< ".new()" << endl
;
1040 // Try block for a function with exceptions
1041 if (xceptions
.size() > 0) {
1042 f_service_
.indent() << "begin" << endl
;
1043 f_service_
.indent_up();
1046 // Generate the function call
1047 t_struct
* arg_struct
= tfunction
->get_arglist();
1048 const std::vector
<t_field
*>& fields
= arg_struct
->get_members();
1049 vector
<t_field
*>::const_iterator f_iter
;
1051 f_service_
.indent();
1052 if (!tfunction
->is_oneway() && !tfunction
->get_returntype()->is_void()) {
1053 f_service_
<< "result.success = ";
1055 f_service_
<< "@handler." << tfunction
->get_name() << "(";
1057 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1063 f_service_
<< "args." << (*f_iter
)->get_name();
1065 f_service_
<< ")" << endl
;
1067 if (!tfunction
->is_oneway() && xceptions
.size() > 0) {
1068 f_service_
.indent_down();
1069 for (x_iter
= xceptions
.begin(); x_iter
!= xceptions
.end(); ++x_iter
) {
1070 f_service_
.indent() << "rescue " << full_type_name((*x_iter
)->get_type()) << " => "
1071 << (*x_iter
)->get_name() << endl
;
1072 if (!tfunction
->is_oneway()) {
1073 f_service_
.indent_up();
1074 f_service_
.indent() << "result." << (*x_iter
)->get_name() << " = " << (*x_iter
)->get_name()
1076 f_service_
.indent_down();
1079 f_service_
.indent() << "end" << endl
;
1082 // Shortcut out here for oneway functions
1083 if (tfunction
->is_oneway()) {
1084 f_service_
.indent() << "return" << endl
;
1085 f_service_
.indent_down();
1086 f_service_
.indent() << "end" << endl
<< endl
;
1090 f_service_
.indent() << "write_result(result, oprot, '" << tfunction
->get_name() << "', seqid)"
1094 f_service_
.indent_down();
1095 f_service_
.indent() << "end" << endl
<< endl
;
1099 * Renders a function signature of the form 'type name(args)'
1101 * @param tfunction Function definition
1102 * @return String of rendered function definition
1104 string
t_rb_generator::function_signature(t_function
* tfunction
, string prefix
) {
1105 // TODO(mcslee): Nitpicky, no ',' if argument_list is empty
1106 return prefix
+ tfunction
->get_name() + "(" + argument_list(tfunction
->get_arglist()) + ")";
1110 * Renders a field list
1112 string
t_rb_generator::argument_list(t_struct
* tstruct
) {
1115 const vector
<t_field
*>& fields
= tstruct
->get_members();
1116 vector
<t_field
*>::const_iterator f_iter
;
1118 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1124 result
+= (*f_iter
)->get_name();
1129 string
t_rb_generator::type_name(const t_type
* ttype
) {
1132 string name
= ttype
->get_name();
1133 if (ttype
->is_struct() || ttype
->is_xception() || ttype
->is_enum()) {
1134 name
= capitalize(ttype
->get_name());
1137 return prefix
+ name
;
1140 string
t_rb_generator::full_type_name(const t_type
* ttype
) {
1141 string prefix
= "::";
1142 vector
<std::string
> modules
= ruby_modules(ttype
->get_program());
1143 for (auto & module
: modules
) {
1144 prefix
+= module
+ "::";
1146 return prefix
+ type_name(ttype
);
1150 * Converts the parse type to a Ruby tyoe
1152 string
t_rb_generator::type_to_enum(t_type
* type
) {
1153 type
= get_true_type(type
);
1155 if (type
->is_base_type()) {
1156 t_base_type::t_base tbase
= ((t_base_type
*)type
)->get_base();
1158 case t_base_type::TYPE_VOID
:
1159 throw "NO T_VOID CONSTRUCT";
1160 case t_base_type::TYPE_STRING
:
1161 return "::Thrift::Types::STRING";
1162 case t_base_type::TYPE_BOOL
:
1163 return "::Thrift::Types::BOOL";
1164 case t_base_type::TYPE_I8
:
1165 return "::Thrift::Types::BYTE";
1166 case t_base_type::TYPE_I16
:
1167 return "::Thrift::Types::I16";
1168 case t_base_type::TYPE_I32
:
1169 return "::Thrift::Types::I32";
1170 case t_base_type::TYPE_I64
:
1171 return "::Thrift::Types::I64";
1172 case t_base_type::TYPE_DOUBLE
:
1173 return "::Thrift::Types::DOUBLE";
1175 } else if (type
->is_enum()) {
1176 return "::Thrift::Types::I32";
1177 } else if (type
->is_struct() || type
->is_xception()) {
1178 return "::Thrift::Types::STRUCT";
1179 } else if (type
->is_map()) {
1180 return "::Thrift::Types::MAP";
1181 } else if (type
->is_set()) {
1182 return "::Thrift::Types::SET";
1183 } else if (type
->is_list()) {
1184 return "::Thrift::Types::LIST";
1187 throw "INVALID TYPE IN type_to_enum: " + type
->get_name();
1190 string
t_rb_generator::rb_namespace_to_path_prefix(string rb_namespace
) {
1191 string namespaces_left
= rb_namespace
;
1192 string::size_type loc
;
1194 string path_prefix
= "";
1196 while ((loc
= namespaces_left
.find(".")) != string::npos
) {
1197 path_prefix
= path_prefix
+ underscore(namespaces_left
.substr(0, loc
)) + "/";
1198 namespaces_left
= namespaces_left
.substr(loc
+ 1);
1200 if (namespaces_left
.size() > 0) {
1201 path_prefix
= path_prefix
+ underscore(namespaces_left
) + "/";
1206 void t_rb_generator::generate_rdoc(t_rb_ofstream
& out
, t_doc
* tdoc
) {
1207 if (tdoc
->has_doc()) {
1209 generate_docstring_comment(out
, "", "# ", tdoc
->get_doc(), "");
1213 void t_rb_generator::generate_rb_struct_required_validator(t_rb_ofstream
& out
, t_struct
* tstruct
) {
1214 out
.indent() << "def validate" << endl
;
1217 const vector
<t_field
*>& fields
= tstruct
->get_members();
1218 vector
<t_field
*>::const_iterator f_iter
;
1220 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1221 t_field
* field
= (*f_iter
);
1222 if (field
->get_req() == t_field::T_REQUIRED
) {
1223 out
.indent() << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, "
1224 "'Required field " << field
->get_name() << " is unset!')";
1225 if (field
->get_type()->is_bool()) {
1226 out
<< " if @" << field
->get_name() << ".nil?";
1228 out
<< " unless @" << field
->get_name();
1234 // if field is an enum, check that its value is valid
1235 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1236 t_field
* field
= (*f_iter
);
1238 if (field
->get_type()->is_enum()) {
1239 out
.indent() << "unless @" << field
->get_name() << ".nil? || "
1240 << full_type_name(field
->get_type()) << "::VALID_VALUES.include?(@"
1241 << field
->get_name() << ")" << endl
;
1243 out
.indent() << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, "
1244 "'Invalid value of field " << field
->get_name() << "!')" << endl
;
1246 out
.indent() << "end" << endl
;
1251 out
.indent() << "end" << endl
<< endl
;
1254 void t_rb_generator::generate_rb_union_validator(t_rb_ofstream
& out
, t_struct
* tstruct
) {
1255 out
.indent() << "def validate" << endl
;
1258 const vector
<t_field
*>& fields
= tstruct
->get_members();
1259 vector
<t_field
*>::const_iterator f_iter
;
1262 << "raise(StandardError, 'Union fields are not set.') if get_set_field.nil? || get_value.nil?"
1265 // if field is an enum, check that its value is valid
1266 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1267 const t_field
* field
= (*f_iter
);
1269 if (field
->get_type()->is_enum()) {
1270 out
.indent() << "if get_set_field == :" << field
->get_name() << endl
;
1271 out
.indent() << " raise "
1272 "::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, "
1273 "'Invalid value of field " << field
->get_name() << "!') unless "
1274 << full_type_name(field
->get_type()) << "::VALID_VALUES.include?(get_value)"
1276 out
.indent() << "end" << endl
;
1281 out
.indent() << "end" << endl
<< endl
;
1284 THRIFT_REGISTER_GENERATOR(
1287 " rubygems: Add a \"require 'rubygems'\" line to the top of each generated file.\n"
1288 " namespaced: Generate files in idiomatic namespaced directories.\n")