blue_field

技術的なメモとか読書記録とかいろいろ(の予定

AWS SDK for Ruby(v2) で簡単なS3操作

仕事でAWS SDK for Ruby (v2) をちょっとだけ使ったのでメモ。 AmazonS3の操作を行った。

aws/aws-sdk-ruby

環境

Credentialsの設定

~/.aws/credentialsに、IAMのユーザーのアクセスキーID・シークレットアクセスキーを書いておくと、 そちらから自動的に読み込まれる。

[default]
aws_access_key_id = <AWS_ACCESS_KEY_ID>
aws_secret_access_key = <AWS_SECRET_ACCESS_KEY>

他にもいくつか方法はあって、GitHubのREADME見るとこう書いてある

Default credentials are searched for in the following locations:

  • Aws.config[:credentials]
  • ENV['AWS_ACCESS_KEY_ID'] and ENV['AWS_SECRET_ACCESS_KEY']
  • The shared credentials ini file at ~/.aws/credentials (more information)
  • From an instance profile when running on EC2

環境変数から直接読み込んでもくれるし、 Aws::Credentials.newインスタンスを作成してAws.config[:credentials]にセットするというやり方でも良さそう。 とにかくGitHubとかにキーをPushするなと。

S3上での各種操作

今回はpryから触った。 Railsプロジェクトでsdkbundle installして利用するときは、最初のrequire要らないはず。

S3のリージョンをセットし、S3用クライアントのインスタンスを生成。

require "aws-sdk-core"
Aws.config[:region] = 'ap-northeast-1'
s3 = Aws::S3::Client.new

s3.methods実行すると、メソッドがいっぱいある...

バケットの一覧表示

resp = s3.list_buckets
resp.buckets
#=> [#<struct  name="aono-test", creation_date=2015-02-15 09:25:20 UTC>]

resp.buckets.map(&:name)
#=> ["aono-test"]

バケットの作成・削除

s3.create_bucket(bucket: 'fugafuga')
#=> #<struct  location="http://fugafuga.s3.amazonaws.com/">

s3.delete_bucket(bucket: 'fugafuga')
#=> #<struct>

オブジェクトの一覧表示

例えばバケットの直下に3枚の画像が入っているとする。

resp = s3.list_objects(bucket: 'aono-test')
resp.contents.map(&:key)
#=> ["foo.jpg", "bar.jpg", "hoge.jpg"]

フォルダーがあって、その下のやつだけ見たいときは、list_objectsメソッドにprefixオプションを指定する。

s3.list_objects(bucket: 'aono-test',  prefix: "folder")

こうすると、folderディレクトリとその直下のファイルだけが一覧される。

また、max_keysオプションで一度に取得するオブジェクトの数を制限できる。 (デフォルトは1,000に設定されている模様)

レスポンスがページングに対応しているので、 それと組み合わせて適度な数だけ取得してくるのがベターな使い方、だろうか。

resp = s3.list_objects(bucket: 'aono-test',  max_keys: 2)

resp.last_page?  #=> false
resp.next_page?  #=> true

next_resp = resp.next_page

アップロード

file = File.open('/path/to/file')
file_name = File.basename('/path/to/file')

s3.put_object(
  bucket: "aono-test",
  body: file,
  key: file_name
)

ダウンロード

File.open("/path/to/file", "w") do |file|
  s3.get_object(bucket: 'aono-test', key: "foo.jpg") do |chunk|
    file.write chunk
  end
end

これで、バケットに置いてあるfoo.jpgをDLして、所定の場所に保存する。

おわり

メモおわり。

上記で挙げた基本的な操作以外にも、バケットポリシーやライフサイクルの更新や削除etc.できるみたいだし、 プログラムからほぼ全操作可能なのだろう。自動化には欠かせないし、今さらながら便利である...

年末年始で読んだ技術書など

社会人3年目の年末年始。去年と一昨年は、iOS版のFinalFantasy(4, 5)やりこんでたら冬休みがほぼ終わってた。今年は何となくゲームに手を付けず、技術書やマンガを読んだりHulu見たりして過ごしていた。

そんなわけで、読んだ技術書について、個人の日記レベルなメモを書いておこうかと思う。

よんだ技術書

評判が良さそうな本を何冊か読んだ。個人的にはどれも当たりの本で学びがあった。

  1. 『HTTPの教科書』
  2. 『Everyday Rails - RSpecによるRailsテスト入門』
  3. 『Webエンジニアが知っておきたいインフラの基本 ~インフラの設計から構成、監視、チューニングまで~』

1. HTTPの教科書

Web開発をする際に知っておくべき「HTTP」の解説を分かりやすくしてくれている本。

自分はRailsからプログラミングを始めた。 Railsだとある程度簡単にWebアプリを作れてしまうこともあり(これがRailsの良い所でもあるのだと思うが)、こういったWebの根幹の仕組みについて理解できていないことが結構多いのでは、という不安がある。(不安ではなくたぶん事実)

この本は、「Webエンジニア」を名乗るならたぶん知っておくべき内容の基礎が平易に書いてあり、さらっと読めた。セキュリティに関してや、SPDYなど新しめのトピックに関しても触れられており非常に良かった。

残念ながらこういった知識を一度で吸収できる脳みそではないので、この本や『Webを支える技術』などを定期的に読み返していくと良さそう。 セキュリティといえば徳丸さんの本ですが、こちらもまだ未読なので読まねば。

動く「成果物」を生み出すことが優先される環境にいると、こういった基礎的な内容がどうしても疎かになってしまう気がする。 けど、こういうのも絶対必要だよなー。(教養の面でも成果物のクオリティの面でも)

2. Everyday Rails - RSpecによるRailsテスト入門

https://leanpub.com/everydayrailsrspec-jp

RSpecの入門本。英語で書かれた原著の和訳。 英語版のアップデートが結構されてるぽいけど、和訳版もちゃんとそれに追随してくれており、RSpec3対応。(翻訳者の方々ありがとうございます)

本の中で利用されているサンプルコードもGitHubに置いてある。 everydayrails/rspec_rails_4

簡単なRailsアプリにRSpecを使ったテストを組み込んでいく内容。 読みつつ写経しつつ8章の終わり(全12章)まで読了。 モデルスペックやコントローラスペックの作成、FactoryGirlによるテストデータ作成、Capybaraを使ったフィーチャースペックの作成などを学べた。 RSpecの思想的なところもなんとなく分かってきて、なるほどなーという感じ。

そもそもちゃんと勉強してみるかーと思ったのは、業務で触っているRails3.2の某レガシーコードを4.0系にアップデートしたいと思ったため。 各種ライブラリのサポートも切られ始めてるし、これはそろそろマズイなと。

どのレイヤーのテストを書くべきか問題とか、カバレッジにどこまでこだわるのか問題とかまあ色々あるんだろうけど、 自分の関わっているアプリケーションはその問題に突き当たるレベルにまで達していないので、とりあえず少しずつ書いていきましょうという感じかなぁ。。 実際のアプリは本の中のアプリほど単純なわけでもなく、簡単なモデルスペックを少し書くだけでもつらみを感じた...

後からテスト書くと設計のクソさに気づくし、先にテスト書くとクソな設計を作りにくくなる、という効果もありそう。

3. Webエンジニアが知っておきたいインフラの基本 ~インフラの設計から構成、監視、チューニングまで~

先月の下旬に出た本。Twitterなどでちょこちょこ感想を見かけたので読んでみた。 「インフラ」に関する内容を広範囲に扱ってくれている。 前半部分はインフラの設計や要件定義などについて広く触れられており、 後半部分は監視や障害対応、パフォーマンスチューニングのポイントなどについて具体的に書いてあった。

周りにインフラをメインでやってるエンジニアの人がいないので、「ああ、インフラエンジニアの方はこうやって仕事しているのかー」という妙な驚きがあったw

「ちゃんとシステムを組む」ための方法が説明されている。 関わっているサービスのインフラ周り(主にAWS)をイメージしつつ読んでいたが、自分の知らない分野・理解の薄い分野の話を補完できて良かった。

DevOpsというワードが一時期流行ったが、これから双方の垣根がどんどん無くなっていくのは間違いない。 そういう意味で、こっち側の世界ももう少し知らんとまずいかなーと感じた。

そのほか

以前のWeb+DB PressのRails4の紹介記事だったり、以前購入して積ん読になってた『チーム開発実践入門』をつまみ食いして読んだりしていた。

(蛇足)よんだマンガ

漫画読んだりもしてた。 普通に過ごしてると少年ジャンプとかヤンジャン系の漫画ばっかりになるので、ちょっと違うのを新規に読んでみた。

  • 僕だけがいない街(1, 2巻)
    • サスペンス系(?)。面白かった、続きが非常に気になるのでたぶんこの後も買う
  • クズの本懐(1巻)
    • 主人公、いまのところそこまでクズじゃない気もする。もっとクズになるなら続き読みたい
    • Twitterでよく見かける例の画像はこの漫画が出典だったんすな

みんな良い漫画はどうやって見つけてるんですか。

おわり

年末年始に読んだ本の簡単な紹介おわり。

冬休み、なんとなく2週間くらいあるイメージだったけど、それより全然短かった。。 インプット重視だったのもあり、あまりコードは書けず。 まあこうやって読書の時間をいっぱいとれる期間もなかなか無いので、良かったかなと思う。

僕の技術レベルはまだまだかなり低い。 自分が組織の成長の足を引っ張らないよう、今年も粛々と腕を磨かなくては...

と、ちょっとだけ意識の高いこと書いて終わります。

Goで簡単な行列計算

Golangでの簡単な行列計算。

ライブラリ

gonum/matrix を利用してみる。

実装

Goのバージョンは1.4。

行列の作成

行列は、mat64.NewDenseという関数を利用して宣言する。 下記の例では、[1 2 3]という行列をxに代入している。

// matrix.go
package main
    
import (
    "fmt"

    "github.com/gonum/matrix/mat64"
)

func main() {
    elem := []float64{1, 2, 3}

    x := mat64.NewDense(3, 1, elem)
    fmt.Println(x)
}

実行。

$ go run matrix.go
&{{3 1 1 [1 2 3]}}

NewDenseの実装を覗いてみる。

// matrix/mat64/dense.go
func NewDense(r, c int, mat []float64) *Dense {
    if mat != nil && r*c != len(mat) {
        panic(ErrShape)
    }
    if mat == nil {
        mat = make([]float64, r*c)
    }
    return &Dense{RawMatrix{
        Rows:   r,
        Cols:   c,
        Stride: c,
        Data:   mat,
    }}
}

列数, 行数, 要素を表す配列の3つを引数として渡し、 返り値のインスタンスには、列数, 行数, Stride(=行数), 行列が含まれる。

行列の要素へのアクセス

Atを使い、位置(列と行)を引数に指定。

elem := []float64{1, 2, 3}
x := mat64.NewDense(3, 1, elem)
fmt.Println(x.At(1, 0))
// => 2

行列の和や積

行列の和や積などの算出。

和はAdd, 差はSubという関数を利用。 返り値はなく、メソッドのレシーバに演算結果が代入される。

func main() {
    elem_x := []float64{1, 2, 3}
    elem_y := []float64{4, 5, 6}
    elem_a := make([]float64, 3)

    x := mat64.NewDense(3, 1, elem_x)
    y := mat64.NewDense(3, 1, elem_y)
    a := mat64.NewDense(3, 1, elem_a)

    a.Add(x, y)
    fmt.Println(a)
    // => &{{3 1 1 [5 7 9]}}

    a.Sub(x, y)
    fmt.Println(a)
    // => &{{3 1 1 [-3 -3 -3]}}
}

続いて行列積。Mulを利用。 下記のようにinit関数を指定しておかないと、panic: mat64: no blas engine registered: call Register()というpanicが発生した。

package main

import (
    "fmt"

    "github.com/gonum/blas/goblas"
    "github.com/gonum/matrix/mat64"
)

func main() {
    elem_x := []float64{1, 2, 3, 4}
    elem_y := []float64{5, 6, 7, 8}
    elem_a := make([]float64, 4)

    x := mat64.NewDense(2, 2, elem_x)
    y := mat64.NewDense(2, 2, elem_y)
    a := mat64.NewDense(2, 2, elem_a)

    a.Mul(x, y)
    fmt.Println(a)
    // => &{{2 2 2 [19 22 43 50]}}
}

func init() {
    mat64.Register(goblas.Blas{})
}

2x2行列の積がちゃんと出てる。

逆行列

逆行列の算出には、Inverseを利用。

func main() {
    elem_x := []float64{2, 5, 1, 3}

    x := mat64.NewDense(2, 2, elem_x)
    inv_x, _ := mat64.Inverse(x)
    fmt.Println(inv_x)
    // => &{{2 2 2 [3 -5 -1 2]}}
}

[[2, 5], [1, 3]]逆行列は、[[3, -5], [-1, 2]]。OK。

https://github.com/gonum/matrix/blob/master/mat64/matrix.go に、Inverseの実装が載ってる。

番外: Rubyでやる

上記でやったようなことをRubyでやるとどうなるか。 Rubyのバージョンは、2.2.0preview2。

Matrixクラスを利用。

# matrix.rb
require 'matrix'

a = Matrix[[1, 2], [3, 4]]
b = Matrix[[5, 6], [7, 8]]

sum = a + b
# => Matrix[[6, 8], [10, 12]]

mul = a * b
# => Matrix[[19, 22], [43, 50]]

c = Matrix[[2, 5], [1, 3]]
c.inverse
# => Matrix[[(3/1), (-5/1)], [(-1/1), (2/1)]]

...やっぱり簡単に書ける。

おわり

Goの簡単な行列演算などを試してみた。 はじめから分かってたけど、扱いやすさではRubyPythonにやはり軍配が上がりますかね。。 あと、行列を転置する方法がよくわからなかったw

数値計算系のライブラリもう少し充実してくれたりすると嬉しいですね。

Golangを触り始めた② 文法・型

前回に引き続き、Web+DB vol.82のGo特集が分かりやすいのでそれ見ながら。

ぜんぶ網羅して書いてないけど、メモ。Goのバージョンは1.3.3です。

変数

var message string = "Hello world"

変数宣言は、varで始まり、変数名、型の順。ちょっと違和感。

関数内部で変数宣言と初期化を行う場合は、以下のように:=を利用しても書ける。こっちの書き方を使うことが多そう。変数の型は、代入する値から推論される。

/* import文は省略 */

func main() {
    message := "Hello world"
    fmt.Println(message)
}

定数は、varじゃなくてconstで宣言。

if

条件部分に()は付けないが、処理部分は{}が必要。

func main() {
    a, b := 10, 1
    if a > b {
        fmt.Println("aの方がデカイ")
    }
}

for

func main() {
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
}

whileloopは無いので、forで代替。

n := 0
for n < 10 {
    fmt.Println(n)
    n++
}

ループから抜けるのはbreak、次の繰り返しに行くのはcontinue

switch

func main() {
    for n := 0; n < 31; n++ {
        switch {
        case n%15 == 0:
            fmt.Println("FizzBuzz")
        case n%5 == 0:
            fmt.Println("Buzz")
        case n%3 == 0:
            fmt.Println("Fizz")
        default:
            fmt.Println(n)
        }
    }
}

関数

関数はfuncで宣言。引数がある場合は、変数と合わせて型も指定。戻り値がある場合は、引数の次に書き、型を指定。

func sum(i, j int) int {
    return i + j
}

func main() {
    n := sum(1, 2)
    fmt.Println(n) // 3
}

関数は複数の値を戻り値として返すことができる。その際、戻り値を格納する変数を必要数分用意してあげないといけない。用意する変数の数が合わないとコンパイルエラー。無視したい戻り値があるときは、_を利用して無視することを明示する。

func swap(i, j int) (int, int) {
    return j, i
}

func main() {
    x, y := 20, 30
    x, y = swap(x, y)
    fmt.Println(x, y) // 30, 20

    x, _ = swap(x, y)
    fmt.Println(x) // 20
}

関数でエラーを返す

上記のような複数の値を戻り値として返す特徴を利用し、Golangでは内部で発生したエラーを戻り値で表現する。

また、エラーはerrorsパッケージを使って自作できる。

// main.go

package main

import (
    "errors"
    "fmt"
    "log"
)

func div(i, j int) (int, error) {
    if j == 0 {
        return 0, errors.New("divided by zero")
    }
    return i / j, nil
}

func main() {
    n, err := div(10, 0)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(n)
}

上記を実行すると、こんな感じになる。

$ go run main.go
2014/10/26 15:29:31 divided by zero
exit status 1

配列

Golangの配列は固定長。 例えば、長さが3で要素の型がstringだったら、こう書く。

arr := [3]string{"a", "b", "c"}

// または
arr := [...]string{"a", "b", "c"}

配列型は長さも情報として持っているため、長さが異なると別の型と認識される。

func show(arr [4]string) {
    fmt.Println(arr)
}

func main() {
    var arr1 [4]string
    var arr2 [5]string

    fn(arr1)
    fn(arr2) // コンパイルエラー!
}

スライス

こっちが慣れ親しんでいる可変長配列。

var s []string

このように長さの情報を宣言に含めないで書くと、スライスになる。

append

スライス(の末尾)に値を追加をするにはappendを利用。

var s []int
s = append(s, 1, 2)
fmt.Println(s) // [1 2]

range

配列やスライスに格納された値を先頭から順に処理するときは、rangeを利用。

arr = [3]string{"a", "b", "c"}
for i, s := range arr {
    fmt.Println(i, s) // iが添字、sが値
} 

上記のような使い方はRubyeach_with_index的な感じかな。

値の切り出し

s := []int{1, 2, 3, 4, 5}

fmt.Println(s[2:4]) // [3 4]
fmt.Println(s[:3]) // [1 2 3]
fmt.Println(s[3:]) // [4 5]

引数として使うとき

以下のように書くと、任意の長さのスライスを引数として使える

func sum(nums ...int) (result int) {
    for _, n := range nums {
        result += n
    }
    return
}

func main() {
    fmt.Println(sum(1, 2, 3, 4)) // 10
}

マップ

Rubyでいうところのハッシュ的なやつ。以下のように宣言。

var month map[int]string = map[int]string{}

month[1] = "January"
month[2] = "February"

宣言と初期化を同時にやるならこう書く。

month := map[int]string {
    1: "January",
    2: "February",
}
fmt.Println(month) // map[1:January 2:February]

値へのアクセス

Rubyなどと方法は同じ。

jan := month[1]
fmt.Println(jan) // January

ただし、2つめの戻り値も実はある。 (このケースは変数を1つしか受け取らなくてもコンパイルエラー出ないのね)

2つめの戻り値は、指定したキーがこのマップに存在しているかを表すbool値。

_, ok = month[1]
if ok {
    // データが存在したとき、何らかの処理
}

マップからデータ消すときはdeleteを使う。

delete(month, 1)
fmt.Println(month) // map[2:February]

スライスと同様、rangeで繰り返し処理を実現できるが、取り出される順番は保証されないらしい。。

ポインタ

ポインタ型の変数には、型の前に*を付ける。アドレスは変数の前に&をつけると分かる。Cと似ているらしい(触ったことない...)

func callByValue(i int) {
    i = 20
}

func callByRef(i *int) {
    *i = 20
}

func main() {
    i := 10
    callByValue(i)
    fmt.Println(i) // 10
    callByRef(&i)
    fmt.Println(i) // 20
}

defer

例えば、ファイル操作で利用するfile.Close()(ファイルを閉じる作業)は必ず実行されなければならない。だが、関数の途中でパニックが起こったりすると、この文まで到達できない可能性がある。

そういったケースに使うのがdefer。これを処理の前に書いておくと、main()を抜ける前にかならず実行されるようになる。

defer file.Close()

パニック

スライスの範囲外にアクセスしようとした時など、エラーを戻り値として返せない時、パニックという方法でエラーが発生する。

パニックは、recover()で取得し、その中でエラー処理をする。recover()deferの中に書く。

// main.go

func main() {
    defer func() {
        err := recover()
        if err != nil {
            log.Fatal(err)
        }
    }()

    a := []int{1, 2, 3}
    fmt.Println(a[10])
}
$ go run main.go
2014/10/26 16:33:39 runtime error: index out of range
exit status 1

panicは自分でも発生させられる。

// main.go

a := []int{1, 2, 3}
for i := 0; i < 10; i++ {
    if i > len(a) {
        panic(errors.New("index out of range"))
    }
    fmt.Println(a[i])
}
$ go run main.go
1
2
3
panic: runtime error: index out of range

goroutine 16 [running]:
runtime.panic(0xb7e60, 0x142b5c)
    /usr/local/Cellar/go/1.3.3/libexec/src/pkg/runtime/panic.c:279 +0xf5
(以下略)

Goで書かれたソースをほとんど読んだことないので、使いどころのイメージがまだあまりできない。。エラーは、できるだけ戻り値として表現し、無理なときにpanicにするって感じだろうか。

構造体

Rubyのクラス的なやつ。typeを用いて宣言。 構造体のあとにフィールドを記述する。フィールドは、名前が大文字から始まればパブリック、小文字からならパッケージ内のみから見える。

type Task struct {
    ID     int
    Detail string
    done   bool
}

func main() {
    task := Task{
        ID:     1,
        Detail: "Write a code",
        done:   true,
    }

    fmt.Println(task.ID) // 1
    fmt.Println(task.Detail) // "Write a code"
}

taskには構造体が代入され、各フィールドにはドットでアクセスできる。

ポインタ型として扱うことも可能。

new()

new()で構造体の初期化を行える。フィールドはすべてゼロ値となる。

func main() {
    var task *Task = new(Task)
    fmt.Println(task.done) // false
}

コンストラク

Newではじまる関数を定義し、その中で構造体を生成することで、コンストラクタの役割を果たす。

func NewTask(id int, detail string) *Task {
    task := &Task{
        ID:     id,
        Detail: detail,
        done:   false,
    }
    return task
}

func main() {
    task := NewTask(1, "Write a code")
    fmt.Printf("%+v", task) // &{ID:1 Detail:Write a code done:false}%
}

メソッド

型にはメソッド定義ができる。 以下は、レシーバをポインタとして受け取るケース。

func (task *Task) Finish() {
    task.done = true
}

func main() {
    task := NewTask(1, "Write a code")
    task.Finish()
    fmt.Printf("%+v", task) // &{ID:1 Detail:Write a code done:true}%
}

ポインタを使っているので、レシーバのdoneの値がtrueに変わっている。

インターフェース

インターフェースは、型がどのようなメソッドを実装すべきかを規定する。

type Finisher interface {
    Finish()
}

こんなかんじで、関数名にerを付けて書くのがお作法らしい。

型の埋め込み

Goには、継承という概念が無いみたい。代わりに、「他の型を埋め込む」という形で構造体やインターフェースを拡張できる。

以下、構造体の埋め込み例。

// main.go

package main

import "fmt"

type User struct {
    FirstName string
    LastName  string
}

func (u *User) FullName() string {
    fullname := fmt.Sprintf("%s %s", u.FirstName, u.LastName)
    return fullname
}

func NewUser(firstName, lastName string) *User {
    return &User{
        FirstName: firstName,
        LastName:  lastName,
    }
}

type Task struct {
    ID     int
    Detail string
    done   bool
    *User
}

func NewTask(id int, detail, firstName, lastName string) *Task {
    task := &Task{
        ID:     id,
        Detail: detail,
        done:   false,
        User:   NewUser(firstName, lastName),
    }
    return task
}

func main() {
    task := NewTask(1, "Write a code", "Yusuke", "Aono")
    fmt.Println(task.FirstName)
    fmt.Println(task.FullName())
    fmt.Println(task.User)
}
$ go run hello.go

Yusuke
Yusuke Aono
&{Yusuke Aono}

Taskの構造宣言時に、Userという型を埋め込む。これでTaskからUserのフィールドやメソッドにアクセスできるようになる。

おわり

お腹がすいたので、おわり。 Ruby書いてる時と異なり、留意しなきゃいけない点が多そうではある。ま、性質の違う言語なんだから当たり前か。。

エラー処理のあたりは難しそう(書くのめんどくさそう)な印象だが、もう少し書いてみないと判断できないですね。

Golangを触り始めた

タイトルの通り、ほんとに触り始めたという程度だけど。。 速いと聞くし、Rails以外の選択肢としてGo製のWAFもそのうち触ってみたいな〜と思っている。

http://golang.org

f:id:yusuke_1031:20141020123314p:plain

インストール

もともとMacpecoghqといったコマンドラインツールを利用していたので、homebrewでのGoインストール、GOPATHの設定くらいはしていた。

zshにpeco + ghqを導入したメモ

$ brew install go

$ go version
go version go1.3.3 darwin/amd64

$ echo $GOPATH
/Users/Aono

触ってみる

何を教材にしようかなと思っていたのだが、WEB+DB PRESS Vol.82 のGo特集が良さげだったので、それを見ながら書いている。

Hello world

よく分からないまま、とりあえずハローワールド。 外部のパッケージとか呼び出す、本元というかメインのファイルにpackage mainと書くのかな。

/* hello.go */

package main

import (
    "fmt"
)

func main() {
    fmt.Println("hello world")
}
$ go run hello.go
hello world

コンパイルしてバイナリファイルにするには、

$ go build hello.go

同じディレクトリにhelloという実行ファイルが生成される。

$ ./hello
hello world

結構厳しいコーディング規約

インデントがハードタブじゃないといけない。 あと、文字列をシングルクオーテーションで括ったらエラーが出た。 シングルクオーテーションで括るとルーンリテラルとなり、ダブルクォーテーションの時の文字列リテラルとは別物のようだ。

Golangには、この規約に従ってコードを整形してくれるコマンドが標準で付いている。

$ go fmt hello.go

Goフォーマットすると、ソフトタブ(半角スペース)がハードタブになるなどの変換が行われる。

Atomプラグイン

毎回上記のコマンドを実行しなくても済むように、各エディタで拡張機能があるっぽい。 Atom Editorにもgo-plusというプラグインがあったので、それを利用。

Goのパッケージも必要みたいなので、go getしておく。

$ go get code.google.com/p/go.tools/cmd/goimports
$ go get github.com/golang/lint/golint

こうしてgo getしたパッケージは、$GOPATH/src以下にインストールされていく。 実行ファイルは$GOPATH/bin下に入るようなので、PATHを通しておいた。

これでAtomのパッケージ側にちょろっと設定をすれば、ファイル保存時にフォーマットしてくれたりするようになる。とりあえずのところはこれで大丈夫かな。

ATOMでGolangの環境設定

godocコマンド

パッケージのドキュメントを見るコマンド。 なんか雑誌の中ではさらっと使っていたのですが、僕のMacだとこのコマンドが最初使えなかった。

別途インストールが必要?みたい。

$ go get code.google.com/p/go.tools/cmd/godoc

これを実行した後も、$GOPATH/bin下にgodocはなかった。 以下の記事を参考に、/usr/local/opt/go/libexec/binにもPATH通したら使えるようになった。 これで合ってるのかな...

godoc with homebrew installed Go

ちなみにタイミングを忘れてしまったのだけど、go getの際、mercurialが入ってないのでコード取ってこれないぞ、と怒られた。homebrewでインストールした。

以下のようなコマンドで、ドキュメントを見れるようになった。

$ godoc fmt

Goのディレクトリのお作法とか

基本的に、

$GOPATH
├── bin
├── pkg
└── src

というディレクトリ構造で開発していくようだ。分かりやすい。

今のところは、srcのみにフォーカス。src下に実際に書いていくコードを格納する。 GitHub上に置いてある(置く予定の)ソースコードは、$GOPATH/src/github.com/<ユーザー名>/<リポジトリ名>に設置して作業していく。ghqを利用してソースコードを取得したりしていたのもあり、そんなに混乱はしなかった。

以下、またサンプルコード。

/* $GOPATH/src/github.com/ysk1031/gosample/gosample.go */

package gosample

var Message string = "hello world"
/* $GOPATH/src/main/main.go */

package main

import (
    "fmt"

    "github.com/ysk1031/gosample"
)

func main() {
    fmt.Println(gosample.Message)
}

mainパッケージ側で、自作した外部のパッケージをインポートしている。 インポートの際は、$GOPATH/srcより下のファイル名を指定すれば自動で解釈してくれるようだ。

最後にパッケージのビルド。 さっきは、go buildでその場に実行ファイルを生成したが、今回はgo installを利用。

$ cd $GOPATH/src/main
$ go install

実行すると、$GOPATH/bin下に実行ファイルmainが生成される。なるほど! すでに$GOPATH/binにPATHを通してあるので、コマンドラインのどこからでも、mainというコマンドを実行することができるようになった。

$ main
hello world

Atomのところでgolintをgo getでインストールした際、$GOPATH/bin以下に実行ファイルも生成されていた。go getは、$GOPATH/src下にソースを持ってきた後go installを実行している、という認識で合っているのかなあ。

おわり

今回はいったん終わり。 Web+DBの記事は非常に分かりやすく書かれていて、苦しまずに読み進めていけそう。

会社の方でSwiftも書く必要が出てきそうで(こちらも初心者)、Goにどこまでパワー割けるか分からないのだけれど、引き続き学んでいきたい。