emahiro/b.log

Drastically Repeat Yourself !!!!

参照型を作成するmake

goには2つの値の型があります

  • 値型
  • 参照型

値型と参照型

関数に渡されるときにコピーされる。
そのため、呼び出し元で引数に指定した値型の変数は関数に渡されるタイミングでコピーされ、コピーされた値が関数の呼び出しに使用されるので、呼び出し元の値は変化しない。

以下のコードで確認する

func main() {
  a := 1
  b := 1
  sampleFunc(a, &b)
  fmt.Println(a) // 値渡し
  fmt.Println(b) //参照渡し
}

func sampleFunc(a int, b *int) {
  a = a + 1 
  *b = *b + 1
}

メソッド内で使われている a は 呼び出し元のmain関数内の a のコピーであるので、sampleFunc内部で a の値に変更を加えてもコピー先の値が変更されるだけで、main関数内のコピー元のaの値は変更されない。

これを呼び出し先でコピーを渡さずに参照(ポインタ)にするとコピーではなくポインタが渡されて呼び出し先で変更した値は呼び出し元の値にも影響を与えることができるようになる。(サンプルコードでは値b)

なお、値型はメモリ空間の別領域に新しく変数のコピーを作成するので、使いすぎるとメモリ効率が悪くなる。
一方で参照型は変数のアドレスを参照するので、メモリ空間上は同じアドレスを指している。そのため、メモリ効率がいいが、意図せず変数が変更されることがある。(変数の汚染)

参照型でのみ値を持つmap

そもそも、このエントリーを書こうと思ったのは「プログラミング言語Go」の冒頭の方でmapが 参照型 であると明示されていたことによる。

値型参照型 が変数にはそれぞれあることは理解していたが、mapは最初から参照型でのみ値を持つということを理解していなかったので、ここに備忘録として記載。

sampleコード

func main() {
    a := map[string]string{"apple": "りんご"}
    sampleFunc(a)    
    fmt.Printf("%v\n", a)
}

func sampleFunc(a map[string]string){
    a["orange"] = "みかん"
}


// map[orange:みかん apple:りんご] と出力される

sampleFuncに渡している値は参照型でなく通常の値型としてのmap。もし map が値型なら sampleFunc内には map のコピーが渡されて main 内の map は書き変わらないはず。
しかし書き換わっているので、map はデフォルトで参照型のみで値を持つということがわかる。

この他にも参照型のみで値を持つ値に

  • slice
  • channel
  • interface

がある。

参照型を作り出すmake

goで参照型を作成するには make メソッドを使う

// sliceの作成
make([]int, 10)

//mapの作成
make(map[string]string)

値型と参照型。ちょっと迷うことが多いけど一度調べてみてよかった。

一通りスターティング的な内容の書籍は読み終わったので、ガッツリ「プログラミング言語Go」を現在呼んでいる。

shell二刀流を試してみてる

shellを二重で使ってみてます。

使っているshell

用途によって分ける

普段使いのshellはfishです。
もともとzsh使っていたのですが、なんとなく使っていてもっさり感が出てきたのと、いちいちrcファイルでカスタマイズするのがめんどくさくなってきたので fish に乗り換えました。

fishはカスタマイズも専用のパッケージ管理ツールで行ってくれるので非常に楽です。
使っているパッケージ管理ツールは oh-my-fish です。

しかし、fishの悩みは、shellスクリプトの書き方が独特だということ。
例えば $(コマンド) みたいな書き方は使えず、 A && B という書き方も使えません。 (それぞれ (コマンド)A; and B と書きます。)
直感的といえば聞こえはいいですが、普段業務で使うshellスクリプトだったり、ネットで探したときに出てくる参考資料は zsh or bash 向けのものがほとんどでそれ自体をいちいち fish 用に書き換える手間もかかるため、人思いに zsh も使ってます。

zsh もデフォだと見にくいので oh-my-zsh でカラースキーマだけカスタマイズしてます。

結果

めんどくさいかと思いきや、これが意外に使い勝手がいい。

  • zshの用途が制限されたので、今までみたいにzsh向けのカスタマイズが必要なくなったのが心理的にすごく楽
  • fishは今まで通り使っている
  • zsh用のコマンドが合ったら、zshに変更して終われば exit してfishに戻す。

こんな感じで二刀流を最近試してます。

Goのtemplateにおける値の評価の書き方

