# coding: utf-8 require 'goa_model_gen' require 'goa_model_gen/goa' require "active_support/core_ext/string" module GoaModelGen class Field attr_reader :name, :type, :default attr_accessor :format # for swagger. See https://swagger.io/docs/specification/data-models/data-types/ attr_accessor :required attr_reader :type_obj attr_reader :datastore_tag def initialize(name, attrs) @name = name @type = attrs['type'] @format = attrs['format'] @required = attrs['required'] @default = attrs['default'] @datastore_tag = attrs['datastore_tag'] end # https://goa.design/design/overview/ PRIMITIVE_TYPES = %w[bool int int64 float string time.Time uuid.UUID *datastore.Key] def goa_name Goa.capitalize_join(name.split("_")) end def primitive? PRIMITIVE_TYPES.include?(type) end def custom? !primitive? end def optional? !required end def not_null? required || !default.nil? end def nullable? !not_null? end def assign_type_base(types) @type_obj = types[self.type] end def tag json_tag = name.underscore.dup json_tag << ',omitempty' if nullable? validate_tag = 'required' unless nullable? [ ['json', json_tag], ['validate', validate_tag], ['datastore', datastore_tag], ].map{|k,v| v ? "#{k}:\"#{v}\"" : nil}.compact.join(' ') end def definition "#{ name } #{ type } `#{ tag }`" end # https://swagger.io/docs/specification/data-models/data-types/ # https://tour.golang.org/basics/11 # https://golang.org/pkg/go/types/#pkg-variables SWAGGER_TYPE_TO_GOLANG_TYPE = { "string" => Hash.new("string").update( "date" => "time.Time", "date-time" => "time.Time", ), "number" => Hash.new("float32").update( "double" => "float64", ), "integer" => Hash.new("int"), "boolean" => Hash.new("bool"), } def golang_type format2type = SWAGGER_TYPE_TO_GOLANG_TYPE[type] raise "Golang type not found for #{self.inspect}" unless format2type return format2type[format] end def conv_func_part_for_model conv_func_part_for(type, !!(/\A\*/ =~ type)) end def conv_func_part_for_payload conv_func_part_for(golang_type, nullable?) end def conv_func_part_for_media_type conv_func_part_for(golang_type, nullable?) end def conv_func_part_for(value, with_pointer) r = value.sub(/\A\*/, '').split('.').map(&:camelize).join with_pointer ? "#{r}Pointer" : r end def payload_assignment_options(f) if custom? if type_obj && type_obj.base if f.not_null? return false, false, type # 型キャスト else st = f.golang_type.camelize dt = type_obj.base.camelize return false, false, ["#{st}PointerTo#{dt}", type] # ポインタを値にしてから型キャスト end else return false, true, "#{type}PayloadToModel" end else if type == f.golang_type if f.not_null? return true, nil, nil else return false, false, "#{f.conv_func_part_for_payload}To#{conv_func_part_for_model}" end else with_error = (f.type == 'string') return false, with_error, "#{f.conv_func_part_for_payload}To#{conv_func_part_for_model}" end end end def media_type_assignment_options(f) if custom? if type_obj && type_obj.base if f.not_null? return false, false, type_obj.base # 型キャスト else st = type_obj.base.camelize dt = f.golang_type.camelize return false, false, [type_obj.base, "#{st}To#{dt}Pointer"] # 型キャストしてポインタを値に変換 end else return false, true, "#{type}ModelToMediaType" end else if type == f.golang_type if f.not_null? return true, nil, nil else return false, false, "#{conv_func_part_for_model}To#{f.conv_func_part_for_payload}" end else with_error = (type == 'string') return false, with_error, "#{conv_func_part_for_model}To#{f.conv_func_part_for_payload}" end end end end end