20200426のGoに関する記事は8件です。

GO言語学習ノート(1)変数型①

環境
Windows10
Visual Studio Code (Go for Visual Studio Code,Code Runner)
golang 1.14

1、変数

  静的型付き言語として、go言語はいつも固定的なデータ型を持ち、データ型は変数のメモリ領域上の大きさや保存タープを決める。一旦決まったら、変数値は変更できるが、変数型は変更できない。型変換やポインタ操作によって、様々な方法で変数値を変更できるが、これは変数型を変更することを意味するものではない

1.1 定義

  varを用いて変数を定義する。但しC言語などと違い、変数型は変数名の後に置かれる。実行時、変数の初期値は二進数の零値を渡す。明示的に初期値を提供すれば、変数型を省略できる。

var x int     //初期値は0
var y = true  //自動的に変数型をbool型と推定する

多変量を一気に定義することも可能であり、異なる初期値や型も含まれる

var x,y int           //同じ型の多変量定義
var z,w ="abc",true   //異なる型の初期化

でも、グループで多行の変数定義を整理することを推奨する

var (
     x,y int
     z,w ="abc",true
)

1.2 短変数宣言

  var以外、ショートモードで変数を定義や初期化することができる。

func main(){
    x :=100
    a,b :=1,"xyz"
}

でも、短変数宣言(short variable declaration)はいくつの制限がある:
・変数定義する当時に、明示的に初期化すること
・変数型を提供しないこと
・関数内部に使うこと

以下の注意事項がある
  ①グローバル変数を変更しようが、同名ローカル変数を変更しまった

var x = 100

func main() {
    println(&x, x) //グローバル変数

    x := "abc"     //ローカル変数
    println(&x, x)
}

出力:

0x4cc180     100        //メモリアドレスを比べると、異なる変数とわかる
0xc000029f68   abc

  ②短変数宣言はいつも新しい変数を定義することではなく、部分的に値を割り当てることもできる。

func main() {
    x := 100
    println(&x)

    x, y := 200, "abc" //xは値を割り当てる、yは変数定義
    println(&x, x)
    println(y)
}

出力:

0xc000029f70  
0xc000029f70 200 //メモリアドレスを比べ、xは同じ変数
abc

  ③このような値を割り当てるのは、前提条件が少なくとも、一つの新変数を定義され、かつ同じ作用領域であること

func main() {
    x := 100
    println(&x)

    x := 200    //エラー:no new variables on left side of :=
    println(&x, x)
}
func main() {
    x := 100
    println(&x)
    {
        x, y := 200, 300    //異なる作用領域、これは新変数を定期すること
        println(&x, x, y)
    }

}

出力:

0xc000029f70
0xc000029f68 200 300

1.3 多変数の割り当て

  割り当ての際、まず、右側の値を計算した後、相次いで割り当てる。

main.py
package main

func main() {
    x, y := 1, 2
    x, y = y+3, x+2
    println(x, y)
}

出力

>go run main.go
5 3

>go tool compile -N -l main.go
>go tool objdump -S main.o

TEXT %22%22.main(SB) gofile..C:/Users/tyous/Desktop/MYWORK/main.go
func main() {
  0x30c                 65488b0c2528000000      MOVQ GS:0x28, CX
  0x315                 488b8900000000          MOVQ 0(CX), CX          [3:7]R_TLS_LE
  0x31c                 483b6110                CMPQ 0x10(CX), SP
  0x320                 0f8685000000            JBE 0x3ab
  0x326                 4883ec30                SUBQ $0x30, SP
  0x32a                 48896c2428              MOVQ BP, 0x28(SP)
  0x32f                 488d6c2428              LEAQ 0x28(SP), BP
        x, y := 1, 2
  0x334                 48c744241001000000      MOVQ $0x1, 0x10(SP)
  0x33d                 48c744240802000000      MOVQ $0x2, 0x8(SP)
        x, y = y+3, x+2
  0x346                 48c744242005000000      MOVQ $0x5, 0x20(SP)
  0x34f                 488b442410              MOVQ 0x10(SP), AX
  0x354                 4883c002                ADDQ $0x2, AX
  0x358                 4889442418              MOVQ AX, 0x18(SP)
  0x35d                 488b442420              MOVQ 0x20(SP), AX
  0x362                 4889442410              MOVQ AX, 0x10(SP)
  0x367                 488b442418              MOVQ 0x18(SP), AX
  0x36c                 4889442408              MOVQ AX, 0x8(SP)
        println(x, y)
  0x371                 e800000000              CALL 0x376              [1:5]R_CALL:runtime.printlock
  0x376                 488b442410              MOVQ 0x10(SP), AX
  0x37b                 48890424                MOVQ AX, 0(SP)
  0x37f                 e800000000              CALL 0x384              [1:5]R_CALL:runtime.printint
  0x384                 e800000000              CALL 0x389              [1:5]R_CALL:runtime.printsp
  0x389                 488b442408              MOVQ 0x8(SP), AX
  0x38e                 48890424                MOVQ AX, 0(SP)
  0x392                 e800000000              CALL 0x397              [1:5]R_CALL:runtime.printint
  0x397                 e800000000              CALL 0x39c              [1:5]R_CALL:runtime.printnl
  0x39c                 e800000000              CALL 0x3a1              [1:5]R_CALL:runtime.printunlock
}
  0x3a1                 488b6c2428              MOVQ 0x28(SP), BP
  0x3a6                 4883c430                ADDQ $0x30, SP
  0x3aa                 c3                      RET