Golangのtemplateファイルでisやnot equal、and条件、or条件の書き方を調べたので備忘録です。

※ 適宜追加していきます。

is評価

{{ if eq num 1 }} // if num == 1

// 以下同義
{{ if eq ( .num, 1 ) }}

not equel評価

{{ if ne .num 1 }} // if num != 1

//以下同義
{{ if ne (.num, 1) }}

and 評価

{{ if and (eq .str1 "a") (eq .str2 "b") }} // if str1 == 1 && str2 == b

and A B または and (A, B) になります。

or 評価

{{ if or (eq .str1 "a") (eq .str2 "b") }} // if str1 == 1 || str2 == b

or A B または or (A, B) という書き方になります。

nil 評価

objectのpropertyがnilかどうかを判定したいケースを想定してます。

{{ if .Object.SampleProperty }} 
// SamplePropertyが存在する
{{ else }}
// SamplePropertyがnil
{{ end }}

参考: buildin関数

上記の他にもgoのパッケージである text/tempalte パッケージにはtemplateファイルで使えるbuiltin関数があります。

builtin関数は src/text/template/func.go の中を見ると

type FuncMap map[string]interface{}

var builtins = FuncMap{
    "and":      and,
    "call":     call,
    "html":     HTMLEscaper,
    "index":    index,
    "js":       JSEscaper,
    "len":      length,
    "not":      not,
    "or":       or,
    "print":    fmt.Sprint,
    "printf":   fmt.Sprintf,
    "println":  fmt.Sprintln,
    "urlquery": URLQueryEscaper,

    // Comparisons
    "eq": eq, // ==
    "ge": ge, // >=
    "gt": gt, // >
    "le": le, // <=
    "lt": lt, // <
    "ne": ne, // !=
}

と定義されています。
and関数を確認して見てみると、

// and computes the Boolean AND of its arguments, returning
// the first false argument it encounters, or the last argument.
func and(arg0 reflect.Value, args ...reflect.Value) reflect.Value {
    if !truth(arg0) {
        return arg0
    }
    for i := range args {
        arg0 = args[i]
        if !truth(arg0) {
            break
        }
    }
    return arg0
}

and (A, B, C...) という形式を取るときに、Aから評価していって一つでもfalseがあればfalseを返しています。

templateで使える関数を見るにはこの src/text/template/func.go を参照すれば良いと思います。。

Golangでの抽象化について

理解がなんとなく浅いと感じていたGolangの抽象化について簡単にまとめました。

goのサンプル

package main

import "fmt"

type Stringer interface {
    String() string
}

// Stringerを実装する
type Hex int
func (h Hex) String() string {
    return fmt.Sprintf("%x", int(h))
}

func main() {
    var s Stringer
    h := Hex(100)
    s = h // Stringer に Hexを埋め込む
    fmt.Println(h.String()) // Hexに定義されているString()
    fmt.Println(s.String()) // Stringerに定義されているString()
}
  1. Stringer はinterface型で string 型の返り値を持つ String() メソッドを持つ。
  2. ‘Hex’ は int型の新しいtypeとして定義されている。
  3. Hexstring 型の返り値を持つ String() メソッドを持つ。
  4. 変数 sString() の実装の詳細を定義していなくても、 Hex で定義されている String() の実装と同じふるまいをできる。

同じinterfaceを持つtypeで新しく定義した型は同じメソッドを持つ場合、実装の詳細をinterface型で定義している方に埋め込むことで、埋め込まれた側(ここでは Stringer 型)は Hex で定義されている実装の詳細を埋め込まれることで使うことができるようになる。

interface型を使うことで抽象化を実現できる。

というのがいまいち理解できなかったのですが、

type Hex int
func (h Hex) String() string{
    // String()の実装の詳細
}

としている箇所で Hex に対して String() メソッドを定義しているときに

type Hex int {
    String() string
}

と見ることができるので、

type Stringer interface {
    String() string
}

こう考えると埋め込むことでメソッド名と返り値が同じメソッドを持つ構造体の場合、実装の詳細も埋め込むことができる。

ということなのかなと。

「マイクロサービスアーキテクチャ」を読みました

マイクロサービスアーキテクチャを読んだ話

去年くらいに話題になった「マイクロサービスアーキテクチャ」を最近読みました。
仕事でマイクロサービスアーキテクチャを採用しているプロジェクトに関わることがあり、自分自身今までマイクロサービスアーキテクチャを採用しているプロジェクトに関わったことがなかったので、そもそもマイクロサービスってどういった設計で、Monolithicなサービスと何が違うのかをちゃんと理解しようと思って読みました。

