class Stark::Ruby
Constants
- CoreTypes
- ReadFunc
- WriteFunc
Public Class Methods
new(stream=STDOUT)
click to toggle source
# File lib/stark/ruby.rb, line 5 def initialize(stream=STDOUT) @namespace = nil @indent = 0 @structs = {} @enums = {} @exceptions = {} @stream = stream o "require 'set'" o "require 'stark/client'" o "require 'stark/processor'" o "require 'stark/struct'" o "require 'stark/exception'" end
Public Instance Methods
close()
click to toggle source
# File lib/stark/ruby.rb, line 27 def close write_protocol if @namespace outdent o "end" end end
const_to_ruby(val)
click to toggle source
# File lib/stark/ruby.rb, line 65 def const_to_ruby(val) case val when Stark::Parser::AST::ConstString return %Q!"#{val.value}"! when Stark::Parser::AST::ConstInt return val.value when Stark::Parser::AST::ConstDouble return val.value when Stark::Parser::AST::ConstIdentifier return val.value.to_sym when Stark::Parser::AST::ConstList parts = val.values.map { |x| const_to_ruby(x) } return "[#{parts.join(', ')}]" when Stark::Parser::AST::ConstMap parts = val.values.map { |(k,v)| const_to_ruby(k) + " => " + const_to_ruby(v) } return "{#{parts.join(', ')}}" else raise "Unsupported default type: #{val.class}" end end
indent()
click to toggle source
# File lib/stark/ruby.rb, line 137 def indent @indent += 2 end
o(str = nil)
click to toggle source
# File lib/stark/ruby.rb, line 132 def o(str = nil) @stream.print(" " * @indent) if str @stream.puts str end
object_type(t)
click to toggle source
# File lib/stark/ruby.rb, line 208 def object_type(t) case t when Stark::Parser::AST::Map "map<#{object_type(t.key)},#{object_type(t.value)}>" when Stark::Parser::AST::List "list<#{object_type(t.value)}>" when Stark::Parser::AST::Set "set<#{object_type(t.value)}>" else t end end
outdent()
click to toggle source
# File lib/stark/ruby.rb, line 141 def outdent @indent -= 2 end
process_enum(enum)
click to toggle source
# File lib/stark/ruby.rb, line 53 def process_enum(enum) @enums[enum.name] = enum e = "Enum_#{enum.name}" o "#{e} = Hash.new { |h,k| p [:bad, k]; h[k] = -1 }" idx = 0 enum.values.each do |f| o "#{e}[#{idx}] = :'#{f}'" o "#{e}[:'#{f}'] = #{idx}" idx += 1 end end
process_exception(str)
click to toggle source
# File lib/stark/ruby.rb, line 121 def process_exception(str) @exceptions[str.name] = str o o "class #{str.name} < Stark::Exception" indent write_field_declarations str.fields outdent o "end" end
process_include(inc)
click to toggle source
# File lib/stark/ruby.rb, line 49 def process_include(inc) raise NotImplementedError, "include not implemented" end
process_namespace(ns)
click to toggle source
# File lib/stark/ruby.rb, line 35 def process_namespace(ns) return unless [nil, 'rb'].include?(ns.lang) @namespace = ns.namespace.gsub('.', '::') parts = @namespace.split('::') if parts.length > 1 0.upto(parts.length - 2) do |i| o "module #{parts[0..i].join('::')}; end unless defined?(#{parts[0..i].join('::')})" end end o o "module #{@namespace}" indent end
process_service(serv)
click to toggle source
# File lib/stark/ruby.rb, line 561 def process_service(serv) unless @protocol_declared o o "module Protocol; end" @protocol_declared = true end o o "module #{serv.name}" indent write_client serv o write_processor serv outdent o "end" end
process_struct(str)
click to toggle source
# File lib/stark/ruby.rb, line 110 def process_struct(str) @structs[str.name] = str o o "class #{str.name} < Stark::Struct" indent write_field_declarations str.fields outdent o "end" end
read_func(t)
click to toggle source
# File lib/stark/ruby.rb, line 221 def read_func(t) ReadFunc[t] || raise("unknown type - #{t}") end
read_type(t, lhs, found_type = 'rtype')
click to toggle source
# File lib/stark/ruby.rb, line 229 def read_type(t, lhs, found_type = 'rtype') o "#{lhs} = expect ip, #{wire_type(t)}, #{found_type} do" indent if desc = @structs[t] o "read_#{desc.name}(ip)" elsif desc = @enums[t] o "Enum_#{desc.name}[ip.read_i32]" elsif t.kind_of? Stark::Parser::AST::Map o "expect_map ip, #{wire_type(t.key)}, #{wire_type(t.value)} do |kt,vt,size|" indent o "{}.tap do |_hash|" indent o "size.times do" indent read_type(t.key, "k", "kt") read_type(t.value, "v", "vt") o "_hash[k] = v" outdent o "end" outdent o "end" outdent o "end" elsif t.kind_of? Stark::Parser::AST::List o "expect_list ip, #{wire_type(t.value)} do |vt,size|" indent o "Array.new(size) do" indent read_type t.value, "_elem", "vt" outdent o "end" outdent o "end" elsif t.kind_of? Stark::Parser::AST::Set o "expect_set ip, #{wire_type(t.value)} do |vt,size|" indent o "_arr = Array.new(size) do" indent read_type t.value, "element", "vt" o "element" outdent o "end" o "::Set.new(_arr)" outdent o "end" else o "ip.#{read_func(t)}" end outdent o "end" end
run(ast)
click to toggle source
# File lib/stark/ruby.rb, line 22 def run(ast) ast.each { |a| a.accept self } close end
type(t)
click to toggle source
# File lib/stark/ruby.rb, line 182 def type(t) CoreTypes[t] || raise("unknown type - #{t}") end
wire_type(t)
click to toggle source
# File lib/stark/ruby.rb, line 186 def wire_type(t) return "::Thrift::Types::STRUCT" if @structs[t] || @exceptions[t] return "::Thrift::Types::I32" if @enums[t] case t when Stark::Parser::AST::Map "::Thrift::Types::MAP" when Stark::Parser::AST::List "::Thrift::Types::LIST" when Stark::Parser::AST::Set "::Thrift::Types::SET" else type t end end
wire_type_and_ruby_type(t)
click to toggle source
# File lib/stark/ruby.rb, line 202 def wire_type_and_ruby_type(t) return "::Thrift::Types::STRUCT, #{t}" if @structs[t] || @exceptions[t] return "::Thrift::Types::I32, Enum_#{t}" if @enums[t] wire_type(t) end
write_client(serv)
click to toggle source
# File lib/stark/ruby.rb, line 476 def write_client(serv) o "class Client < Stark::Client" indent o "include Protocol" serv.functions.each do |func| names = Array(func.arguments).map { |f| f.name }.join(", ") o o "def #{func.name}(#{names})" indent o "op = @oprot" o "op.write_message_begin '#{func.name}', ::Thrift::MessageTypes::CALL, 0" o "op.write_struct_begin \"#{func.name}_args\"" Array(func.arguments).each do |arg| o "#{arg.name} = value_for_write #{arg.name}, #{wire_type_and_ruby_type(arg.type)}" write_field arg.type, arg.name, arg.index end o "op.write_field_stop" o "op.write_struct_end" o "op.write_message_end" o "op.trans.flush" if func.options == :oneway o "return" outdent o "end" next end o "ip = @iprot" o "_, mtype, _ = ip.read_message_begin" o "handle_exception mtype" o "ip.read_struct_begin" o "_, rtype, rid = ip.read_field_begin" if t = func.throws o "case rid" t.each do |ex| o "when #{ex.index}" o " _ex = read_#{ex.type}(ip)" o " ip.read_field_end" o " _, rtype, _ = ip.read_field_begin" o " fail if rtype != ::Thrift::Types::STOP" o " ip.read_struct_end" o " ip.read_message_end" o " raise _ex" end o "end" end o "result = nil" o "fail unless rid == 0" if func.return_type != "void" o "if rtype != ::Thrift::Types::STOP" indent read_type func.return_type, "result" o "ip.read_field_end" o "_, rtype, rid = ip.read_field_begin" outdent o "end" end o "fail if rtype != ::Thrift::Types::STOP" o "ip.read_struct_end" o "ip.read_message_end" o "return result" outdent o "end" end outdent o "end" end
write_field(ft, name, idx)
click to toggle source
# File lib/stark/ruby.rb, line 319 def write_field(ft, name, idx) o "op.write_field_begin '#{name}', #{wire_type(ft)}, #{idx}" write_type ft, name o "op.write_field_end" end
write_field_declarations(fields)
click to toggle source
# File lib/stark/ruby.rb, line 88 def write_field_declarations(fields) max_field_len = fields.inject(0) {|max,f| f.name.length > max ? f.name.length : max } max_index_len = fields.inject(0) {|max,f| f.index.to_s.length > max ? f.index.to_s.length : max } current_index = 1 fields.each do |f| if f.index != current_index o "field_number #{f.index}" current_index = f.index end current_index += 1 if f.value o("attr_writer :%-*s # %*s: %s" % [max_field_len, f.name, max_index_len, f.index, object_type(f.type)]) o("def %s; @%s || %s; end" % [f.name, f.name, const_to_ruby(f.value)]) else o("attr_accessor :%-*s # %*s: %s" % [max_field_len, f.name, max_index_len, f.index, object_type(f.type)]) end end end
write_func(t)
click to toggle source
# File lib/stark/ruby.rb, line 225 def write_func(t) WriteFunc[t] || raise("unknown type - #{t}") end
write_processor(serv)
click to toggle source
# File lib/stark/ruby.rb, line 325 def write_processor(serv) o "class Processor < Stark::Processor" indent o "include Protocol" serv.functions.each do |func| o o "def process_#{func.name}(seqid, ip, op)" indent o "ip.read_struct_begin" args = Array(func.arguments) o "fields = {}" o "while true" o " _, ftype, fid = ip.read_field_begin" o " break if ftype == ::Thrift::Types::STOP" if args.empty? o " ip.skip ftype" else o " case fid" args.each do |arg| o " when #{arg.index}" indent; indent read_type arg.type, "fields[#{arg.index}]", "ftype" outdent; outdent end o " else" o " ip.skip ftype" o " end" end o " ip.read_field_end" o "end" o "ip.read_struct_end" o "ip.read_message_end" o "args = #{args.map(&:index).inspect}.inject([]) {|arr,i| arr << fields[i] }" if t = func.throws o "begin" indent o "result = @handler.#{func.name}(*args)" outdent t.each do |ex| o "rescue #{ex.type} => ex#{ex.index}" indent o "op.write_message_begin '#{func.name}', ::Thrift::MessageTypes::REPLY, seqid" o "op.write_struct_begin '#{func.name}_result'" write_field ex.type, "ex#{ex.index}", ex.index o "op.write_field_stop" o "op.write_struct_end" o "op.write_message_end" o "op.trans.flush" o "return" outdent end o "end" else o "result = @handler.#{func.name}(*args)" end if func.options == :oneway o "return result" outdent o "end" next end ft = func.return_type o "result = value_for_write(result, #{wire_type_and_ruby_type(ft)})" if ft != "void" o "op.write_message_begin '#{func.name}', ::Thrift::MessageTypes::REPLY, seqid" o "op.write_struct_begin '#{func.name}_result'" if ft != "void" o "if result" indent write_field ft, 'result', 0 outdent o "end" end o "op.write_field_stop" o "op.write_struct_end" o "op.write_message_end" o "op.trans.flush" o "return result" outdent o "end" end outdent o "end" end
write_protocol()
click to toggle source
# File lib/stark/ruby.rb, line 422 def write_protocol o o "module Protocol" indent @structs.merge(@exceptions).each do |name, struct| o o "def read_#{name}(ip)" indent o "obj = #{name}.new" o "ip.read_struct_begin" o "while true" o " _, ftype, fid = ip.read_field_begin" o " break if ftype == ::Thrift::Types::STOP" o " case fid" struct.fields.each do |f| o " when #{f.index}" indent; indent read_type f.type, "obj.#{f.name}", 'ftype' outdent; outdent end o " else" o " ip.skip ftype" o " end" o " ip.read_field_end" o "end" o "ip.read_struct_end" o "obj" outdent o "end" o o "def write_#{name}(op, str)" indent o "op.write_struct_begin '#{name}'" struct.fields.each do |f| o "if #{f.name} = value_for_write(str.#{f.name}, #{wire_type_and_ruby_type(f.type)})" indent write_field f.type, f.name, f.index outdent o "end" end o "op.write_field_stop" o "op.write_struct_end" outdent o "end" end outdent o "end" end
write_type(ft, name)
click to toggle source
# File lib/stark/ruby.rb, line 281 def write_type(ft, name) if desc = (@structs[ft] || @exceptions[ft]) o "write_#{desc.name} op, #{name}" elsif desc = @enums[ft] o "op.write_i32 Enum_#{desc.name}[#{name}.to_sym]" elsif ft.kind_of? Stark::Parser::AST::Map o "#{name} = hash_cast #{name}" o "op.write_map_begin(#{wire_type(ft.key)}, #{wire_type(ft.value)}, #{name}.size)" o "#{name}.each do |k,v|" indent write_type ft.key, "k" write_type ft.value, "v" outdent o "end" o "op.write_map_end" elsif ft.kind_of? Stark::Parser::AST::List o "#{name} = Array(#{name})" o "op.write_list_begin(#{wire_type(ft.value)}, #{name}.size)" o "#{name}.each do |v|" indent write_type ft.value, "v" outdent o "end" o "op.write_list_end" elsif ft.kind_of? Stark::Parser::AST::Set o "#{name} = Set.new(#{name})" o "op.write_list_begin(#{wire_type(ft.value)}, #{name}.size)" o "#{name}.each do |v|" indent write_type ft.value, "v" outdent o "end" o "op.write_set_end" elsif ft != "void" o "op.#{write_func(ft)} #{name}" end end