func main() {
  0x3ab                 e800000000              CALL 0x3b0              [1:5]R_CALL:runtime.morestack_noctxt
  0x3b0                 e957ffffff              JMP %22%22.main(SB)

アセンブリ言語とコードを比較すれば

1.4 未使用エラー

コンパイラは未使用のローカル変数をエラーとして扱う。

func main() {
    x := 1
}

出力

> go run main.go
# command-line-arguments
.\main.go:4:2: x declared but not used
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GO言語学習ノート(1)変数型

環境
Windows10
Visual Studio Code (Go for Visual Studio Code,Code Runner)
golang 1.14

1、変数

  静的型付き言語として、go言語はいつも固定的なデータ型を持ち、データ型は変数のメモリ領域上の大きさや保存タープを決める。一旦決まったら、変数値は変更できるが、変数型は変更できない。型変換やポインタ操作によって、様々な方法で変数値を変更できるが、これは変数型を変更することを意味するものではない

1.1 定義

  varを用いて変数を定義する。但しC言語などと違い、変数型は変数名の後に置かれる。実行時、変数の初期値は二進数の零値を渡す。明示的に初期値を提供すれば、変数型を省略できる。

main.go
var x int      //初期値は0
var y = true  //自動的に変数型をbool型と推定する

多変量を一気に定義することも可能であり、異なる初期値や型も含まれる

main.go
var x,y int            //同じ型の多変量定義
var z,w ="abc",true   //異なる型の初期化

でも、グループで多行の変数定義を整理することを推奨する

main.go
var (
     x,y int
     z,w ="abc",true
)

1.2 短変数宣言

  var以外、ショートモードで変数を定義や初期化することができる。

main.go
func main(){
    x :=100
    a,b :=1,"xyz"
}

でも、短変数宣言(short variable declaration)はいくつの制限がある:
・変数定義する当時に、明示的に初期化すること
・変数型を提供しないこと
・関数内部に使うこと

以下の注意事項がある
①グローバル変数を変更しようが、同名ローカル変数を変更しまった

main.go
var x = 100

func main() {
    println(&x, x) //グローバル変数

    x := "abc"     //ローカル変数
    println(&x, x)
}

出力:

0x4cc180     100        //メモリアドレスを比べると、異なる変数とわかる
0xc000029f68   abc

②短変数宣言はいつも新しい変数を定義することではなく、部分的に値を割り当てることもできる。

main.go
func main() {
    x := 100
    println(&x)

    x, y := 200, "abc" //xは値を割り当てる、yは変数定義
    println(&x, x)
    println(y)
}

出力:

0xc000029f70  
0xc000029f70 200 //メモリアドレスを比べ、xは同じ変数
abc

③このような値を割り当てるのは、前提条件が少なくとも、一つの新変数を定義され、かつ同じ作用領域であること

main.go
func main() {
    x := 100
    println(&x)

    x := 200    //エラー:no new variables on left side of :=
    println(&x, x)
}
main.go
func main() {
    x := 100
    println(&x)
    {
        x, y := 200, 300    //異なる作用領域、これは新変数を定期すること
        println(&x, x, y)
    }

}

出力:

0xc000029f70
0xc000029f68 200 300

1.3 多変数の割り当て

  割り当ての際、まず、右側の値を計算した後、相次いで割り当てる。

main.go
package main

func main() {
    x, y := 1, 2
    x, y = y+3, x+2
    println(x, y)
}

出力

>go run main.go
5 3

>go tool compile -N -l main.go
>go tool objdump -S main.o

TEXT %22%22.main(SB) gofile..C:/Users/tyous/Desktop/MYWORK/main.go
func main() {
  0x30c                 65488b0c2528000000      MOVQ GS:0x28, CX
  0x315                 488b8900000000          MOVQ 0(CX), CX          [3:7]R_TLS_LE
  0x31c                 483b6110                CMPQ 0x10(CX), SP
  0x320                 0f8685000000            JBE 0x3ab
  0x326                 4883ec30                SUBQ $0x30, SP
  0x32a                 48896c2428              MOVQ BP, 0x28(SP)
  0x32f                 488d6c2428              LEAQ 0x28(SP), BP
        x, y := 1, 2
  0x334                 48c744241001000000      MOVQ $0x1, 0x10(SP)
  0x33d                 48c744240802000000      MOVQ $0x2, 0x8(SP)
        x, y = y+3, x+2
  0x346                 48c744242005000000      MOVQ $0x5, 0x20(SP)
  0x34f                 488b442410              MOVQ 0x10(SP), AX
  0x354                 4883c002                ADDQ $0x2, AX
  0x358                 4889442418              MOVQ AX, 0x18(SP)
  0x35d                 488b442420              MOVQ 0x20(SP), AX
  0x362                 4889442410              MOVQ AX, 0x10(SP)
  0x367                 488b442418              MOVQ 0x18(SP), AX
  0x36c                 4889442408              MOVQ AX, 0x8(SP)
        println(x, y)
  0x371                 e800000000              CALL 0x376              [1:5]R_CALL:runtime.printlock
  0x376                 488b442410              MOVQ 0x10(SP), AX
  0x37b                 48890424                MOVQ AX, 0(SP)
  0x37f                 e800000000              CALL 0x384              [1:5]R_CALL:runtime.printint
  0x384                 e800000000              CALL 0x389              [1:5]R_CALL:runtime.printsp
  0x389                 488b442408              MOVQ 0x8(SP), AX
  0x38e                 48890424                MOVQ AX, 0(SP)
  0x392                 e800000000              CALL 0x397              [1:5]R_CALL:runtime.printint
  0x397                 e800000000              CALL 0x39c              [1:5]R_CALL:runtime.printnl
  0x39c                 e800000000              CALL 0x3a1              [1:5]R_CALL:runtime.printunlock
}
  0x3a1                 488b6c2428              MOVQ 0x28(SP), BP
  0x3a6                 4883c430                ADDQ $0x30, SP
  0x3aa                 c3                      RET
func main() {
  0x3ab                 e800000000              CALL 0x3b0              [1:5]R_CALL:runtime.morestack_noctxt
  0x3b0                 e957ffffff              JMP %22%22.main(SB)

アセンブリ言語とコードを比較すれば

1.4 未使用エラー

コンパイラは未使用のローカル変数をエラーとして扱う。

main.go
func main() {
    x := 1
}

出力

> go run main.go
# command-line-arguments
.\main.go:4:2: x declared but not used

2、変数名

どの言語も同じ、理解しやすい変数名を付けてでください。

2.1 命名規則

・英文字やアンダーラインで始める。英文字、アンダーライン、数字の組み合わせ。
・大小文字を見分ける
・キャメルケース(camel case)を使用すること
・ローカル変数の短い名前の優先に使用すること
・予約語は使わないすること
・定義済みの組み込み関数、定数、型と同じ名前を使用することは推奨されない
・通常は専用名詞を大文字にするこt、例、escapeHTML

golangでは、漢字などUnicode文字はうまく使えると言っても、漢字などを変数名として使用するのは良い選択ではない。

変数の頭文字は、大文字や小文字によって作用範囲が異なる。大文字の場合、変数は外部からインポートされる。小文字の場合、変数は外部からのインポートされない、内部使うのみだ

2.2 空識別子

pythonと同じく、golangでは、"_"(blank identifier)という特別な識別子がある。通常では、Return値を無視するために使用する。関数のReturn値を受け取るが、読み取ることができない。

main.go
import "strconv"

func main() {
    x, err := strconv.Atoi("12")
    println(x, err)
    y, _ := strconv.Atoi("12") //AtoiのerrのReturn値を無視する
    println(y)
}

出力

12 (0x0,0x0)
12

空識別子は、未使用の変数やパケットに対するコンパイラのエラー検査を一時的に回避できる。

3 定数

main.go
    const x, y int = 123, 0x22
    const s = "hello 世界"

    const (
        i, f = 1, 0.12  //int,float64(デフォルト)
        b    = false
    )

関数の内部に、定数を定義できる、でも、不使用の定数はエラーを起こすので、要注意。

明示的に型を指定すれば、"="左側と右側の型を一致にしなければならない。必要な場合、明示的に型変換する。対応する型の精度(有効桁数)を超えないことを要注意

main.go
    const (
        x, y int  = 99, -999  
        b    byte = byte(x)   //xはint型に指定したので、明示的にbyte型に変換すべき
        n         = uint8(y)  //エラー: constant -999 overflows uint8
    )

定数値は、とあるコンパイラの計算できる式にしてもいける。例えば,unsafe.Sizeof,len,capなど

main.go
import "unsafe"

func main() {
    const (
        ptrSize = unsafe.Sizeof(uintptr(12))
        strSize = len("hello 世界")
    )
    println(ptrSize, strSize)
}

定数グループの中、定数の型と初期値を指定しないと、それらは、前の行の空でない定数の右辺値と同じ

main.go
import "fmt"

func main() {
    const (
        x uint16 = 120
        y        // 前行xの型と値に同じ
        s = "世界"
        z       // 前行sの型と値に同じ
    )
    fmt.Printf("%T,%v\n", y, y)   //型と値を出力
    fmt.Printf("%T,%v\n", z, z)
}

出力

uint16,120
string,世界

3.1 列挙

goでは明示的なenumを定義していない。しかし、iotaを使って自己増加する変数値のセットを実現することによって、列挙を実現する

main.go
    const (
        a =iota    //0
        b          //1
        c          //2
    )
    const (
        _  = iota              //0
        KB = 1 << (10 * iota)  // 1<<(10*1)
        MB                     // 1<<(10*2)
        GM                     // 1<<(10*3)
    )

多変数の定義でも多数のiotaを使える、それぞれ独自に計算するので、お互いに影響を与えない。グループの中、毎行の定数の個数を一致すべきだけ

main.go
    const (
        _,_  = iota,iota*10  // 0,0*10
        a,b                  //1,1*10
        c,d                  //2,2*10
    )

iotaの自分増加を中断すれば、明示的に再開すべき。その次の自分増加は行の順に増加する

main.go
    const (
        a = iota  //0
        b         //1
        c = 100   //100
        d         //100(前の行と同じ)
        e = iota  //4 (iotaを再開、カウントはc、dを含む)
        f         //5
    )

時に型を指定しないと、iotaの型はintだ、でも明示的に型指定が可能

main.go
func main() {

    const (
        a         = iota
        b float32 = iota
        c         = iota (明示的にiotaを指定しないとbの型と同じになる)
    )
    println(a)
    println(b)
    println(c)
}

出力

0
+1.000000e+000
2

実際の作業では、ユーザ定義型で明白な使い道のある列挙型を実現することは推奨される。しかし、これは値の範囲を予め設定した列挙の範囲内に限定されることができない

main.go
package main

type fruit byte

const (
    apple fruit = iota
    banana
    orange
)

func eat(f fruit) {
    println(f)
}

func main() {

    eat(banana)
    eat(100)     // intの範囲は fruit/byteに超えていない
    egg := 2
    eat(egg)    //エラー: cannot use egg  (type int) as type fruit in argument to eat
}

3.2 展開

定数はメモリアドレスがない。

main.go
package main

var x = 100
const y = 100

func main() {
    println(&x, x)
    println(&y, y)    //cannot take the address of y

}

定数は前処理の段階で命令として展開し、使用する

main.go
package main

const y = 0x200

func main() {
    println(y)
}

出力

PS C:\MYWORK> go tool compile -N -l main.go
PS C:\MYWORK> go tool objdump -S main.o
TEXT %22%22.main(SB) gofile..C:/main.go
func main() {
  0x2e1                 65488b0c2528000000      MOVQ GS:0x28, CX
  0x2ea                 488b8900000000          MOVQ 0(CX), CX          [3:7]R_TLS_LE
  0x2f1                 483b6110                CMPQ 0x10(CX), SP
  0x2f5                 7634                    JBE 0x32b
  0x2f7                 4883ec10                SUBQ $0x10, SP
  0x2fb                 48896c2408              MOVQ BP, 0x8(SP)
  0x300                 488d6c2408              LEAQ 0x8(SP), BP
        println(y)
  0x305                 e800000000              CALL 0x30a              [1:5]R_CALL:runtime.printlock
  0x30a                 48c7042400020000        MOVQ $0x200, 0(SP)      // 定数を命令として展開
  0x312                 e800000000              CALL 0x317              [1:5]R_CALL:runtime.printint
  0x317                 e800000000              CALL 0x31c              [1:5]R_CALL:runtime.printnl
  0x31c                 e800000000              CALL 0x321              [1:5]R_CALL:runtime.printunlock
}
  0x321                 488b6c2408              MOVQ 0x8(SP), BP
  0x326                 4883c410                ADDQ $0x10, SP
  0x32a                 c3                      RET
func main() {
  0x32b                 e800000000              CALL 0x330              [1:5]R_CALL:runtime.morestack_noctxt
  0x330                 ebaf                    JMP %22%22.main(SB)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Go言語] GORMを使った単体テストでのDBの初期化

はじめに

GORMにはLaravelにあるような、DBのリフレッシュが用意されていません。
そのため、DBの単体テストをするためには自分でプログラムを準備する必要があります。

前提

まず単体テストをしたい関数ですが、以下のような全件取得するものサンプルとします。
DDDで作っているので、Structを使っていますがこの記事には関係ありません。

type userPersistence struct {
    db *gorm.DB
}

func NewUserPersistence(r Repository) repository.UserRepository {
    return &userPersistence{r.GetConn()}
}

func (bp bookPersistence) GetAllBook() ([]entity.Book, error) {
    var books []entity.Book
    err := bp.db.Find(&books).Error
    if err != nil {
        log.Error(err.Error())
        return nil, errors.New("Get all book failed")
    }
    return books, nil
}

単体テスト

事前準備

単体テスト本体の紹介の前に、準備用のプログラムです。
ポイントは3つあります。

  1. NewTestRepositoryで、DropTableAutoMigrateを使ってDB初期化してます。
  2. insertBooksでテストケース用のデータを投入できるようにしています。
  3. DBでミリ秒が落ちてしまうので、convertDBTimeで時間の精度を調整しています。
const location = "Asia/Tokyo"

// NewRepository DBへ接続を行う
func NewRepository(conf config.Config) (Repository, func(), error) {
    DBURL := fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true&loc=Asia%%2FTokyo", conf.DB.User, conf.DB.Password, conf.DB.Host, conf.DB.Port, conf.DB.Name)
    db, err := gorm.Open("mysql", DBURL)
    if err != nil {
        return nil, nil, err
    }

    // DB切断用の関数を定義
    cleanup := func() {
        if err := db.Close(); err != nil {
            log.Print(err)
        }
    }

    return &repositoryStruct{
        db: db,
    }, cleanup, nil
}

// Automigrate マイグレーション
func (r *repositoryStruct) Automigrate() error {
    return r.db.AutoMigrate(&entity.Book{}).Error
}

// Connectionを取得する
func (r *repositoryStruct) GetConn() *gorm.DB {
    return r.db
}


// NewTestRepository test用DBを準備する
func NewTestRepository() (Repository, func(), error) {
    // test用の設定ファイルを読み込む
    conf, err := config.NewConfig()
    if err != nil {
        return nil, nil, err
    }

    // test用DBへ接続する
    repo, cleanup, err := NewRepository(conf)
    if err != nil {
        return nil, nil, err
    }

    // cleanup db for test
    err = repo.GetConn().DropTableIfExists(&entity.Book{}).Error
    if err != nil {
        return nil, nil, err
    }

    // create tables for test
    err = repo.GetConn().AutoMigrate(
        entity.Book{},
    ).Error
    if err != nil {
        return nil, nil, err
    }
    return repo, cleanup, nil
}

func seedBooks() ([]entity.Book, error) {
    now, err := convertDBTime(now())
    if err != nil {
        return nil, err
    }
    books := []entity.Book{
        {
            Isbn:      "9784798121963",
            Title:     "エリック・エヴァンスのドメイン駆動設計",
            Author:    "エリック・エヴァンス",
            CreatedAt: *now,
            UpdatedAt: *now,
        },
        {
            Isbn:      "9784873117522",
            Title:     "Go言語によるWebアプリケーション開発",
            Author:    "Mat Ryer",
            CreatedAt: *now,
            UpdatedAt: *now,
        },
    }
    return books, nil
}

func insertBooks(r Repository) ([]entity.Book, error) {
    books, err := seedBooks()
    if err != nil {
        return nil, err
    }
    for _, v := range books {
        err = r.GetConn().Create(&v).Error
        if err != nil {
            return nil, err
        }
    }
    var insertedBooks []entity.Book
    err = r.GetConn().Find(&insertedBooks).Error
    if err != nil {
        return nil, err
    }
    return insertedBooks, nil
}

// now timezoneを指定した現在のtimeを返す
func now() time.Time {
    loc, err := time.LoadLocation(location)
    if err != nil {
        log.Fatal(err)
    }
    return time.Now().In(loc)
}

// convertDBTime ミリ秒を切り捨てる
func convertDBTime(t time.Time) (*time.Time, error) {
    loc, err := time.LoadLocation(location)
    if err != nil {
        log.Fatal(err)
    }
    const layout = "2006-01-02 15:04:05 -0700 MST"
    t, parseErr := time.Parse(layout, t.Format(layout))
    if parseErr != nil {
        return nil, parseErr
    }
    t = t.In(loc)
    return &t, nil

}

単体テスト本体

単体テストは以下のようになります。
テストケースごとにNewTestRepository()を呼び出すことで、DBをリフレッシュしています。
あとは普通の単体テストになります。

func TestGetAllBook_Success(t *testing.T) {
    // テスト用DBを準備
    testRepo, cleanup, err := NewTestRepository()
    if err != nil {
        log.Fatal("faild create test repository:", err)
    }
    defer cleanup()

    // テストデータをInsert
    seedBooks, err := insertBooks(testRepo)
    if err != nil {
        log.Fatal("faild insert data", err)
    }

    // テスト実行
    bp := NewBookPersistence(testRepo)
    books, getErr := bp.GetAllBook()
    if getErr != nil {
        log.Fatal(getErr)
    }

    // 結果の検証
    // Insetされているものが全て返ってきているか
    assert.ElementsMatch(t, seedBooks, books)
}

いかがだったでしょうか?
実際に使っているソースなので、関係ないソースが混じってしまっていますが、少しでも皆様の参考になれば幸いです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure SDK for Goを使う上での開発者向けツール

Azure SDK for Go documentation」サイトの「Tools for developers using the Azure SDK for Go」ページを読み進めました。

本セクションでは、Azure SDK for Goを使用する上でGoコードを効率的に記述したりAzureサービスとシームレスに連携したりする開発者向けツールが紹介されています。

ここでは、対象ページの内容をベースとして、私なりの解説を加えてみます。

Azure CLI

AzureにはAPIを操作する方法として、WebブラウザーでログインしてのGUI操作、Azure SDKを使ってのコード操作の他に、Azure CLIによるコマンドライン操作が用意されています。

Azure CLIは az コマンドで操作を行い、Windowsは実行バイナリーで、macOSとLinuxはPython 3スクリプトでインストールされます。またDockerやAzure Cloud shellでも実行可能です。

CLIのメリットは、コマンドライン上で他のコマンドと組み合わせて一連の処理をスクリプト化して実行できる柔軟性の良さが挙げられます。

個人的には、Goのマルチプラットフォーム向けに処理速度の良さも含めた実行バイナリーを提供できるメリットを生かして「Azure CLIこそGoで!」と思うのですが、如何でしょうか?(Google社のパブリッククラウド「Google Cloud Platform」のCLIツールであるgcloudコマンドにも同様に思うところ)

Visual Studio Code

Visual Studio Code(以下、vscode)は、Microsoft社が提供しているオープンソースのコードエディターで、エクステンション(いわゆるプラグイン)をインストールすることで機能拡張でき、オートコンプリート、実装テンプレート、リファクタリング、デバッグなどの機能が追加されます。

Goコードを効率的にコーディングするためのエクステンションとして「ms-vscode.Go」がMicrosoft社員にて開発&サポートされており、デバッガーとして「delve」が対応しています。

2015年11月のvscodeのベータ版の公開時にms-vscode.Goも紹介され、当時はGopherとして「Microsoft社がGoへの取り組みもしっかり行なっている!」と感動しました。

本セクションでは、Azure サービスと連携するためのエクステンションがまとめられた「Azure Extension Pack」も紹介されていますが、

リンク先のエクステンション提供サイト「Marketplace」では非公開(unpublished)になっているため、今後は個々のAzureサービス用のエクステンションをインストールして使用することになります。

Azure DevOps プロジェクトでのCI/CD

Azure上にデプロイしているGoアプリケーションを継続的インティグレーション/継続的デリバリー(CI/CD:Continuous Integration/Continuous Delivery)するには、Azure DevOps Project パイプラインを使用します。これにより、gitリポジトリー上のGoアプリケーションのソースコードを直接Azureにデプロイしてテストすることができます。

depによる依存関係管理

Azure SDK for Goは、パッケージ(ライブラリー)の依存関係管理にdepを使用しています。

depはGoでパッケージ依存管理を行うためのプロジェクトとしてスタートしました。現在のGo本体には、これらの成果を反映した「Go Modules」が実装されています。Go Moludesの採用によりdepの開発も収束しており、今後はAzure SDK for GoもGo Modules化が進むと思われます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Visual Studio Code で編集中のテストコードを実行する (Golang編)

TL;DR

Visual Studio Code で編集中のテストコードを実行する

 この記事にいたく感動したのでGo言語でもショートカット一発で任意の関数をテストしたくなった!

できばえ

  • 開発言語非依存にしたかったのでシェルスクリプトで実装しました
  • 関数名を定義した行に移動するのがめんどいかったのでコードの途中からでも出来るようにしました

pic.png

やりかた

・以下、gotet.shをプロジェクト名フォルダの下に置いてください

gotest.sh
relativeFile=$1
lineNumber=$2
line=0
funcName=""
while :
do
    cursol=`expr $lineNumber - $line`
    if [ "$cursol" = "0" ]; then
        break
    fi

     strs=`sed -n ${cursol}p $relativeFile | grep func | grep testing.T`

    if [ "$strs" != "" ]; then
        funcName=$strs
        break
        fi

        line=`expr $line + 1`
done

if [ "$funcName" != "" ]; then
    funcName=`echo $funcName | cut -d " " -f 2 | cut -d "(" -f 1`
    echo "Testing function: $funcName"
    go test -run $funcName
else
    echo "not find Test function name..."
fi

・元記事の通りプロジェクト名フォルダの下に.vscodeを掘って、以下tasks.jsonを置く

tasks.json
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "run Go test here",
            "type": "shell",
            "command": "sh gotest.sh ${relativeFile} ${lineNumber}",
            "group": "test",
            "presentation": {
                "reveal": "always",
                "panel": "shared"
            }
        }
    ]
}