本書の構成

  1. マイクロサービス
  2. 進化的アーキテクト
  3. サービスのモデル化方法
  4. 統合
  5. モノリス分割
  6. デプロイ
  7. テスト
  8. 監視
  9. セキュリティ
  10. コンウェイの法則とシステム設計
  11. 大規模なマイクロサービス
  12. まとめ

大まかにまとめると、

  • 1~2が概説
  • 3~ 4が各コンポーネントやDBの設計方法、森シック→マイクロサービスにする流れの説明
  • 6~9が実際にマイクロサービスアーキテクチャで考慮しなればいけない内容を分野を分けてそれぞれ具体例を用いて説明してくれている
  • 10以降はもう少し粒度が荒く、組織的な内容やマイクロサービスアーキテクチャを採用する上で考慮しなければならないこと

という感じ。

3~5、6~9は実際にマイクロサービスアーキテクチャを採用する上でのプラクティスやミドルウェアの説明など粒度が細かい内容が多かったので本エントリでは触れず。

監視ツールやテスト方法、セキュリティに関しては大体設計をする上でMonolithicでもマイクロサービスアーキテクチャでも根本は同じだと感じた。

監視については、マイクロサービスに関わる全コンポーネントを監視できるようにすること。
適切に振る舞っているかを一目でわかるようなメトリックスを用意することなど、Monolithicなサービスを開発しているときは監視対象もほぼ一箇所であった一方で、マイクロサービスは小規模な各コンポーネントが自律して動作しているので、すべて が正常に動作しているかを確認できるように監視しなればならない、ということ。

監視はMonolithicなサービスを開発しているときと比べると、監視対象が増える分、適切な監視体制を構築するのはレベルの高い作業だと感じた。

マイクロサービスアーキテクチャとは?

ではそもそもマイクロサービスアーキテクチャとは何なのか?

1章の言葉を借りると 協調して動作する小規模で自律的なサービス である。

  • 小さく、かつ1つの役割に専念
  • 自律的

どの程度小さければいいのかという問に対してケース・バイ・ケースという答えしか書いてなかったのはちょっと個人的には消化不良。
確かにそうだとは思うのだけれど、そこの指針をもう少し教えてほしかった印象がある。
そのあたりは、実際にアーキテクチャを考える上で議論しながら適切な粒度を決めていくのがいいのであろう。

マイクロサービスの原則

まとめの章から拝借

  • ビジネス概念に沿ったモデル化
  • 自動化の文化
  • 内部実装と詳細の隠蔽
  • すべての分散化
  • 独立したデプロイ
  • 障害の分離
  • 高度な観測

勉強になったところ

  • 「境界づけられたコンテキスト」にそってコンポーネントは分割すること
    • この本を読む前に会社の中でDDD本の輪読会をしていて、DDDについて、ある程度知識を持った状態で読んだので、粒度の粗い、主に設計文脈で説明されている内容を理解するのに非常に役立ちました。
    • ビジネスレベルでのドメイン、技術レベルでのドメイン、目的レベルでのドメインなど、コンテキストを分ける粒度は組織によって異なると思いますが、一定の指針としての 境界づけられたコンテキスト を参考にすべきというのはいい指針だと感じた。
    • コンポーネント間の境界では境界をどう策定するか、どう各コンポーネント間をつなげるかの議論を丁寧に、より厳格にしていくこと。
    • 特定のコンポーネントから見たときにAPIで別のコンポーネントとつなげるので、呼び出し元のコンポーネントは呼び出し先のコンポーネントの詳細は知らなくていいこと。
    • この 知らなくていい 、裏を返せば、何を知っている必要があるのか 、どんなインターフェースを用意するのかを設計段階で合意形成しておくのが大切。
      一方で各コンポーネント内については比較的制限はゆるく、コンポーネント内の規範に従えばいいと。
  • マイクロサービスもコンポーネント単位ではOOP的な思想が入っていること
    • Monolithicなサービス作っているとどうしても、内部で色々密結合してしまうことが多く、いざ大胆な変更をしたいときになかなか動き出せないことがある。
    • 疎結合、詳細の隠蔽というのはマイクロサービスアーキテクチャ全体で見るとオブジェクティブ指向のパラダイムが内部に採用されているように感じました。
  • 最初からマイクロサービスにしない
  • 銀の弾丸にはなりえない
    • マイクロサービスアーキテクチャは課題に対する銀の弾丸ではなく、あくまで求められる要件に対するソリューションの一つであること

