emahiro/b.log

勉強記録と書評とたまに長めの呟きを書きます

structのomitemptyの挙動と使い所の検討

golangのstructでjsonのencodingのためのpropertyに omitempty をつけた時の挙動とその使いどこを検討します。

omitemptyタグとは

https://golang.org/pkg/encoding/json/#Marshal には以下の用に記載されている。

The "omitempty" option specifies that the field should be omitted from the encoding if the field has an emptyvalue, 
defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string. 

omitempty属性をつけたstructのjsonのpropertyははその値がfalse, 0, nilのpointer,長さが0の文字列と配列、sliceの時はjsonにencodeされるときに、propertyごと省略される

type Person struct {
  Name string `json:"name"`
  Age  int64 `json:"age,omitempty"` //省略可能
}

上記hのようなstructを考える。

jon := Person {
  Name: "Jon",
  Age: 20
}

// jsonにencodeしたときのoutput
/*
{
  "name": "jon",
  "age" : 20
}
*/

taro := Person {
  Name: "taro",
}

// jsonにencodeしたときのoutput
/*
{
  "name": "taro"
}
*/

jsonにencodeしたときに omitempty 属性をつけておくとstructのinstance化のときに指定したなかったkeyについてはencodeしたjson文字列のpropertyとして含めない(省略される)。
ここまでは一般的なgoのencoding/jsonに関する知識。

ではこのomitemptyを実際のプロジェクトに使うときの注意点について検討する

omitemptyの使い所の検討

使い所はどこなのか。

omitempty 属性を使ういいところは必要ないstructのkeyをjsonにencodeしたときに自動で省略してくれるところであるので、例えば特定のリクエストのクエリパラメーターの値からjsonを生成して、別のAPIにPOSTするような動作を考えたときに、パラメータの有無によって生成するstructを返るなどの手間がいらなくなると思います。

具体的には以下のようなtokenと送信元をクエリパラメーターにタグとしてくっつけて、POSTした先でtokenと送信元をロギングするような挙動があったとします。

curl -i https:sample.com/user/1?token=hogehoge&from=yahoo.co.jp

こんなURLを叩くと以下のようなhandlerにリクエストが入ってくるとします。

package handler

type Logging struct {
  Token string `json: "token"`
  From  string `json: "from, omitempty"`
}

func SenderLogging(w http.ResponseWriter, r *http.Request) {
  values := r.URL.Query()
  token := values.Get("token")
  from := values.Get("from")
  
  // tokenもfromもクエリパラメーターがなければ空文字が入る。
  logging := Logging{
    Token: token,
    From:  from,
  }
  
  b, _ := json.Marshal(&logging)
  
  // encodeされたjsonのbyte配列になる。
}

このとき from が指定されたリクエストの時は from に値が入ったjsonが生成され、from が指定されない(直叩きされた)場合は from のpropertyが省略され、ログにもfrom要素は記録されません。 from の有無を見て、条件分岐等はする必要がありません。

omitemptyをうまく使うと、そこで差分を吸収してくれます。

懸念点

omitemptyの懸念点は上記にも記載しましたが false or "" or 0 or nil pointer などそれ自体が意味を持っていそうな値の場合も omiempty 属性が付いていることでjson化したときにkeyは省略されてしまいます。
つまり、ともすると omitempty 属性をつけていることで意図するjsonが生成されないことが想定されます。

もし、認証のプロセスで必須のパラメータに omitempty をつけていたら、何かの問題で空になってしまい、想定している正しいjsonが作られず、認証が通らない、ということが起きるかもしれません。

特に false or 0 のようなそれ自体が意味を持ちそうな値の時ですら省略されてしまうのは気をつけないと、生成されるつもりだったということが起きかねないので注意が必要です。

まとめ

omitemptyは便利な属性だけど、使い方を見誤ると想定したjsonを生成しないということになるので注意が必要です。
ただし、使い勝手のいい機能なので、ちゃんと使っていきたい。

structに埋め込んだmapの要素を上書きする

やりたいこと

struct内に埋め込んだmapのfieldの値を上書きするという処理を考えます。
下記のようなサンプルコードがあったときに originalSamples のItemsの中身を上書きするような処理です。

type Samples struct {
  Items map[string]string
}