このあたりを参照にキーバインドを設定してください

tasks.json
[
    {
        "key": "ctrl+shift+a",
        "command": "workbench.action.tasks.runTask",
        "args": "run Go test here"
      }
]

ではでは

Windowsでもcygwinとかインストールすれば問題なく動きます。
Gow軽くて良いっすよ!

さらに

VS CodeのGo言語テストコード生成ツールを使ってみたらめちゃくちゃ便利だった話とか

こちらと組み合わせてテストテンプレートを自動生成するとテスト能率爆上がりなんでオススメです!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ubuntu に apt で golang をインストール

ubuntu に apt で golang をインストール

手順

sudo add-apt-repository -y ppa:longsleep/golang-backports
sudo apt update -y 
sudo apt install -y golang-go

※ -y をつけて自動でインストールできるようにしている。

メモ

ubuntu 19.10 では go 1.14.2 がインストールされた (2020/4/26 現在)
ubuntu 20.04 では go 1.13.8 がインストールされた (2020/4/26 現在)

リンク

https://github.com/golang/go/wiki/Ubuntu
https://launchpad.net/~longsleep/+archive/ubuntu/golang-backports
https://github.com/longsleep/golang-deb
https://go.googlesource.com/go
https://qiita.com/Sylba2050/items/29211a4cf4caa60162c0

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go で競技プログラミングをやるためのツール