読んでみての感想

流行っている、採用実績があるからと行って、プロジェクトにマイクロサービスアーキテクチャを採用していいかというのはやはりすぐには決められないことだと思う。 僕も、キャリアの中でMonolithicなサービスのいち部分を切り出したことがあったのを思い出したけど、Monolithicな状態でも疎結合で切り出しやすい形にしていてよかったと今振り返ってみても思う。
マイクロサービスアーキテクチャ銀の弾丸ではないので、やはり採用した瞬間に管理しなければならない対象は増え、全てを知る人がいなくなるかもしれない。また、ちょっとした変更でも複数コンポーネントの再デプロイが必要になるかもしれず、開発速度もその分食われて遅くなるので、スピード感をもって開発したいスタートアップなどにはむしろ向かない思想だと思う。

ただ、一方でコンポーネントが一つの状態で中にごった煮の如くありとあらゆるドメインのサービスが突っ込まれている状態のツラミももちろんあり、その点から見ると、ある一定程度の規模を超えたり、最初からスケールすることを前提とするサービスであるのであればマイクロサービスアーキテクチャを採用するのはいい選択肢だと感じた。

また、本書とはあまり関係ないけれど、去年話題になった書籍だからか、書籍内で紹介しているツールは主にAWSの内容が多かった。
今だとGCPやMicrosoftAzure等もクラウドサービスが拡張されてきて、フルマネージドな製品も出てきているので、AWSに拘る必要はないのかなーと感じた。

GoのtimeのFormat表記方法でハマったこと

GolangUnixタイムをISO8601形式に変換したいということがあって、実際に返還する際にFormatの指定の仕方でかなりハマりました。

こういった方の記事を参考にしたらすぐわかったのですが、

GO言語での日付処理

そもそもGoで時刻表記をするときにちょっとクセがあるのでメモとしてまとめてます。

GAE上では必ずUTC

最初にローカルで time を使ったときはJSTだったのですが、GAE上で動かしたときは自動的にUTCになってしまうので、JSTに変換しなければならない、というところでハマりました。
TimeZoneの話ではないんですね。

なので日本時間に合わせます。

import time

func main () {

    var (
        location := time.FixedZone("Asia/Tokyo", 9*60*60)
        now := func() time.Time { return time.Now().In(location) }
    )

    fmt.Printf("%v", now.Format(time.RFC3339))

}

独特なフォーマットの表記方法

日本時間への変更でも記述してありますが、goで指定した形式への変換は time.Format を使います。
ここで、RFC3339 ではなく、明示的にISO8601形式に変えます。

通常の言語であれば、年をY、月をMみたいに形式が決まっています。 なので、goでも同じことしようとしたらgoでは明示的に 2000-01-01T01:00:00+09:00 みたいに書式の結果で形式を指定します。 そこで試しに、表示させたい形式を適当に作ってみたところ。

now := time.Now()
fmt.Printf("%s", now.Format("2000-01-01T01:00:00+09:00"))

// 7074-09-78T77:79:14+07:00 ????

なんかよくわからない形式になってて、変換が失敗している...

ここでどうして変換したい書式を指定してもうまくいかないのか結構ハマりました。

結論としては goでのtimeのformat変換では使える文字が決まっている っていうことになります。

どういうことかというとgoのTimeパッケージのドキュメントを見るとかいてありました。
time - The Go Programming Language

変換したいフォーマットで使えるのは年なら Y ではなく 2006 みたいな感じです。

now := time.Now()
fmt.Printf("%s", now.Format("2006-01-02T15:04:05+09:00"))
// 2017-7-12T22:24:43+09:00

ちゃんと変換できました。

※ ISO8601形式で +09:00 と表記する形式は標準パッケージには用意されてなかったので、自前で書いてます。

このフォーマットの一覧はtimeパッケージの format.goの中に書いてます。

const (
        ANSIC       = "Mon Jan _2 15:04:05 2006"
        UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
        RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
        RFC822      = "02 Jan 06 15:04 MST"
        RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
        RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
        RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
        RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
        RFC3339     = "2006-01-02T15:04:05Z07:00"
        RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
        Kitchen     = "3:04PM"
        // Handy time stamps.
        Stamp      = "Jan _2 15:04:05"
        StampMilli = "Jan _2 15:04:05.000"
        StampMicro = "Jan _2 15:04:05.000000"
        StampNano  = "Jan _2 15:04:05.000000000"
)