originalSamples := Samples{
  Items: map[string]string{
    "first": "apple",
    "second": "orange",
  },
}

やることはmapの中身を更新するだけなのですが、いくつかハマったのでメモとして残して起きます。

ハマったところ

assignment to entry in nil map が発生した

mapを初期化しなかったケースです。
mapを初期化せずに新規の値をappendしようとすると panic: assignment to entry in nil map のエラーが発生します。

var m Samples

for k, item := range originalSamples.Items{
  item = "iPhone"
  m.Items[k] = item
}

この場合は var で定義するのではなく Sample{} で初期化することが必要になります。

otiai10.hatenablog.com

structを初期化してもassinment to entry in nil map が起きた

structを初期化しても同様のエラーが発生しました。
具体的なコードは以下です。

originalSamples := Samples{
  Items: map[string]string{
    "first":  "pc",
    "second": "book",
  },
}

m := Samples{}

for k, item := range originalSamples.Items {
  item = "iPhone"
  m.Items[k] = item
}

これはなぜ起きるかというと、struct内に埋め込まれているmapはstructをインスタンス化してもnilでになってしまうために assignment to entry in nil map のエラーが発生します。

こちらを回避策を検討した結果、mapをnilしないようにするので下記の2パターンが考えられると思います。

  1. mapをinstace化して後で上書きしたいstructに代入する
  2. structのinstance化と合わせて埋め込まれているmapもinstance化する
// mapをinstance化する
newSamples := Samples{}
m := make(map[string]string)

for k, item := range originalSamples.Items {
  item = "iPhone"
  m[k] = item
}

// structに代入する
newSamples.Items = m
fmt.Printf("%v", newSamples)
// structのinstance化のタイミングでmapも初期化する
newSamples := Samples{
  Items: map[string]string{},
}

for k, item := range originalSamples.Items {
  item = "iPhone"
  newSamples.Items[k] = item
}
fmt.Printf("%v", newSamples)

まとめ

mapを動的に扱いたい場合は、ちゃんと初期化されているか確認する。
structを初期化しても中のpropertyまで初期化されているわけではない。

goで任意引数でtemplateでの表示を変えたりしたい場合の考察

golangで関数に任意引数を取り、その任意引数にmapを指定することで、template場でmapで定義したinterfaceを見て表示を変えたいという実装を考えます。

やりたいこと

以下のようなことをしたいと想定します。

func RendarHTML (flags ...map["string"]interface{}){
  // flags に各種表示を分けるstatusを入れてtemplate側で制御する
  
}

golangにおける任意引数とは?

golangでは、LL言語はじめとしたオブジェクト指向言語にあるようなデフォルト引数という機能を使うことができません。
rubyとかだと

def renderHtml(templ, flag=false)
  # flagを使って何かする
end

# callする側でflagを使わないとき
renderHtml("index.html")

# flagの値を使うとき
renderHtml("detail.html", true)

上記のようにmethodに対してdefatult引数を指定して、その引数はcallする側で指定されなければdefatult引数に与えられた値がmethod内部で使用されるということが可能です。

しかし、golangではこのdefault引数を取ることはできません。
また、静的言語のため、指定した引数は必ず呼び出し側で関数にsetすることが求められます。
setしないとコンパイルが通りません。

// flagでfalseをデフォルトで指定するようなことはできない
func renderHTML(tmpl string, flag bool){ 
  // 何かしらの処理
}

// 呼び出す側

// ◯ コンパイルが通るとき
renderHTML("index.tmpl", false)

// × コンパイルが通らない
renderHTML("detail.tmpl")

// 関数に指定されている引数は満たさなければならない。

ここでgolangで使うのが 任意引数。 言葉通り、呼び出す側でsetしてもいいし、しなくてもいい任意の引数をgolangでは関数に使うことができます。
可変引数が正しい呼称らしいですね

qiita.com

任意引数を使った実装パターンの検討

まず任意引数の仕様として 任意引数をとる場合は必ずsliceとして扱う というのがあります。

func renderHTML(tmpl string, flags ...bool){
    // flagsはslice型。この場合では []book となっている。
}

// 引数をとる
renderHTML("index.tmpl", true)

// 引数をとらない
renderHTML("detail.tmpl")