Go で競技プログラミングをするとき、綺麗なコードを提出できるツールを作ったので紹介します。

リポジトリはこちら: https://github.com/murosan/gollect

ツールの目的

Go で競技プログラミングする場合、皆さんは自分で実装したライブラリを予め用意していると思います。

では、それらをどのファイルで管理していますか。また提出するときどうしていますか。
全部 main.go に書いてそのまま提出していますか?

それでも基本的に問題はないわけですが、提出したコードを振り返ってみたとき「汚い..」と感じたことはありませんか?
それに、同じファイルに全て書くのはメンテナンスするのが大変です。

そこで作ったのがこのツールです。ざっくり以下のことを行います。

  • golang.org/x/tools に AST を解析してもらう
  • 必要な宣言だけ抽出する
  • 一つのファイルにまとめる
  • gofmt をかける
  • 出力する

おまけで github.com/atotto/clipboard を使ってクリップボードに提出用コードをコピーする機能も付いてます。
また、宣言は別のパッケージに書いてあっても問題ありません。

これでライブラリの管理が楽になりますし、提出するコードも綺麗になりますね。

使い方

インストールします。

go get -u github.com/murosan/gollect/cmd/gollect

設定を YAML で書きます。

config.yml
# main パッケージのファイルパス
# 一応 glob でも指定可能です。 e.g, ./*.go
inputFile: ./main.go

