oinume journal

Scratchpad of what I learned

Macで特定のポートを使用しているプロセスを特定する

ターミナルで

$ lsof -i :3000
COMMAND  PID   USER   FD   TYPE            DEVICE SIZE/OFF NODE NAME
go   2087 a11920  269u  IPv6 0x7574d6a56e437f1      0t0  TCP *:hbci (LISTEN)

って打つと3000番ポートを使ってるプロセスが調べられる。

Goで標準出力をキャプチャする

こんな感じかなぁ。http://play.golang.org/p/2DFtYAsFO9

package main

import (
    "bytes"
    "fmt"
    "io"
    "os"
)

type Capturer struct {
    saved         *os.File
    bufferChannel chan string
    out           *os.File
    in            *os.File
}

// 標準出力をキャプチャする
func (c *Capturer) StartCapturingStdout() {
    c.saved = os.Stdout
    var err error
    c.in, c.out, err = os.Pipe()
    if err != nil {
        panic(err)
    }

    os.Stdout = c.out
    c.bufferChannel = make(chan string)
    go func() {
        var b bytes.Buffer
        io.Copy(&b, c.in)
        c.bufferChannel <- b.String()
    }()
}

// キャプチャを停止する
func (c *Capturer) StopCapturingStdout() string {
    c.out.Close()
    os.Stdout = c.saved
    return <-c.bufferChannel
}

func main() {
    c := &Capturer{}
    c.StartCapturingStdout()
    fmt.Println("hello")

    captured := c.StopCapturingStdout()
    fmt.Println("captured:", captured)
}

GoLangでJavaのenumっぽいライブラリ作った話

はじめに

Goでは言語標準でenumという機構がなくてつらかったのでそれっぽいものを作ったよという話。

Goではiotaを使って以下のようにして定数を列挙することはできる。

const (
    Go int = iota + 1
    Python
    Ruby
)

ただし、このやり方だとJavaのenumのように

  1. 値から名前(↑でいう"Java"という定数名)を取得したり、名前からその値を引く
  2. 全ての値を列挙する
  3. 全ての名前を列挙する

というようなことができない。下記のように値→名前を取得する関数を定義することはできるので1.はなんとかなるわけなんだけど、これを毎回書くのは面倒なので goenum というライブラリを作ってみたよという話(ここからが本題)。

http://play.golang.org/p/5nrPOMEQKZ

package main

import (
    "fmt"
)

type Lang int

func (l Lang) String() string {
    switch l {
    case Go:
        return "Go"
    case Python:
        return "Python"
    case Ruby:
        return "Ruby"
    }
    panic("Unknown value")
}

const (
    Go Lang = iota + 1
    Python
    Ruby
)

func main() {
    fmt.Printf("%s: %d\n", Go.String(), Go)
    fmt.Printf("%s: %d\n", Python.String(), Python)
    fmt.Printf("%s: %d\n", Ruby.String(), Ruby)
}

goenum

goenumでは列挙する値はconstではなくstructを使って定義する。

type LangsEnum struct {
    Go     int
    Python int
    Ruby   int
    Haskel int
}

でで、その構造体(↑でいうLangsEnum)に下記のようなEnum()というメソッドを定義しておき、goenum.EnumerateStruct という関数にLangsEnumの実体を渡す。goenum.EnumerateStruct() という関数は goenum.Enum を返すんだけど、これが

  • 値→名前
  • 名前→値

の変換を行ってくれる。

var Langs LangsEnum = LangsEnum{1, 2, 3, 4}

func (e LangsEnum) Enum() goenum.Enum {
    // Make Enum from Langs
    return goenum.EnumerateStruct(&Langs)
}

以下は実際に使ってみた例。

func main() {
    var langs goenum.Enum = Langs.Enum()

    // 名前の列挙
    fmt.Println(langs.Names())
    // --> [Go Python Ruby Haskel]

    // 値の列挙
    fmt.Println(langs.Values())
    // --> [1 2 3 4]

    // 値 -> 名前の取得
    fmt.Println(langs.MustName(1))
    // --> Go

    // 名前 -> 値の取得
    fmt.Println(langs.MustValue("Python"))
    // --> 2
}

本当は構造体で元の列挙値を定義するやり方にはしたくなかったんだけど、自分のGo力が低すぎるのでこういう仕上がりになっているので、もっと良いやり方があったらぜひ教えて欲しいところです。

追記

Big Sky :: Re: GoLangでJavaのenumっぽいライブラリ作った話でstringerというコマンドを使う方法を教えてもらいました。1.4では go generate コマンドもあるし、これ使ってgenerateするのがIdiomatic wayのような気がしますね。mattnさんありがとうございます!

Recent open source activities

pmd

https://github.com/pmd/pmd/pull/44

CPD (Copy Paste Detector) supported GoLang.

go-xorm

https://github.com/go-xorm/xorm/issues/166

Suggested and implemented "soft delete" for xorm. My pull-request was immediately merged! (very impressive)

goose

https://bitbucket.org/liamstask/goose/pull-request/43/support-mysql-driver

Added mysql driver support. My pull-request hasn't been merged yet. I contacted to the author but he didn't respond.