// どちらでもコンパイルは正常に通ります。

しかし、引数一つでも任意引数をとっている関数内ではsliceから特定の値を取り出さないと一番最初に意図していたようなflagによってtemplateの表示を変更するような実装はできないです。 上記の例では []bool の中からお目当のstatusを持っているかを判別するような実装をすることはしません。

ここで実際に具体的な実装方法を検討します。

任意引数は関数内ではsliceとして扱われるので、flagsという直接boolを扱うことを連想させるような引数をやめて、mapをとります。
このとき関数内の変数名は、追加分なので extra のような変数名にしておくと可読性が高くなると思います。

sample実装

具体的な実装方法として、以下のようなmethodを最初考えます。

fumc renderHTML (tmpl string, extra ...map["string"]interface{}){
  // extraを使って描画する
  // extras は []map(string)interface{}
  
  RenderPage(ctx, "templete/path.tmpl",{"Extra":extra})
}

// こんな感じの引数にする
renderHTML("index.tmpl", map["string"]interface{}{"isIOS": true, "isAndroid": false})

// 引数をとらないことも可能
renderHTML("detail.tmpl")

※ 関数名は適当。

renderHTML内の引数に任意引数を使うことで、メソッドの汎用性が高くなります。
何か余分なデータや追加でデータを持たせないときはextrasに全て突っ込んで仕舞えば問題ないです。

これをtemplate側で使うにはいくつか方法があります。

  • templateでsliceのelementを扱う関数を使う
    • range
    • index
<!-- rangeで一つずつ取り出してloopを回す -->
{{ $ext := range $.Extra }}
{{ end }}

<!-- 到底のindex位置の取得する -->
{{ $ext := index $.Extra 3 }}

しかし、extraがそのまま渡されてきてtemplate側で条件分岐を使ったり、indexを制御したりするのはあまり筋がいいとは言えません。
何より、Extraに紐づく値がなければtemplateのコンパイルエラーが起きる場合もあります。

よりベターな実装方法の検討

任意のsliceで返されるextra引数をそのままとるのではなく、ルールを決めます。

  • 任意引数でとる値は必ず一つ
  • 関数側でslice型になっても、slice型のindex:0が必ずflagsをとることが決まっていれば問題ありません。
  • privateなinterfal関数を用意する

以下のような実装になります。

func renderHTML(tmpl string, extra ...map[string]interface{}){
    ext := extra[0] //先頭に来ることが確定済み
    internalRenderHTML("index.tmpl", ext) 
}

func internalRenderHTML(tmpl string, extra map[string]interface{}){
    // extraを使ってstatusを取り出す
}

任意引数のindex0番が使いたいflagなにで以下のような風に呼び出し側で使います。

renderHTML("index.tmpl", map["string"]interface{}{"IsIOS": true, "IsAndroid": false})

html側で利用するときはindexやreangeの関数を使わずとも、extraはslice型なくmapで受け取れるようにします。

{{ /* iOSモードもとき */ }}
{{ if $.Extra.IsIOS }}
{{ end }}

{{ /* Androidモードのとき */ }}
{{ if $.Extra.IsAndroid }}
{{ end }}

こういった感じでinternalで共通のmethodを呼ぶことで環境ごとにtemplateで使えるようにします。変なelement管理等は必要ないです。

任意引数でslice型をそのまま使うより単体として扱った方が使い勝手がいいですし、templateでもすぐにカンマ区切りで呼び出せるので便利だと思いました。

goのテストのカバレッジを計測する

関わっているプロダクトでテストのカバレッジ取得してみようと思ったので、標準で動作している機能を使ってgoのテストカバレッジを計測してみました。

coverageを計測する

coverageを測定するpackageを指定して、カバレッジを図るオプション -cover を指定します。

$ gotest -cover ./path/to/package_name

## test 実行中

coverage: 45.9% of statements

coverageのパーセンテージが出力されます。
goではpackage単位でカバレッジが計測されます。

テストされている箇所とされてない箇所を視認する

htmlを生成してブラウザで開く

以下の記事を参考にcover.htmlを生成してブラウザで表示させてみました。

qiita.com

GPPATH配下にプロジェクトがある場合は任意のディレクトリにて以下を叩きます。