# 出力先のリスト
# 標準出力、クリップボード、ファイルパスを指定できます。
outputPaths:
  - stdout
  - clipboard
  - ./out/main.go

ついでに Makefile に追加しておくと便利です。

Makefile
# mac の人は pbpaste | go run main.go としておくと便利
r:
    go run main.go

g:
    gollect -config ./config.yml

あとは通常通りコードを書いて実行します。

# テストする
make r
# 提出するコードを生成する
make g

例えばこのコードは

lib/scanner.go
type Scanner struct{ *bufio.Scanner }

func NewScanner(r io.Reader) *Scanner {
    s := bufio.NewScanner(r)
    s.Split(bufio.ScanWords)
    return &Scanner{Scanner: s}
}

func (s *Scanner) Bytes() []byte { s.Scan(); return s.Scanner.Bytes() }
func (s *Scanner) Text() string  { s.Scan(); return s.Scanner.Text() }
main.go
package main

import (
    "fmt"
    "os"

    "github.com/owner/repo/lib"
)

func main() {
    var n int
    fmt.Scan(&n)

    m := make(map[string]interface{})
    sc := lib.NewScanner(os.Stdin)

    for i := 0; i < n; i++ {
        key := string(sc.Bytes())
        m[key] = struct{}{}
    }
    fmt.Println(len(m))
}