Goのsliceで重複を削除する

重複削除処理を実装する

ruby でいうところの uniq メソッドみたいなものが golang の slice にもないのかと思って調べてみたけどないらしいので、重複のある slice に対して独自に処理を実装しなければ行けない。

go は非常にシンプルでLL言語をずっと書いていたエンジニアとしては覚えることが比較的少なく、静的型付けされていて安全で、高速という点で有用なツールだと思うけど、シンプルすぎる反面、LL言語ならデフォルトのAPIやメソッドとして提供されていそうな処理が実装されてなかったりすることが多い。。。

LL言語の処理をブラックボックス化してしまうことに比べれば、いいことのように感じる一方で、これくらい用意しておいてよ~的な感想もしばしば(笑)

というわけで go における slice の重複削除の実装方法を調べてみた。

方法

  1. mapを使う
  2. mapと空のstructを使う

map を使う方法

make(map[string]int) or make(map[string]bool) のようにkeyに配列の要素をおいて値を適当にintやboolをぶち込みます。

arr := [string]{"a", "b", "c", "a"}
m := make(map[string]bool)
uniq := [] string{}

for _, ele := range arr {
    if !m[ele] {
        m[ele] = true
        uniq = append(uniq, ele)
    }
}

fmt.Printf("%v", uniq) // ["a", "b", "c"]

どういうことかというと、重複キーがあるので、同様のキーを持つmapの場合は新しく値を上書きしない。
ここの処理で言うところの m["a"] = true は一度目はこれが呼ばれるけど、二度目はすでに true なので if句の中に入ってず、resultに a が二度入ることがない。
こんな感じでmapにboolをいれることで重複を削除する方法はgoでは一般的です。

空のstructとの合わせ技

だいたいのユースケースにおいてintかboolをぶち込む方法で事足りる前提で付け足しですが、空のstructを使っても同じことができます。
実際に出会う機会があるかはわかりませんが、メモリ的にきついめの状況があるかもしれず、intやboolでいちいちメモリ空間とってられん、的な状況があるかもしれません。(ないかもしれません)

arr := [string]{"a", "b", "c", "a"}
m := make(map[string]struct{}) // 空のstructを使う

for _, ele := range arr {
    m[ele] = struct{}{} // m["a"] = struct{}{} が二度目は同じものとみなされて重複が消える。
}

uniq := [] string{}
for i := range m {
    uniq := append(uniq, i)
}

fmt.Printf("%v", uniq) // ["a", "b", "c"]

loopを二度使わなければなりませんが、intやboolようにメモリを確保しなくていいので省メモリで実装できます。 struct{}{} はgoで出て来るお作法みないなもので、valueに意味はないよっていうこと表します。

Goでシェルを実行するCLIToolを書く

goで外部コマンド(デフォルトのコマンドやshellスクリプト)を実行するCLIツールを作ったのでその触りをまとめます。
内容は、goでCLIツール書く時の実装方法について。

やったこと

  1. shellスクリプトを外部コマンドとして叩く
  2. goの os/exec パッケージを利用してコマンドをひたすら記述していく

参考 Golangで外部コマンドを実行する方法まとめ

ディレクトリ構成

-- 
 |- app.go
 |- test.sh

shellスクリプトを外部コマンドとして叩く

使ったシェルスクリプトはとりあえず Hello を表示するだけ

#!bin/shell
echo "Hello"

goのスクリプトこち

package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    // カレントディレクトリを移動
    os.Chdir("./cmds")
    // コマンドを実行 .(ピリオド)は使えずにちゃんと sh コマンドを使う
    out, err := exec.Command("sh", "./test.sh").Output()
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }
    fmt.Println(string(out))
}

実行結果

$ go run app.go
Hello

できた。

パッケージを利用してコマンドをひたすら記述していく

使用パッケージ: os/exec

外部コマンド実行に際してとく使うインターフェイス

  1. exec.Command(cmd, args).Run() 結果を出力せずに実行。戻り値はerror
  2. exec.Command(cmd, args).Output() 実行結果で何かしら出力される場合はこちら。戻り値はoutput結果([]byte型)とerror