$ go test -coverprofile=cover.out ./path/to/package
ok      ./path/to/package 0.12s  
coverage: 75.0% of statements
$ go tool cover -html=cover.out -o cover.html
$ open cover.html

ブラウザにテストされている箇所とされていない箇所が表示されます。
されている箇所は緑色で、されてない箇所は赤でコードがハイライトされているので、条件分岐箇所などをみながらどこがテストされていて、どこがテストされていないのかを確認することが可能です。

こういうツールが標準でついてるんだから本当に便利だなと思います。

テスト戦略にも関わってくるので、例えばキャッシュの有無など、動作に影響ないところのコード等はある程度テストを書かずともいいですが、ビジネスロジックに関わるところはちゃんとテストされているかを確認してみてください。

ハマったところ

direnvでプロジェクトごとにGOPATHを指定している場合に少しハマりました。 カバレッジを計測したhtmlファイルをプロジェクトとは別ディレクトリでgo tool cover -htmlをしようとしてもGOPATHが違うと言われて動作しなかったので、direnvを使っている場合は、direnvで指定したGOPATH配下に出力ファイルを作ると正常にhtmlに変換されます。

// デフォルトのGOPATHに移動
$ cd $GOPATH
$ ~/go go tool cover -html=cover.out -o cover.html
cover: can't find "application.go": cannot find package "GAE_PROJECT_GOPATH/model" in any of:
  /usr/local/Cellar/go/1.9.2/libexec/path/to/model (from $GOROOT)
  path/to/model (from $GOPATH)

ただ、gitでトラッキングする際にノイズになるので、 .gitignore~/.gitignore_global でノイズは取り払ってもおくといいかと思います。

『お金2.0』を読んだ

『お金2.0』を読んだので、ちょっとした感想をメモって起きます。

感想

帯やamazonの説明文、レビューコメントを参照した方が内容をざっと俯瞰するにはいいと思うので、ここで詳しくは載せませんが、それなりにこれからの経済のあり方がわからない人向けに丁寧に説明されている書籍でした。
普段から本書で述べられている内容に触れている身としては若干冗長さも感じつつも、改めて丁寧に説明されることで「ああ、確かに」とか「こういうことだったのか」といった知識の再確認やモヤっとしていたところが整理された感覚があり、すごくリズムよく読むことができました。
一方で「評価経済」「トークン/シェアリングエコノミー」に馴染みのない方には、正直何をいっているのか多分わからないのでは?的な感想を抱いてます。
そんなこと考えてたら、著者のTwitterでも似たようなことを呟いてて納得しました。

「お金」っていうキャッチーなフレーズを使っているので、内容が全然お金関係ないじゃんと思ったり、経済の枠組みや、歴史的背景といった内容まで一気に飛躍したりと書籍の中であっちにいったりこっちにいったりを繰り返しているのですが、今までいわば価値の王様として絶対的だった「お金」というものが、これから少しずつ変わってくるのではないか、ということが言いたかったのかなと思います。

これからは「お金」の意味も変わってくるし、国家が管理している経済以外の枠組みで色々な経済が生まれてくるだろうし、その各々の経済でエコシステムが成り立つだろうということは僕自身なんとなく感覚的には感じていました。

正直なところ。本書を読んだからといって、明日から何かが変わるとかということは一切ないと思います。
学術書ではないので、何か専門的な知識が身につくわけでもありません。
ただ、これからの経済を考える一つの観点を提供している書籍だと思います。何か新しい概念が出てきたときに、歴史からみてどうしてそういった概念、サービス、仕組みが現れてくるのか、それを俯瞰して、大きな流れの中の一つの点であるという観点を持つことができるようになるくらいのことだと思います。

お金っていう言葉が非常にキャッチーだし、内容の中でも、これからのお金の考え方そのものが変わってくるという趣旨の内容がメインなので、それにちなんだタイトルになっているのかなとも思いますが、個人的にはは「経済2.0」といってもいいのかもしれないと感じてます。
これからの経済、エコシステムを考えた時に、単一のエコシステムだけではなく、エコシステムにも多様性と、どのエコシステムで生きるのか選ぶ自由が出てくるのであろうという知識なり、一つの世界の見方、観点を持っておくことで、もし本当に本書で述べられているような時代が来たときに、面食らうことなくスムーズに受け入れることができるのではないかなーとかそんなことを感じながら本書を読み終えました。