こんな感じで出力されます

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {
    var n int
    fmt.Scan(&n)

    m := make(map[string]interface{})
    sc := NewScanner(os.Stdin)

    for i := 0; i < n; i++ {
        key := string(sc.Bytes())
        m[key] = struct{}{}
    }
    fmt.Println(len(m))
}

type Scanner struct{ *bufio.Scanner }

func NewScanner(r io.Reader) *Scanner {
    s := bufio.NewScanner(r)
    s.Split(bufio.ScanWords)
    return &Scanner{Scanner: s}
}

func (s *Scanner) Bytes() []byte { s.Scan(); return s.Scanner.Bytes() }

func (s *Scanner) Text() は使わなかったため消えました。
また、lib.NewScannerNewScanner になり、Go のビルトインパッケージのみがインポートされています。

注意点

  • コンパイルが通らないコードには使えません
  • 途中で panic を起こすコードにも使えないかもしれません
  • 宣言のリネームはしません
    • コードを一つにまとめた時に名前衝突が起こらないようにしてください
  • _(アンダースコア)で宣言されたパッケージレベルの変数は消えます
  • interface を満たすために宣言されているが、直接は使われてないメソッドは何もしないと消えます
    • Sort で使用する Less などは注意
    • コメントに // gollect: keep methods を追加することで回避できます
    • このコメントは提出時には消えます
    • README.md

最後に

Go で競技プログラミングするのが少しでも楽になればいいなと思います。

おまけ

このままだとただの宣伝になってしまうので、デバッグ中に新しく知った Go の書き方などを適当に書きます。

type A struct{}
func (A) m()  {}

var a A
A.m(a)

こんな書き方できるんですね。やめてほしいです。

  • interface を埋め込める
type T struct{ I }
type I interface {
    a()
    b()
}

var t T
t.a() // this causes panic.

func (T) a() が実装されていませんが、コンパイルできます。
ただし実行すると、panic を起こすようです。

  • 複数の変数を同時に宣言するときは改行できる
a,
b,
c := 1, 2, 3

知らなかったです。

  • 色々な変数宣言方法
const (
    zero = iota // 0
    one         // 1
    two         // 2
)

const (
    numA = 100 // 100
    numB       // 100
    numC       // 100
    numD = 200 // 200
    numE       // 200
)

const (
    a = 10     // 10
    b = iota   // 1 (0 ではない。a も残す必要あり)
    c          // 2
    d = "d"    // d
    e          // d
)

const (
    f, g = iota, iota // 0, 0
    h, i              // 1, 1
)

const (
    _ = iota // 0
    j        // 1
    _        // 2
    l        // 3
)

type A int

const (
    _ A = iota // A(0)
    m          // A(1)
    n          // A(2)
)

gollect では、iota が含まれる場合、()内いずれかの宣言が使われていたらすべて残す。
iota が含まれない場合、使ってない宣言は消す。
というルールで実装しました。_(アンダースコア)の宣言も iota があれば残します。


一応 The Go Programming Language Specification を読んで確認したので、だいたいカバーできていると思うのですが、なにか不具合等見つけたら issue をください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GraphQLのType別gqlgen自動生成結果一覧

お題

表題の通り。備忘録として。

開発環境

# OS - Linux(Ubuntu)

$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.4 LTS (Bionic Beaver)"

# 言語 - Golang

$ go version
go version go1.13.9 linux/amd64

# ツール - gqlgen

$ gqlgen version
v0.11.3-dev

結果

今回のソースは下記。
https://github.com/sky0621/study-gqlgen/tree/v0.1/types

Null"不可"のケース

型名 GraphQLスキーマ上の定義 gqlgen自動生成後の型 備考
ID型 ID! string Uniqueであることが前提
UTF8文字列型 String! string -
符号付き32bit整数型 Int! int -
符号付き倍精度浮動小数点数型 Float! float64 -
真偽値型 Boolean! bool -
日付型 Date! string Date型で定義しても自動的に言語依存の日付型にはマッピングされない
列挙型 Signal! Signal enumはGolangではstring型の定数となる
時刻型(gqlgenビルトイン) Time! time.Time -
マップ型(gqlgenビルトイン) Map! map[string]interface{} GraphQLスキーマ上はNull"不可"の定義だが、map なので nil が入りうる
アップロード型(gqlgenビルトイン) Upload! graphql.Upload -
なんでもあり型(gqlgenビルトイン) Any! interface{} GraphQLスキーマ上はNull"不可"の定義だが、interface なので nil が入りうる
カスタムScalar型 YesNo! YesNo 事前定義した型にマッピングされる
Object構造型 SomeType! *Object GraphQLスキーマ上はNull"不可"の定義だが、参照型で自動生成されるので nil が入りうる
文字列型配列 [String!]! []string GraphQLスキーマ上はNull"不可"の定義だが、Slice なので nil が入りうる
マップ型配列 [Map!]! []map[string]interface{} GraphQLスキーマ上はNull"不可"の定義だが、Slice なので nil が入りうる
内部の要素も map なので nil が入りうる
なんでもあり型配列 [Any!]! []interface{} GraphQLスキーマ上はNull"不可"の定義だが、Slice なので nil が入りうる
内部の要素も interface なので nil が入りうる
Object構造型配列 [SomeType!]! []*Object GraphQLスキーマ上はNull"不可"の定義だが、Slice なので nil が入りうる
内部の要素も参照型なので nil が入りうる

Null"可"のケース

型名 GraphQLスキーマ上の定義 gqlgen自動生成後の型 備考
UTF8文字列型 String *string -
符号付き32bit整数型 Int *int -
符号付き倍精度浮動小数点数型 Float *float64 -
真偽値型 Boolean *bool -
日付型 Date *string Date型で定義しても自動的に言語依存の日付型にはマッピングされない
列挙型 Signal *Signal enumはGolangではstring型の定数となる
時刻型(gqlgenビルトイン) Time *time.Time -
マップ型(gqlgenビルトイン) Map map[string]interface{} -
アップロード型(gqlgenビルトイン) Upload *graphql.Upload -
なんでもあり型(gqlgenビルトイン) Any interface{} -
カスタムScalar型 YesNo *YesNo 事前定義した型にマッピングされる
Object構造型 SomeType *Object -
文字列型配列 [String] []*string -
文字列型配列 [String]! []*string -
文字列型配列 [String!] []string -
マップ型配列 [Map] []map[string]interface{} -
マップ型配列 [Map]! []map[string]interface{} -
マップ型配列 [Map!] []map[string]interface{} -
なんでもあり型配列 [Any] []interface{} -
なんでもあり型配列 [Any]! []interface{} -
なんでもあり型配列 [Any!] []interface{} -
Object構造型配列 [SomeType] []*Object -
Object構造型配列 [SomeType]! []*Object -
Object構造型配列 [SomeType!] []*Object -