package main

import (
  "fmt"
  "os.exec"
)

func main(){
out, err := exec.Command("ls", "-la").Output()
  if err != nil {
    fmt.Println(err.Error())
    os.Exit(1)
  }
}

引数が複数ある時

git add XXXX 等など…

この場合、exec.Command(cmd, args) のargs に git 以下を入れるんですが、argsはslice型で取らないければなりません。 そこで git を例に取るならば add XXXX がargsの中身なり、これはargsは配列でする必要がある。

import (
  // 追加
  "strings"
)

func main (){
  // スペースごとに区切って配列化
  args := Strings.Filelds("add XXXX") // => ["add", "XXXX"]
  out, err := exec.Command("git", args...).Output() 
  if err != nil {
    fmt.Println(err.Error())
    os.Exit(1)
  }
  fmt.Pringln(string(out))

}

exec.Command は第一引数にコマンドを取り、それ以外は第2引数に入れなければならない。

goで実際にコマンドをつらつら書きながら進める場合、上記実装のようにひたすらコマンドの実行と成功の可否(outputとエラー内容)を愚直に確認しながら書いていきます。
もし処理が止まったら必ずexitしましょう。

goからshを呼ぶのはただたんにshellを書いているだけ。むしろそちらよりもgoにコマンドを実行してもらう方法の方がgoでCLIツールを書くには向いているような気がする。

【fish】CLI上で利用する変数を保持する

fishでのお話。

ターミナル上で特定の文字列を変数にセットして使う方法

$ set x (ls) | echo $x
# 当該ディレクトリ上で ls した内容を出力する

set x (何か出力をともなうコマンド) とすると () 内で実行されたコマンドの出力結果を x に格納する。

セットした変数をコマンドとして実行する

sample:

$ set x "pwd"; and echo $x
pwd # 文字列をそのまま出力するだけでは文字列化された `pwd` コマンドは実行されない
$ set x "pwd"; and eval $x
~/PROJECT_ROOT # 出力された

eval コマンドを実行すると文字列化されたコマンドを入れた変数をコマンドとして実行する。

fishでのevalの設定方法

fish を使っていて、 eval の設定方法がわからなかったので調べました。

参考
eval command in config.fish

# evalの設定方法
# bashrc での rbenv の設定
# eval "$(rbenv init -)" と同様のことを書きたい時
eval (rbenv init - | source)

# その他の書き方
# eval () ← zshやbashで言うところの $ を付けない
# source () 直に指定

などなど

rubyでオブジェクトの持つメソッドを探索する

ユースケース

オブジェクトの中に意図するプロパティを取り出せるのかを確かめる方法に、オブジェクトが持つメソッドを調査するという方法があります。
rubyではjsonオブジェクトのプロパティもメソッドとして取り出せるので、プロパティ not found エラーが発生してプログラムの処理が中断してしまわないか予め調べておくことができたら便利です。

Obj.methods.grep(regexp) を使用する。

例えばある特定のAPIを叩いて返ってきたレスポンスオブジェウトのプロパティを調べるという動作を想定します。

response.methods.grep(/任意のメソッド名 or プロパティ名/)

grepメソッドを使って正規表現で調べればいとするメソッドを調査できます。
methods で全てのメソッドを取り出さずとも、特定のメソッド(プロパティ)を取り出すことが可能。

※ 上記サンプルでは前方後方一致を使っています。

これは rails console でも使えるので、api叩いて、ちゃんと想定するレスポンスの型になっているかを事前に調査できるので便利だと思う。

githubのユーザーネームを変更した話

ユーザーネームを変更しようと思った背景

もともとのgithubのユーザーネームは ememhr っていうのを使っていたのですが、これ、前職で会社用のgithub.comを作るために、もともと作っていた個人用のgithubとは別にとったもので、インターネット周りでは全てのユーザーネームを emahiro で統一しているので、どうしても座りが悪いと感じていました。

もともと ememhr で作成したアカウントはそこまで運用していくつもりはなかったのですが、気づくと会社用で作ったアカウントがメインのアカウントとして稼働してしまっていて、もともと個人でもっていたアカウントは使わなくなってしまってました。
githubのアカウントが2つあるのも変な話なので、古いアカウントを削除して、空いたユーザーネームに変更しました。