自分でも不思議なくらい、読み終わった後に色々感が混んでしまってました。
頭の中が整理された一方で、またさらにふわふわした、モワッとした思考を延々繰り返してしまっています。

【学習】「GOならわかるシステムプログラミング」~Chapter 3~

※「GOならわかるシステムプログラミング」の3章の学習記録です。

主に

  • PNGファイルを分析してみる
  • PNGファイルに秘密のテキストを入れてみる

の2節の内容の学習記録です。

sample

sampleコードは以下

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "hash/crc32"
    "io"
    "os"
)

const (
    OFFSET = 8
)

func dumpChunk(chunk io.Reader) {
    var l int32
    binary.Read(chunk, binary.BigEndian, &l)
    // png をbyteに変換した時の入れ物
    buf := make([]byte, 4)
    chunk.Read(buf)
    fmt.Printf("chunk '%v' (%d byte)\n", string(buf), l)
    if bytes.Equal(buf, []byte("tExt")) {
        rawText := make([]byte, l)
        chunk.Read(rawText)
        fmt.Printf("%s\n", string(rawText))
    }
}

func readChunks(file *os.File) []io.Reader {
    // pngをchunkに分けたときのchunkを格納する入れ物
    var chunks []io.Reader

    // pngの最初の8byteを飛ばして9byte目から読み込む
    file.Seek(OFFSET, 0)
    var ofs int64 = OFFSET

    for {
        // data長のいれもの
        var l int32
        err := binary.Read(file, binary.BigEndian, &l)
        if err == io.EOF {
            break
        }

        chunks = append(chunks, io.NewSectionReader(file, ofs, int64(l)+12))

        // 次のchunkの先頭に移動
        ofs, _ = file.Seek(int64(l+8), 1)
    }

    return chunks
}

func textChunk(txt string) io.Reader {
    byteData := []byte(txt)
    var buf bytes.Buffer
    binary.Write(&buf, binary.BigEndian, int32(len(byteData))) // 書き込むためのbufferを確保。長さは入力文字長。
    buf.WriteString("tExt")                                    //bufferに書き込む
    buf.Write(byteData)                                        // 入力対象の文字をbufferに書き込む
    // CRCを計算して追加
    crc := crc32.NewIEEE()
    io.WriteString(crc, "tExt")
    binary.Write(&buf, binary.BigEndian, crc.Sum32()) // crc分を新しくbufferに書き込む
    return &buf                                       // 書き込み終わったbufferを返す
}

func WriteNewTextChunk() {
    // pngをチャンクに分けて読み込み
    file, err := os.Open("./imgs/Lenna.png")
    if err != nil {
        fmt.Printf("error! err: %v", err)
        os.Exit(-1)
    }
    defer file.Close()

    newFile, _ := os.Create("./imgs/result.png")
    defer newFile.Close()

    chunks := readChunks(file)
    // pngのシグニチャー書き込み
    io.WriteString(newFile, "\x89PNG\r\n\x1a\n")

    // 先頭にIHDR chunkを書き込む
    io.Copy(newFile, chunks[0])

    // TextChunk を追加する
    io.Copy(newFile, textChunk("Test Text Chunk"))

    // 残りのチャンクを新しく追加する
    for _, c := range chunks[1:] {
        io.Copy(newFile, c)
    }
}

func main() {
    // 私いchunkを書き込んだfileを生成する
    WriteNewTextChunk()

    file, err := os.Open("./imgs/result.png")
    if err != nil {
        fmt.Printf("error! err: %v", err)
        os.Exit(-1)
    }

    chunks := readChunks(file)
    for _, c := range chunks {
        dumpChunk(c)
    }
}

githubはこちら

github.com

TOPIC

エンディアン変換について

エンディアン変換(以下 Byte Swapping)がgoには標準で装備されています。
CPUはデータの先頭には小さい桁から格納されるリトルエンディアンが採用されていますが、ネットワーク(TCP/IPプロトコル)ではデータの先頭に大きな桁から格納されるビックエンディアンが採用されています。

そのため、ビックエンディアンで格納されているネットワークで受け取ったデータをリトルエンディアンに修正する必要があります。

