]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_rb_generator.cc
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / thrift / compiler / cpp / src / thrift / generate / t_rb_generator.cc
1 /*
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
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
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
17 * under the License.
18 *
19 * Contains some contributions under the Thrift Software License.
20 * Please see doc/old-thrift-license.txt in the Thrift distribution for
21 * details.
22 */
23
24 #include <string>
25 #include <fstream>
26 #include <iostream>
27 #include <vector>
28 #include <algorithm>
29
30 #include <stdlib.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <sstream>
34
35 #include "thrift/platform.h"
36 #include "thrift/version.h"
37 #include "thrift/generate/t_oop_generator.h"
38
39 using std::map;
40 using std::ofstream;
41 using std::ostringstream;
42 using std::string;
43 using std::stringstream;
44 using std::vector;
45
46 static const string endl = "\n"; // avoid ostream << std::endl flushes
47
48 /**
49 * A subclass of std::ofstream that includes indenting functionality.
50 */
51 class t_rb_ofstream : public std::ofstream {
52 private:
53 int indent_;
54
55 public:
56 t_rb_ofstream() : std::ofstream(), indent_(0) {}
57 explicit t_rb_ofstream(const char* filename,
58 ios_base::openmode mode = ios_base::out,
59 int indent = 0)
60 : std::ofstream(filename, mode), indent_(indent) {}
61
62 t_rb_ofstream& indent() {
63 for (int i = 0; i < indent_; ++i) {
64 *this << " ";
65 }
66 return *this;
67 }
68
69 void indent_up() { indent_++; }
70 void indent_down() { indent_--; }
71 };
72
73 /**
74 * Ruby code generator.
75 *
76 */
77 class t_rb_generator : public t_oop_generator {
78 public:
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) {
83 (void)option_string;
84 std::map<std::string, std::string>::const_iterator iter;
85
86 require_rubygems_ = false;
87 namespaced_ = 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) {
92 namespaced_ = true;
93 } else {
94 throw "unknown option ruby:" + iter->first;
95 }
96 }
97
98 out_dir_base_ = "gen-rb";
99 }
100
101 /**
102 * Init and close methods
103 */
104
105 void init_generator() override;
106 void close_generator() override;
107
108 /**
109 * Program-level generation functions
110 */
111
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;
120
121 t_rb_ofstream& render_const_value(t_rb_ofstream& out, t_type* type, t_const_value* value);
122
123 /**
124 * Struct generation code
125 */
126
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,
139 t_type* field_type,
140 const std::string& field_name,
141 t_const_value* field_value,
142 bool optional);
143
144 /**
145 * Service-level generation functions
146 */
147
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);
153
154 /**
155 * Serialization constructs
156 */
157
158 void generate_deserialize_field(t_rb_ofstream& out,
159 t_field* tfield,
160 std::string prefix = "",
161 bool inclass = false);
162
163 void generate_deserialize_struct(t_rb_ofstream& out, t_struct* tstruct, std::string prefix = "");
164
165 void generate_deserialize_container(t_rb_ofstream& out, t_type* ttype, std::string prefix = "");
166
167 void generate_deserialize_set_element(t_rb_ofstream& out, t_set* tset, std::string prefix = "");
168
169 void generate_deserialize_map_element(t_rb_ofstream& out, t_map* tmap, std::string prefix = "");
170
171 void generate_deserialize_list_element(t_rb_ofstream& out,
172 t_list* tlist,
173 std::string prefix = "");
174
175 void generate_serialize_field(t_rb_ofstream& out, t_field* tfield, std::string prefix = "");
176
177 void generate_serialize_struct(t_rb_ofstream& out, t_struct* tstruct, std::string prefix = "");
178
179 void generate_serialize_container(t_rb_ofstream& out, t_type* ttype, std::string prefix = "");
180
181 void generate_serialize_map_element(t_rb_ofstream& out,
182 t_map* tmap,
183 std::string kiter,
184 std::string viter);
185
186 void generate_serialize_set_element(t_rb_ofstream& out, t_set* tmap, std::string iter);
187
188 void generate_serialize_list_element(t_rb_ofstream& out, t_list* tlist, std::string iter);
189
190 void generate_rdoc(t_rb_ofstream& out, t_doc* tdoc);
191
192 /**
193 * Helper rendering functions
194 */
195
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);
206
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;
210 if (ns.empty()) {
211 return modules;
212 }
213
214 std::string::iterator pos = ns.begin();
215 while (true) {
216 std::string::iterator delim = std::find(pos, ns.end(), '.');
217 modules.push_back(capitalize(std::string(pos, delim)));
218 pos = delim;
219 if (pos == ns.end()) {
220 break;
221 }
222 ++pos;
223 }
224
225 return modules;
226 }
227
228 void begin_namespace(t_rb_ofstream&, std::vector<std::string>);
229 void end_namespace(t_rb_ofstream&, std::vector<std::string>);
230
231 private:
232 /**
233 * File streams
234 */
235
236 t_rb_ofstream f_types_;
237 t_rb_ofstream f_consts_;
238 t_rb_ofstream f_service_;
239
240 std::string namespace_dir_;
241 std::string require_prefix_;
242
243 /** If true, add a "require 'rubygems'" line to the top of each gen-rb file. */
244 bool require_rubygems_;
245
246 /** If true, generate files in idiomatic namespaced directories. */
247 bool namespaced_;
248 };
249
250 /**
251 * Prepares for file generation by opening up the necessary file output
252 * streams.
253 *
254 * @param tprogram The program to generate
255 */
256 void t_rb_generator::init_generator() {
257 string subdir = get_out_dir();
258
259 // Make output directory
260 MKDIR(subdir.c_str());
261
262 if (namespaced_) {
263 require_prefix_ = rb_namespace_to_path_prefix(program_->get_namespace("rb"));
264
265 string dir = require_prefix_;
266 string::size_type loc;
267
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);
272 }
273 }
274
275 namespace_dir_ = subdir;
276
277 // Make output file
278 string f_types_name = namespace_dir_ + underscore(program_name_) + "_types.rb";
279 f_types_.open(f_types_name.c_str());
280
281 string f_consts_name = namespace_dir_ + underscore(program_name_) + "_constants.rb";
282 f_consts_.open(f_consts_name.c_str());
283
284 // Print header
285 f_types_ << rb_autogen_comment() << endl << render_require_thrift() << render_includes() << endl;
286 begin_namespace(f_types_, ruby_modules(program_));
287
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_));
291 }
292
293 /**
294 * Renders the require of thrift itself, and possibly of the rubygems dependency.
295 */
296 string t_rb_generator::render_require_thrift() {
297 if (require_rubygems_) {
298 return "require 'rubygems'\nrequire 'thrift'\n";
299 } else {
300 return "require 'thrift'\n";
301 }
302 }
303
304 /**
305 * Renders all the imports necessary for including another Thrift program
306 */
307 string t_rb_generator::render_includes() {
308 const vector<t_program*>& includes = program_->get_includes();
309 string result = "";
310 for (auto include : includes) {
311 if (namespaced_) {
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";
317 } else {
318 result += "require '" + underscore(include->get_name()) + "_types'\n";
319 }
320 }
321 if (includes.size() > 0) {
322 result += "\n";
323 }
324 return result;
325 }
326
327 /**
328 * Autogen'd comment
329 */
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";
333 }
334
335 /**
336 * Closes the type files
337 */
338 void t_rb_generator::close_generator() {
339 // Close types file
340 end_namespace(f_types_, ruby_modules(program_));
341 end_namespace(f_consts_, ruby_modules(program_));
342 f_types_.close();
343 f_consts_.close();
344 }
345
346 /**
347 * Generates a typedef. This is not done in Ruby, types are all implicit.
348 *
349 * @param ttypedef The type definition
350 */
351 void t_rb_generator::generate_typedef(t_typedef* ttypedef) {
352 (void)ttypedef;
353 }
354
355 /**
356 * Generates code for an enumerated type. Done using a class to scope
357 * the values.
358 *
359 * @param tenum The enumeration
360 */
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();
364
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();
369
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());
374
375 generate_rdoc(f_types_, *c_iter);
376 f_types_.indent() << name << " = " << value << endl;
377 }
378
379 // Create a hash mapping values back to their names (as strings) since ruby has no native enum
380 // type
381 f_types_.indent() << "VALUE_MAP = {";
382 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
383 // Populate the hash
384 int value = (*c_iter)->get_value();
385 if (c_iter != constants.begin())
386 f_types_ << ", ";
387 f_types_ << value << " => \"" << capitalize((*c_iter)->get_name()) << "\"";
388 }
389 f_types_ << "}" << endl;
390
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) {
394 // Populate the set
395 if (c_iter != constants.begin())
396 f_types_ << ", ";
397 f_types_ << capitalize((*c_iter)->get_name());
398 }
399 f_types_ << "]).freeze" << endl;
400
401 f_types_.indent_down();
402 f_types_.indent() << "end" << endl << endl;
403 }
404
405 /**
406 * Generate a constant value
407 */
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();
412
413 name[0] = toupper(name[0]);
414
415 f_consts_.indent() << name << " = ";
416 render_const_value(f_consts_, type, value) << endl << endl;
417 }
418
419 /**
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
423 */
424 t_rb_ofstream& t_rb_generator::render_const_value(t_rb_ofstream& out,
425 t_type* type,
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();
430 switch (tbase) {
431 case t_base_type::TYPE_STRING:
432 out << "%q\"" << get_escaped_string(value) << '"';
433 break;
434 case t_base_type::TYPE_BOOL:
435 out << (value->get_integer() > 0 ? "true" : "false");
436 break;
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();
442 break;
443 case t_base_type::TYPE_DOUBLE:
444 if (value->get_type() == t_const_value::CV_INTEGER) {
445 out << value->get_integer();
446 } else {
447 out << value->get_double();
448 }
449 break;
450 default:
451 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
452 }
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;
457 out.indent_up();
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();
467 }
468 }
469 if (field_type == NULL) {
470 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
471 }
472 out.indent();
473 render_const_value(out, g_type_string, v_iter->first) << " => ";
474 render_const_value(out, field_type, v_iter->second) << "," << endl;
475 }
476 out.indent_down();
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();
481 out << "{" << endl;
482 out.indent_up();
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) {
486 out.indent();
487 render_const_value(out, ktype, v_iter->first) << " => ";
488 render_const_value(out, vtype, v_iter->second) << "," << endl;
489 }
490 out.indent_down();
491 out.indent() << "}";
492 } else if (type->is_list() || type->is_set()) {
493 t_type* etype;
494 if (type->is_list()) {
495 etype = ((t_list*)type)->get_elem_type();
496 } else {
497 etype = ((t_set*)type)->get_elem_type();
498 }
499 if (type->is_set()) {
500 out << "Set.new([" << endl;
501 } else {
502 out << "[" << endl;
503 }
504 out.indent_up();
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) {
508 out.indent();
509 render_const_value(out, etype, *v_iter) << "," << endl;
510 }
511 out.indent_down();
512 if (type->is_set()) {
513 out.indent() << "])";
514 } else {
515 out.indent() << "]";
516 }
517 } else {
518 throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
519 }
520 return out;
521 }
522
523 /**
524 * Generates a ruby struct
525 */
526 void t_rb_generator::generate_struct(t_struct* tstruct) {
527 if (tstruct->is_union()) {
528 generate_rb_union(f_types_, tstruct, false);
529 } else {
530 generate_rb_struct(f_types_, tstruct, false);
531 }
532 }
533
534
535 /**
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.
541 */
542 void t_rb_generator::generate_forward_declaration(t_struct* tstruct) {
543 generate_rb_struct_declaration(f_types_, tstruct, tstruct->is_xception());
544 }
545
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";
550 }
551 if (is_exception) {
552 out << " < ::Thrift::Exception";
553 }
554 out << "; end" << endl << endl;
555 }
556
557 /**
558 * Generates a struct definition for a thrift exception. Basically the same
559 * as a struct but extends the Exception class.
560 *
561 * @param txception The struct definition
562 */
563 void t_rb_generator::generate_xception(t_struct* txception) {
564 generate_rb_struct(f_types_, txception, true);
565 }
566
567 /**
568 * Generates a ruby struct
569 */
570 void t_rb_generator::generate_rb_struct(t_rb_ofstream& out,
571 t_struct* tstruct,
572 bool is_exception = false) {
573 generate_rdoc(out, tstruct);
574 out.indent() << "class " << type_name(tstruct);
575 if (is_exception) {
576 out << " < ::Thrift::Exception";
577 }
578 out << endl;
579
580 out.indent_up();
581 out.indent() << "include ::Thrift::Struct, ::Thrift::Struct_Union" << endl;
582
583 if (is_exception) {
584 generate_rb_simple_exception_constructor(out, tstruct);
585 }
586
587 generate_field_constants(out, tstruct);
588 generate_field_defns(out, tstruct);
589 generate_rb_struct_required_validator(out, tstruct);
590
591 out.indent() << "::Thrift::Struct.generate_accessors self" << endl;
592
593 out.indent_down();
594 out.indent() << "end" << endl << endl;
595 }
596
597 /**
598 * Generates a ruby union
599 */
600 void t_rb_generator::generate_rb_union(t_rb_ofstream& out,
601 t_struct* tstruct,
602 bool is_exception = false) {
603 (void)is_exception;
604 generate_rdoc(out, tstruct);
605 out.indent() << "class " << type_name(tstruct) << " < ::Thrift::Union" << endl;
606
607 out.indent_up();
608 out.indent() << "include ::Thrift::Struct_Union" << endl;
609
610 generate_field_constructors(out, tstruct);
611
612 generate_field_constants(out, tstruct);
613 generate_field_defns(out, tstruct);
614 generate_rb_union_validator(out, tstruct);
615
616 out.indent() << "::Thrift::Union.generate_accessors self" << endl;
617
618 out.indent_down();
619 out.indent() << "end" << endl << endl;
620 }
621
622 void t_rb_generator::generate_field_constructors(t_rb_ofstream& out, t_struct* tstruct) {
623
624 out.indent() << "class << self" << endl;
625 out.indent_up();
626
627 const vector<t_field*>& fields = tstruct->get_members();
628 vector<t_field*>::const_iterator f_iter;
629
630 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
631 if (f_iter != fields.begin()) {
632 out << endl;
633 }
634 std::string field_name = (*f_iter)->get_name();
635
636 out.indent() << "def " << field_name << "(val)" << endl;
637 out.indent() << " " << tstruct->get_name() << ".new(:" << field_name << ", val)" << endl;
638 out.indent() << "end" << endl;
639 }
640
641 out.indent_down();
642 out.indent() << "end" << endl;
643
644 out << endl;
645 }
646
647 void t_rb_generator::generate_rb_simple_exception_constructor(t_rb_ofstream& out,
648 t_struct* tstruct) {
649 const vector<t_field*>& members = tstruct->get_members();
650
651 if (members.size() == 1) {
652 vector<t_field*>::const_iterator m_iter = members.begin();
653
654 if ((*m_iter)->get_type()->is_string()) {
655 string name = (*m_iter)->get_name();
656
657 out.indent() << "def initialize(message=nil)" << endl;
658 out.indent_up();
659 out.indent() << "super()" << endl;
660 out.indent() << "self." << name << " = message" << endl;
661 out.indent_down();
662 out.indent() << "end" << endl << endl;
663
664 if (name != "message") {
665 out.indent() << "def message; " << name << " end" << endl << endl;
666 }
667 }
668 }
669 }
670
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;
674
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);
678
679 out.indent() << cap_field_name << " = " << (*f_iter)->get_key() << endl;
680 }
681 out << endl;
682 }
683
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;
687
688 out.indent() << "FIELDS = {" << endl;
689 out.indent_up();
690 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
691 if (f_iter != fields.begin()) {
692 out << "," << endl;
693 }
694
695 // generate the field docstrings within the FIELDS constant. no real better place...
696 generate_rdoc(out, *f_iter);
697
698 out.indent() << upcase_string((*f_iter)->get_name()) << " => ";
699
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);
705 }
706 out.indent_down();
707 out << endl;
708 out.indent() << "}" << endl << endl;
709
710 out.indent() << "def struct_fields; FIELDS; end" << endl << endl;
711 }
712
713 void t_rb_generator::generate_field_data(t_rb_ofstream& out,
714 t_type* field_type,
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);
719
720 // Begin this field's defn
721 out << "{:type => " << type_to_enum(field_type);
722
723 if (!field_name.empty()) {
724 out << ", :name => '" << field_name << "'";
725 }
726
727 if (field_value != NULL) {
728 out << ", :default => ";
729 render_const_value(out, field_type, field_value);
730 }
731
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()) {
739 out << ", :key => ";
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());
746 }
747 } else {
748 if (((t_base_type*)field_type)->is_binary()) {
749 out << ", :binary => true";
750 }
751 }
752
753 if (optional) {
754 out << ", :optional => true";
755 }
756
757 if (field_type->is_enum()) {
758 out << ", :enum_class => " << full_type_name(field_type);
759 }
760
761 // End of this field's defn
762 out << "}";
763 }
764
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;
768 out.indent_up();
769 }
770 }
771
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();
774 ++m_iter) {
775 out.indent_down();
776 out.indent() << "end" << endl;
777 }
778 }
779
780 /**
781 * Generates a thrift service.
782 *
783 * @param tservice The service definition
784 */
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());
788
789 f_service_ << rb_autogen_comment() << endl << render_require_thrift();
790
791 if (tservice->get_extends() != NULL) {
792 if (namespaced_) {
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;
796 } else {
797 f_service_ << "require '" << require_prefix_
798 << underscore(tservice->get_extends()->get_name()) << "'" << endl;
799 }
800 }
801
802 f_service_ << "require '" << require_prefix_ << underscore(program_name_) << "_types'" << endl
803 << endl;
804
805 begin_namespace(f_service_, ruby_modules(tservice->get_program()));
806
807 f_service_.indent() << "module " << capitalize(tservice->get_name()) << endl;
808 f_service_.indent_up();
809
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);
814
815 f_service_.indent_down();
816 f_service_.indent() << "end" << endl << endl;
817
818 end_namespace(f_service_, ruby_modules(tservice->get_program()));
819
820 // Close service file
821 f_service_.close();
822 }
823
824 /**
825 * Generates helper functions for a service.
826 *
827 * @param tservice The service to generate a header definition for
828 */
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;
832
833 f_service_.indent() << "# HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
834
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);
839 }
840 }
841
842 /**
843 * Generates a struct and helpers for a function.
844 *
845 * @param tfunction The function
846 */
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);
852 }
853
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);
859 }
860 generate_rb_struct(f_service_, &result);
861 }
862
863 /**
864 * Generates a service client definition.
865 *
866 * @param tservice The service to generate a server for.
867 */
868 void t_rb_generator::generate_service_client(t_service* tservice) {
869 string extends = "";
870 string extends_client = "";
871 if (tservice->get_extends() != NULL) {
872 extends = full_type_name(tservice->get_extends());
873 extends_client = " < " + extends + "::Client ";
874 }
875
876 f_service_.indent() << "class Client" << extends_client << endl;
877 f_service_.indent_up();
878
879 f_service_.indent() << "include ::Thrift::Client" << endl << endl;
880
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();
889
890 // Open function
891 f_service_.indent() << "def " << function_signature(*f_iter) << endl;
892 f_service_.indent_up();
893 f_service_.indent() << "send_" << funname << "(";
894
895 bool first = true;
896 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
897 if (first) {
898 first = false;
899 } else {
900 f_service_ << ", ";
901 }
902 f_service_ << (*fld_iter)->get_name();
903 }
904 f_service_ << ")" << endl;
905
906 if (!(*f_iter)->is_oneway()) {
907 f_service_.indent();
908 if (!(*f_iter)->get_returntype()->is_void()) {
909 f_service_ << "return ";
910 }
911 f_service_ << "recv_" << funname << "()" << endl;
912 }
913 f_service_.indent_down();
914 f_service_.indent() << "end" << endl;
915 f_service_ << endl;
916
917 f_service_.indent() << "def send_" << function_signature(*f_iter) << endl;
918 f_service_.indent_up();
919
920 std::string argsname = capitalize((*f_iter)->get_name() + "_args");
921 std::string messageSendProc = (*f_iter)->is_oneway() ? "send_oneway_message" : "send_message";
922
923 f_service_.indent() << messageSendProc << "('" << funname << "', " << argsname;
924
925 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
926 f_service_ << ", :" << (*fld_iter)->get_name() << " => " << (*fld_iter)->get_name();
927 }
928
929 f_service_ << ")" << endl;
930
931 f_service_.indent_down();
932 f_service_.indent() << "end" << endl;
933
934 if (!(*f_iter)->is_oneway()) {
935 std::string resultname = capitalize((*f_iter)->get_name() + "_result");
936 t_struct noargs(program_);
937
938 t_function recv_function((*f_iter)->get_returntype(),
939 string("recv_") + (*f_iter)->get_name(),
940 &noargs);
941 // Open function
942 f_service_ << endl;
943 f_service_.indent() << "def " << function_signature(&recv_function) << endl;
944 f_service_.indent_up();
945
946 // TODO(mcslee): Validate message reply here, seq ids etc.
947
948 f_service_.indent() << "result = receive_message(" << resultname << ")" << endl;
949
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;
953 }
954
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;
961 }
962
963 // Careful, only return _result if not a void function
964 if ((*f_iter)->get_returntype()->is_void()) {
965 f_service_.indent() << "return" << endl;
966 } else {
967 f_service_.indent() << "raise "
968 "::Thrift::ApplicationException.new(::Thrift::ApplicationException::"
969 "MISSING_RESULT, '" << (*f_iter)->get_name()
970 << " failed: unknown result')" << endl;
971 }
972
973 // Close function
974 f_service_.indent_down();
975 f_service_.indent() << "end" << endl << endl;
976 }
977 }
978
979 f_service_.indent_down();
980 f_service_.indent() << "end" << endl << endl;
981 }
982
983 /**
984 * Generates a service server definition.
985 *
986 * @param tservice The service to generate a server for.
987 */
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;
992
993 string extends = "";
994 string extends_processor = "";
995 if (tservice->get_extends() != NULL) {
996 extends = full_type_name(tservice->get_extends());
997 extends_processor = " < " + extends + "::Processor ";
998 }
999
1000 // Generate the header portion
1001 f_service_.indent() << "class Processor" << extends_processor << endl;
1002 f_service_.indent_up();
1003
1004 f_service_.indent() << "include ::Thrift::Processor" << endl << endl;
1005
1006 // Generate the process subfunctions
1007 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1008 generate_process_function(tservice, *f_iter);
1009 }
1010
1011 f_service_.indent_down();
1012 f_service_.indent() << "end" << endl << endl;
1013 }
1014
1015 /**
1016 * Generates a process function definition.
1017 *
1018 * @param tfunction The function to write a dispatcher for
1019 */
1020 void t_rb_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
1021 (void)tservice;
1022 // Open function
1023 f_service_.indent() << "def process_" << tfunction->get_name() << "(seqid, iprot, oprot)" << endl;
1024 f_service_.indent_up();
1025
1026 string argsname = capitalize(tfunction->get_name()) + "_args";
1027 string resultname = capitalize(tfunction->get_name()) + "_result";
1028
1029 f_service_.indent() << "args = read_args(iprot, " << argsname << ")" << endl;
1030
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;
1034
1035 // Declare result for non oneway function
1036 if (!tfunction->is_oneway()) {
1037 f_service_.indent() << "result = " << resultname << ".new()" << endl;
1038 }
1039
1040 // Try block for a function with exceptions
1041 if (xceptions.size() > 0) {
1042 f_service_.indent() << "begin" << endl;
1043 f_service_.indent_up();
1044 }
1045
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;
1050
1051 f_service_.indent();
1052 if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
1053 f_service_ << "result.success = ";
1054 }
1055 f_service_ << "@handler." << tfunction->get_name() << "(";
1056 bool first = true;
1057 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1058 if (first) {
1059 first = false;
1060 } else {
1061 f_service_ << ", ";
1062 }
1063 f_service_ << "args." << (*f_iter)->get_name();
1064 }
1065 f_service_ << ")" << endl;
1066
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()
1075 << endl;
1076 f_service_.indent_down();
1077 }
1078 }
1079 f_service_.indent() << "end" << endl;
1080 }
1081
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;
1087 return;
1088 }
1089
1090 f_service_.indent() << "write_result(result, oprot, '" << tfunction->get_name() << "', seqid)"
1091 << endl;
1092
1093 // Close function
1094 f_service_.indent_down();
1095 f_service_.indent() << "end" << endl << endl;
1096 }
1097
1098 /**
1099 * Renders a function signature of the form 'type name(args)'
1100 *
1101 * @param tfunction Function definition
1102 * @return String of rendered function definition
1103 */
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()) + ")";
1107 }
1108
1109 /**
1110 * Renders a field list
1111 */
1112 string t_rb_generator::argument_list(t_struct* tstruct) {
1113 string result = "";
1114
1115 const vector<t_field*>& fields = tstruct->get_members();
1116 vector<t_field*>::const_iterator f_iter;
1117 bool first = true;
1118 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1119 if (first) {
1120 first = false;
1121 } else {
1122 result += ", ";
1123 }
1124 result += (*f_iter)->get_name();
1125 }
1126 return result;
1127 }
1128
1129 string t_rb_generator::type_name(const t_type* ttype) {
1130 string prefix = "";
1131
1132 string name = ttype->get_name();
1133 if (ttype->is_struct() || ttype->is_xception() || ttype->is_enum()) {
1134 name = capitalize(ttype->get_name());
1135 }
1136
1137 return prefix + name;
1138 }
1139
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 + "::";
1145 }
1146 return prefix + type_name(ttype);
1147 }
1148
1149 /**
1150 * Converts the parse type to a Ruby tyoe
1151 */
1152 string t_rb_generator::type_to_enum(t_type* type) {
1153 type = get_true_type(type);
1154
1155 if (type->is_base_type()) {
1156 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1157 switch (tbase) {
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";
1174 }
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";
1185 }
1186
1187 throw "INVALID TYPE IN type_to_enum: " + type->get_name();
1188 }
1189
1190 string t_rb_generator::rb_namespace_to_path_prefix(string rb_namespace) {
1191 string namespaces_left = rb_namespace;
1192 string::size_type loc;
1193
1194 string path_prefix = "";
1195
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);
1199 }
1200 if (namespaces_left.size() > 0) {
1201 path_prefix = path_prefix + underscore(namespaces_left) + "/";
1202 }
1203 return path_prefix;
1204 }
1205
1206 void t_rb_generator::generate_rdoc(t_rb_ofstream& out, t_doc* tdoc) {
1207 if (tdoc->has_doc()) {
1208 out.indent();
1209 generate_docstring_comment(out, "", "# ", tdoc->get_doc(), "");
1210 }
1211 }
1212
1213 void t_rb_generator::generate_rb_struct_required_validator(t_rb_ofstream& out, t_struct* tstruct) {
1214 out.indent() << "def validate" << endl;
1215 out.indent_up();
1216
1217 const vector<t_field*>& fields = tstruct->get_members();
1218 vector<t_field*>::const_iterator f_iter;
1219
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?";
1227 } else {
1228 out << " unless @" << field->get_name();
1229 }
1230 out << endl;
1231 }
1232 }
1233
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);
1237
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;
1242 out.indent_up();
1243 out.indent() << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, "
1244 "'Invalid value of field " << field->get_name() << "!')" << endl;
1245 out.indent_down();
1246 out.indent() << "end" << endl;
1247 }
1248 }
1249
1250 out.indent_down();
1251 out.indent() << "end" << endl << endl;
1252 }
1253
1254 void t_rb_generator::generate_rb_union_validator(t_rb_ofstream& out, t_struct* tstruct) {
1255 out.indent() << "def validate" << endl;
1256 out.indent_up();
1257
1258 const vector<t_field*>& fields = tstruct->get_members();
1259 vector<t_field*>::const_iterator f_iter;
1260
1261 out.indent()
1262 << "raise(StandardError, 'Union fields are not set.') if get_set_field.nil? || get_value.nil?"
1263 << endl;
1264
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);
1268
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)"
1275 << endl;
1276 out.indent() << "end" << endl;
1277 }
1278 }
1279
1280 out.indent_down();
1281 out.indent() << "end" << endl << endl;
1282 }
1283
1284 THRIFT_REGISTER_GENERATOR(
1285 rb,
1286 "Ruby",
1287 " rubygems: Add a \"require 'rubygems'\" line to the top of each generated file.\n"
1288 " namespaced: Generate files in idiomatic namespaced directories.\n")