補足

GraphQLスキーマ

types/graph/schema.graphqls
# Global Object Identification ... 全データを共通のIDでユニーク化
interface Node {
  id: ID!
}

schema {
  query: Query
  mutation: Mutation
}

type Query {
  node(id: ID!): Node
}

type Mutation {
  noop(input: NoopInput): NoopPayload
}

input NoopInput {
  clientMutationId: String
}

type NoopPayload {
  clientMutationId: String
}

type MutationResponse {
  id: ID
}

scalar Date
scalar Time
scalar Map
scalar Upload
scalar Any
scalar YesNo

enum Signal {
  RED
  YELLOW
  GREEN
}
types/graph/various.graphqls

extend type Query {
    nonNullTypes: [NonNullType!]!
    nullableTypes: [NullableType!]!
}

"""
https://graphql.org/learn/schema/#scalar-types
https://github.com/99designs/gqlgen/blob/master/docs/content/reference/scalars.md
"""
type NonNullType implements Node {
    "ID型 - ID!"
    id: ID!

    "UTF8文字列型 - String!"
    stringNonNull: String!

    "符号付き32bit整数型 - Int!"
    intNonNull: Int!

    "符号付き倍精度浮動小数点数型 - Float!"
    floatNonNull: Float!

    "真偽値型 - Boolean!"
    booleanNonNull: Boolean!

    "日付型 - Date!"
    dateNonNull: Date!

    "列挙型 - Signal!"
    signalNonNull: Signal!

    "時刻型(gqlgenビルトイン) - Time!"
    timeNonNull: Time!

    "マップ型(gqlgenビルトイン) - Map!"
    mapNonNull: Map!

    "アップロード型(gqlgenビルトイン) - Upload!"
    uploadNonNull: Upload!

    "なんでもあり型(gqlgenビルトイン) - Any!"
    anyNonNull: Any!

    "カスタムScalar型 - YesNo!"
    yesNoNonNull: YesNo!

    "Object構造型 - SomeType!"
    objectNonNull: Object!

    "文字列型配列 - [String!]!"
    stringsNonNull: [String!]!

    "マップ型配列 - [Map!]!"
    mapsNonNull: [Map!]!

    "なんでもあり型配列 - [Any!]!"
    anyTypesNonNull: [Any!]!

    "Object構造型配列 - [SomeType!]!"
    objectsNonNull: [Object!]!
}

type NullableType implements Node {
    "ID型 - ID!"
    id: ID!

    "UTF8文字列型 - String"
    stringNullable: String

    "符号付き32bit整数型 - Int"
    intNullable: Int

    "符号付き倍精度浮動小数点数型 - Float"
    floatNullable: Float

    "真偽値型 - Boolean"
    booleanNullable: Boolean

    "日付型 - Date"
    dateNullable: Date

    "列挙型 - Signal"
    signalNullable: Signal

    "時刻型(gqlgenビルトイン) - Time"
    timeNullable: Time

    "マップ型(gqlgenビルトイン) - Map"
    mapNullable: Map

    "アップロード型(gqlgenビルトイン) - Upload"
    uploadNullable: Upload

    "なんでもあり型(gqlgenビルトイン) - Any"
    anyNullable: Any

    "カスタムScalar型 - YesNo"
    yesNoNullable: YesNo

    "Object構造型 - SomeType"
    objectNullable: Object

    "文字列型配列 - [String]"
    stringsNullable: [String]
    "文字列型配列 - [String]!"
    stringsObjectNullable: [String]!
    "文字列型配列 - [String!]"
    stringsArrayNullable: [String!]

    "マップ型配列 - [Map]"
    mapsNullable: [Map]
    "マップ型配列 - [Map]!"
    mapsObjectNullable: [Map]!
    "マップ型配列 - [Map!]"
    mapsArrayNullable: [Map!]

    "なんでもあり型配列 - [Any]"
    anyTypesNullable: [Any]
    "なんでもあり型配列 - [Any]!"
    anyTypesObjectNullable: [Any]!
    "なんでもあり型配列 - [Any!]"
    anyTypesArrayNullable: [Any!]

    "Object構造型配列 - [SomeType]"
    objectsNullable: [Object]
    "Object構造型配列 - [SomeType]!"
    objectsObjectNullable: [Object]!
    "Object構造型配列 - [SomeType!]"
    objectsArrayNullable: [Object!]
}