変更に際して考えられた懸念点

  1. 今まで ememhr で作っていたリポジトリへのアクセス
  2. ローカル環境での古いユーザー名で作ったリポジトリ
  3. Organizationのアカウントのアクセスについて
  4. githubユーザーのリンクを表示しているサイトのリンクの変更

の4つが懸念点として考えられました。

参考

Changing your GitHub username
What happens when I change my username?

githubの公式のヘルプページを参照

変更方法

  1. Settingへ移動
    f:id:ema_hiro:20170518013042p:plain
  2. Account ▷ Change username を選択
    f:id:ema_hiro:20170518013100p:plain
  3. 警告を食らう
    f:id:ema_hiro:20170518013134p:plain

  4. 古いプロフィールページにはリダイレクトしないよ

  5. 古いユーザー名で作成したPage(github page)にはリダイレクトしないよ
  6. リポジトリは新しいユーザー名でリダイレクトするよ ← すご!!
  7. ちょっと時間かかるよ

ってことが書いてある。

なので問題なくユーザー名を変更します。

この時点で懸念点だった1はクリア。
変更後もOrganizationのアカウントには問題なくアクセスできました。 また、旧ユーザー名でリンクを貼っていたサイトもwantedlyとlinkedinとqiitaくらいだったので、この時点ではそこまで問題にならず。

んで最後のローカルのリポジトリについてですが、個人的に開発したものについてはほとんど更新する必要もなかったので、ほっといても良かったのですが、とりあえず地道に1つずつ新しいユーザー名でのURLに変更

# まず globalのユーザーネームを変更する
$ git config --global user.name 'emahiro'

# 変更したいリポジトリで
$ git remote -v  
origin https://github.com/ememehr/(repository_name).git (fetch)
origin https://github.com/ememehr/(repository_name).git (push)

# 新しいユーザーネームの形に変更
$ git remote set-url origin git@github.com:emahiro/(repository_name).git 

これでOK。

※1 もともとhttpで設定していたのに、ssh経由に変えた理由は ※2 リポジトリを一つ一つremote先を変更するのはめんどくさいですが、このためにリポジトリを断捨離しておいて良かったです。たまに、githubにあげておいてもしょうがないようなリポジトリは掃除しておくのもいいかもしれません。

  1. sshの方が容量が大きくてもpushできる
  2. httpでアクセスした時にusernameとpasswordを聞かれたが新しいのに変更してもうまく行かなかったので、ssh形式に変更することで対応。

これでかねてより取りたかったusernameでgithub生活を送れるようになりました。

【go】構造体にメソッドを追加する

goはオブジェクト指向言語とは違って、書いていてC言語を書いている印象に近く、OOPで言うところのクラスが構造体で、クラスにメソッドを定義することは、構造体にメソッドを定義することになる。

しかし、クラスでカプセル化したりするわけではないので、構造体にメソッドを定義する、その定義のしかたがちょっと理解しづらかったのでそのメモ。

理解しづらかったポイント

goを使って簡易的なHTTPサーバーを立てたときに、http.Handle にServeHTTPのメソッドを持つ構造体を作るって箇所で詰まって色々調べた備忘録。

package main

import (
    "fmt"
    "net/http"
)

// AppHandler アプリ
type AppHandler struct {
    appName string
}

func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(h.appName)
}

func main() {
    http.Handle("/", &AppHandler{appName: "myApp"})
    if err := http.ListenAndServe(":8080", nil); err != nil {
        fmt.Println("エラー発生")
    }

    fmt.Println("サーバースタート")
}

こういうコードを想定。特に最初???だったのはここ

func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(h.appName)
}

これは

type AppHandler struct {
  appName string
}

の構造体に ServeHTTP メソッドを定義している。

https://golang.org/pkg/net/http/#Handler を確認すると、

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

Handlerはinterface型なので、定義先はどんな型でもいいのだけれど、ServeHTTPというメソッドを持っていなくてはならない。
つまりAppHandlerの構造体であってもServeHTTPを定義することができる

func (構造体) (登録するfunc) {}

という形で定義が可能。

これでAppHandlerの構造体はServeHTTPというメソッドを持つことができ、http.Handleに登録して、指定したPortでHTTPリクエストを受け付けることができるようになる。

構造体以外にメソッドが定義できる。

HTTPサーバーを立てる際に以下のような定義の仕方もある。

func main() {
  http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        res, err := w.Write([]byte(`Test`))
        if err != nil {
            log.Println("Error")
        }
        log.Printf("%d", res)
  }))
}