それが何度か出てくる以下の部分

binary.Read(r io.Reader, binary.BigEndian, data interface{})

TCP/IPプロトコルではデータは BigEndian なので、rは BigEndian で格納されているので、binary.Read するときにorderに BigEndian を指定することで、そのorderでio.Readerからデータを読み込み、dataの中に格納し直します。格納するときはリトルエンディアンに修正して格納されます。

binaryパッケージ

encoding/binary についてのメモです。

binary.Read

encoding/binary パッケージ - golang.jp

Read関数

func Read(r io.Reader, order ByteOrder, data interface{}) os.Error Readは、構造化されたバイナリデータをrから読み込みます。データは固定サイズの値へのポインタ、または固定サイズの値のスライスでなければいけません。

バイナリかされたio.Reader型のデータから、任意のdata(interface型)へ変換する

binary.Write

encoding/binary パッケージ - golang.jp

func Write(w io.Writer, order ByteOrder, data interface{}) os.Error Writeは、wへdataのバイナリ表現を書き込みます。データは固定サイズの値、または固定サイズの値へのポインタでなければいけません。

data := "Hello World"
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, int32(len(data))) // dataサイズの長さをbufに書き込む
buf.Write([]byte(data)) // 確保したbufferにdataの内容を書き込む

binary表現の書き込みがどう行われているのか、知る機械になってよかった。こういったことはシステムプログラミングをしながらわかることなので、引き続き読み進めようと思います。

json.Unmarshalではnilの参照型へのmappingはできない

httpレスポンスを構造体にmappingする際に以下のようなコードを書くケースは多々あると思います。 ※ error handlingは割愛

// responseの取得
resp, _ := http.Get(url)
body := resp.Body
defer body.Close()

// []byteへの変換
b, _ := ioutil.ReadAll(body)

var data *Data

// Data structへのjsonのmapping
if err := json.Unmarshal(b, data); err != nil{
    panic(err)
}

しかしこのコードは一見正しいように見えますが、期待する動作はしません。
以下のようなerrorが発生します。

json: Unmarshal(nil *model.Data)

参照型でもnilの構造体にはUnmarshal(復元)して構造体へのmappingはできないというエラーです。

実は json.Unmarshal(src, dist) において dist にとるべきinterface型の変数はnilではいけません。
nilではなく空の参照型の構造体を指定する必要があります。
そのため、上記のコードは以下のように書き換える必要があります。

// responseの取得
resp, _ := http.Get(url)
body := resp.Body
defer body.Close()

// []byteへの変換
b, _ := ioutil.ReadAll(body)

// ここで空の構造体を指定する。
data := &Data{}

// Data structへのjsonのmapping
if err := json.Unmarshal(b, data); err != nil{
    panic(err)
}

data := &Data{} とすることで json.Unmarshal でstrcutにmapping可能になります。

なぜこのような挙動になるかを調べました。

json.Unmarshalの公式ドキュメントによると

Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. If v is nil or not a pointer, Unmarshal returns an InvalidUnmarshalError.

と冒頭に記述されており、 If v is nil or not a pointer, Unmarshal returns an InvalidUnmarshalError にある通り、「nilの参照型」or 「参照型でない」場合はErrorを返すことが公式に書かれています。

つまり、答えはまんま「nilの参照型は取れない」ということで完結します。

json.Unmarshal(src, dist) において、nilの参照型を取れないことについては以下のstackoverflowも参考になると思います。

Why does json.Unmarshal work with reference but not pointer?

エントリーの中で記述されている

To unmarshal JSON into a pointer, Unmarshal first handles the case of the JSON being the JSON literal null. In that case, Unmarshal sets the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into the value pointed at by the pointer. If the pointer is nil, Unmarshal allocates a new value for it to point to.

の中で、実際にUnmarshalメソッドがjsonを参照型に復元するときの挙動について記載してあって、これによると、unmarshalメソッドは復元プロセスの中で参照型をnilにする、しかし、復元対象の格納先の参照型の変数がすでにnilの場合、nilnilにするという挙動が発生し、そのためにerrorが発生するのだという解釈をしました。

追記

jsonのUnmarshalについてはブログの以下のエントリでも追記してます。

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com