type Object implements Node {
    id: ID!

自動生成モデル

types/graph/model/models_gen.go
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.

package model

import (
    "fmt"
    "io"
    "strconv"
    "time"

    "github.com/99designs/gqlgen/graphql"
)

type Node interface {
    IsNode()
}

type MutationResponse struct {
    ID *string `json:"id"`
}

// https://graphql.org/learn/schema/#scalar-types
// https://github.com/99designs/gqlgen/blob/master/docs/content/reference/scalars.md
type NonNullType struct {
    // ID型 - ID!
    ID string `json:"id"`
    // UTF8文字列型 - String!
    StringNonNull string `json:"stringNonNull"`
    // 符号付き32bit整数型 - Int!
    IntNonNull int `json:"intNonNull"`
    // 符号付き倍精度浮動小数点数型 - Float!
    FloatNonNull float64 `json:"floatNonNull"`
    // 真偽値型 - Boolean!
    BooleanNonNull bool `json:"booleanNonNull"`
    // 日付型 - Date!
    DateNonNull string `json:"dateNonNull"`
    // 列挙型 - Signal!
    SignalNonNull Signal `json:"signalNonNull"`
    // 時刻型(gqlgenビルトイン) - Time!
    TimeNonNull time.Time `json:"timeNonNull"`
    // マップ型(gqlgenビルトイン) - Map!
    MapNonNull map[string]interface{} `json:"mapNonNull"`
    // アップロード型(gqlgenビルトイン) - Upload!
    UploadNonNull graphql.Upload `json:"uploadNonNull"`
    // なんでもあり型(gqlgenビルトイン) - Any!
    AnyNonNull interface{} `json:"anyNonNull"`
    // カスタムScalar型 - YesNo!
    YesNoNonNull YesNo `json:"yesNoNonNull"`
    // Object構造型 - SomeType!
    ObjectNonNull *Object `json:"objectNonNull"`
    // 文字列型配列 - [String!]!
    StringsNonNull []string `json:"stringsNonNull"`
    // マップ型配列 - [Map!]!
    MapsNonNull []map[string]interface{} `json:"mapsNonNull"`
    // なんでもあり型配列 - [Any!]!
    AnyTypesNonNull []interface{} `json:"anyTypesNonNull"`
    // Object構造型配列 - [SomeType!]!
    ObjectsNonNull []*Object `json:"objectsNonNull"`
}

func (NonNullType) IsNode() {}

type NoopInput struct {
    ClientMutationID *string `json:"clientMutationId"`
}

type NoopPayload struct {
    ClientMutationID *string `json:"clientMutationId"`
}

type NullableType struct {
    // ID型 - ID!
    ID string `json:"id"`
    // UTF8文字列型 - String
    StringNullable *string `json:"stringNullable"`
    // 符号付き32bit整数型 - Int
    IntNullable *int `json:"intNullable"`
    // 符号付き倍精度浮動小数点数型 - Float
    FloatNullable *float64 `json:"floatNullable"`
    // 真偽値型 - Boolean
    BooleanNullable *bool `json:"booleanNullable"`
    // 日付型 - Date
    DateNullable *string `json:"dateNullable"`
    // 列挙型 - Signal
    SignalNullable *Signal `json:"signalNullable"`
    // 時刻型(gqlgenビルトイン) - Time
    TimeNullable *time.Time `json:"timeNullable"`
    // マップ型(gqlgenビルトイン) - Map
    MapNullable map[string]interface{} `json:"mapNullable"`
    // アップロード型(gqlgenビルトイン) - Upload
    UploadNullable *graphql.Upload `json:"uploadNullable"`
    // なんでもあり型(gqlgenビルトイン) - Any
    AnyNullable interface{} `json:"anyNullable"`
    // カスタムScalar型 - YesNo
    YesNoNullable *YesNo `json:"yesNoNullable"`
    // Object構造型 - SomeType
    ObjectNullable *Object `json:"objectNullable"`
    // 文字列型配列 - [String]
    StringsNullable []*string `json:"stringsNullable"`
    // 文字列型配列 - [String]!
    StringsObjectNullable []*string `json:"stringsObjectNullable"`
    // 文字列型配列 - [String!]
    StringsArrayNullable []string `json:"stringsArrayNullable"`
    // マップ型配列 - [Map]
    MapsNullable []map[string]interface{} `json:"mapsNullable"`
    // マップ型配列 - [Map]!
    MapsObjectNullable []map[string]interface{} `json:"mapsObjectNullable"`
    // マップ型配列 - [Map!]
    MapsArrayNullable []map[string]interface{} `json:"mapsArrayNullable"`
    // なんでもあり型配列 - [Any]
    AnyTypesNullable []interface{} `json:"anyTypesNullable"`
    // なんでもあり型配列 - [Any]!
    AnyTypesObjectNullable []interface{} `json:"anyTypesObjectNullable"`
    // なんでもあり型配列 - [Any!]
    AnyTypesArrayNullable []interface{} `json:"anyTypesArrayNullable"`
    // Object構造型配列 - [SomeType]
    ObjectsNullable []*Object `json:"objectsNullable"`
    // Object構造型配列 - [SomeType]!
    ObjectsObjectNullable []*Object `json:"objectsObjectNullable"`
    // Object構造型配列 - [SomeType!]
    ObjectsArrayNullable []*Object `json:"objectsArrayNullable"`
}

func (NullableType) IsNode() {}

type Object struct {
    ID string `json:"id"`
}

func (Object) IsNode() {}

type Signal string

const (
    SignalRed    Signal = "RED"
    SignalYellow Signal = "YELLOW"
    SignalGreen  Signal = "GREEN"
)

var AllSignal = []Signal{
    SignalRed,
    SignalYellow,
    SignalGreen,
}

func (e Signal) IsValid() bool {
    switch e {
    case SignalRed, SignalYellow, SignalGreen:
        return true
    }
    return false
}

func (e Signal) String() string {
    return string(e)
}

func (e *Signal) UnmarshalGQL(v interface{}) error {
    str, ok := v.(string)
    if !ok {
        return fmt.Errorf("enums must be strings")
    }

    *e = Signal(str)
    if !e.IsValid() {
        return fmt.Errorf("%s is not a valid Signal", str)
    }
    return nil
}

func (e Signal) MarshalGQL(w io.Writer) {
    fmt.Fprint(w, strconv.Quote(e.String()))
}

カスタムScalar:「YesNo」型

types/graph/model/custom_scalar.go
package model

import (
    "fmt"
    "io"
)

type YesNo bool

// UnmarshalGQL implements the graphql.Unmarshaler interface
func (y *YesNo) UnmarshalGQL(v interface{}) error {
    yes, ok := v.(string)
    if !ok {
        return fmt.Errorf("points must be strings")
    }

    if yes == "yes" {
        *y = true
    } else {
        *y = false
    }
    return nil
}

// MarshalGQL implements the graphql.Marshaler interface
func (y YesNo) MarshalGQL(w io.Writer) {
    if y {
        if _, err := w.Write([]byte(`"yes"`)); err != nil {
            fmt.Println(err)
        }
    } else {
        if _, err := w.Write([]byte(`"no"`)); err != nil {
            fmt.Println(err)
        }
    }
}

gqlgen.yml

types/gqlgen.yml
# Where are all the schema files located? globs are supported eg  src/**/*.graphqls
schema:
  - graph/*.graphqls

  〜〜省略〜〜

models:
  ID:
    model:
      - github.com/99designs/gqlgen/graphql.ID
      - github.com/99designs/gqlgen/graphql.Int
      - github.com/99designs/gqlgen/graphql.Int64
      - github.com/99designs/gqlgen/graphql.Int32
  Int:
    model:
      - github.com/99designs/gqlgen/graphql.Int
      - github.com/99designs/gqlgen/graphql.Int64
      - github.com/99designs/gqlgen/graphql.Int32
  YesNo:
    model:
      - github.com/sky0621/study-gqlgen/types/graph/model.YesNo
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む