HTTPサーバーを立てる上で行っている動作は直観的でわかりやすいのであるが、HanderFuncメソッドは一体どこからきていた何なのかがわからず。これもドキュメントを確認すると下記のようになっている。

type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)

HandlerFuncfunc(ResponseWriter, *Request) の関数型として定義されていて、HanderFunc 型に ServeHTTP が定義されている。これは構造体に ServeHTTP を定義していたのと似ていると思う。特定のエイリアスを関数型として定義して、その関数型に関数を持たせるという個人的には摩訶不思議な言語仕様のなせる技なんだなーと。

goは言語仕様がシンプルで柔軟というはこういうところから来ているような気がしました。

fishに乗り換えた話

zshの管理がめんどくさくなってきたのでもう少しイケてるshell環境をつくれないものかと調べてたら、fish なるものがあるらしく、使い勝手がよさげなので、期間限定で乗り換えてたときの備忘録

パッケージ管理

  1. oh-my-fish
  2. fisherman

1と2もどちらもパッケージ管理ツールに変わりはないので、どちらでも良さげ。
軽く調べたらfishermanの方がイケてるらしい。
最初に1を入れてその後2を入れたものの、 agnoster というテーマを使いたかったのに fisherman では表示がおかしかったので、oh-my-fish を使用。

shellの設定

fishでの標準的なシェルの設定は、 ~/.config/fish/config.fish を使用する。
しかし、oh_my_fish を使うと ~/.config/fish/conf.d/omf.fish になる。
ただし、ここに書きすぎると後々やっぱり fisherman 使いたいなーとか思った時に足かせになるので、自前で ~/.config/fish/config.fish を作って、そちらにちまちま設定を書いていった方が無難(のような気がする)

簡単に設定

pecoとghqを入れておく。

$ brew install peco
$ brew insatll ghq

冗長にbrewで入れようと思ったけど、oh-my-fishを入れると自動的にpecoもghqも入るっぽい。

というわけでoh-my-fishを入れる。

$ curl -L https://get.oh-my.fish | fish

これで omf コマンドが使えるようになる。
fishではoh-my-fishにしろ、fishermanにしろ、パッケージ管理ツールを使ってテーマ等々shellのカスタマイズを行うツールをインストールする。

例えば、agnoster というテーマを入れようと思った場合、

$ omf install agnoster

と入力すると、テーマがインストールされて自動的に反映される。インストールしているテーマは ~/.config/omf/theme のファイルに記録されている。 設定ファイル(config.fish)上で set theme (theme名) と書いておくことで使いたいテーマを決める。※ 1つしか入っていないと自動的に決まるみたい。

※ 注 : agnosterというテーマはPowerlineというフォントを指定しないと正確にテーマが反映されないので注意

とりあえず今回はここまで。
fishはパッケージ管理の容量で簡単にshellがカスタマイズできるので使い勝手が良さげ。専用のコマンドがあるのもありがたい。
設定ファイルはもう少し使い込んで、もろもろ設定してからまたエントリーにする予定。

go で簡易HTTPサーバー立てる

http.HandlerFuncを使う場合

package main

import (
  "fmt"
  "net/http"
)

func main () {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
    w.Writer([]bytes(`表示内容`))
  }
}

if err := http.ListenAndServe(":8080", nil); err ! = nil{
  fmt.Printf("エラー表示")
}

http.Handleを使う

package main

import (
  "fmt"
  "net/http"
)

type AppHandler struct{
  appName string
}

func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  fmt.Printf(h.appName)
}

func main () {
  http.Handler("/", &AppHandler{appName: "myApp"})
  if err := http.ListenAndSerce(":8080", nil); err ! = nil {
    fmt.Printf("エラー発生")
  }
}

Linterのエラーが発生する

exported type AppHandler should have comment or be unexported (golint)  というエラーが発生。これはキャメルケースの構造体(外部からアクセス可能)なのにコメントが無いよっていうエラー

// AppHandler
type AppHandler struct{
  appName string
}

とコメントをつける

comment on exported type AppHandler should be of the form "AppHandler ..." (with optional leading article) (golint) というLinterのエラーがまだ発生。これはコメントの形式の警告。 構造体名 (半角スペース) 説明 みたいな書き方が必要になる。

// AppHandler アプリケーションに関わる〜
type AppHandler struct{
  appName string
}

というコメントに説明箇所を半角スペースで追加する。