まっしろけっけ

めもてきなやーつ

RubyでStyleStatsをつくった

StyleStats is 何?

StyleStatsの説明は下記をご覧いただくのが一番早いかと思います。

html5experts.jp

簡単に言うと@t32kさんが作ったCSSを解析するツールです。
先週のYAPC::Asia Tokyo 2015の2日目でLTをなさっていたっぽいので
知っている方もいるかと思います。

経緯

今回StyleStatsをRubyで作ることになった経緯は下記です。

こんなことをつぶやいていたので、おっ!と思って反応してみた

そしたらお願いされたので30秒くらい考えて面白そうだと思ったので
頑張ってみる旨を伝えた。
8月中に公開できればいいかなという感じで考えていた。

t32kさんとは前職で一時期一緒のチームで働いていたり等の関わりがありました。

作る際に注意した点

  • CLIツールなので出来るだけ他のGemに依存しないようにしたい。
  • できる限り本家のStyleStatsと同じオプション等を指定して使えるようにしたい。

特に注意した点は上記の2つ

gem依存問題に関して

今回実装する際の大きなポイントが

  • CSSを解析する必要があるのでCSSをパースする
  • URLを渡された際にはHTMLをパースしてSTYLEタグとLINKタグからstylesheetを取得する

上記の2点がありそれを行うgemを使うか自前で実装するかする必要があった。
自前で実装するにしては結構な時間が必要になるなと思ったのでgemを使うことに決めた。

CSSのパースに関しては premailer/css_parser · GitHub こちらのgemくらいしか
要件を満たしてくれそうなやつがなかったので選択。

HTMLのパースに関してはNokogiriが有名ですがNokogiriはインストールする際に
libxmlのエラーが発生するというよく見かける問題があるので、
そこで諦められるのは嫌だったので他のgemを探したら YorickPeterse/oga · GitHub こちらのgemが良さそうだったので選択した。
(最初はNokogiriを使っていた)

作ってみての感想

出来たのがこちら
最初はgem名もstylestatsで作っていたが、本家とこのgemを入れた際に面倒くさいなと思って
Rubyっぽくstyle_statsにした。

optionのほとんどが未実装の状態だがt32kさんに確認してもらったら
どんどんリリースしちゃっていいよって感じだったのでリリースすることにした。

github.com

  • 作ってみて気づいたがStyleStatsは思っていた以上に高機能で良くできていた。
  • Rubyだったので文字の色は赤くしてみた。
  • まだoptionはformatしか指定出来ない。
  • optionは今後のversion upで対応していく。
  • CSSにそんなに詳しくなかったが、CSSのParserを自作出来るのでは?と思える程度には理解出来た。気がする
  • 作ったけど、クソコードな部分が多いので、v1.0.0までに全部書きかわる可能性があるかもしれない
  • 楽しかった!

最後に

このような機会を与えて頂き有り難く思います。
実は勉強会かtwitterでStyleStatsを知った時に下記のような感想を持っていました。
なのでとても楽しく開発できたし、少しても関われることができありがたいという思いです。

golangのフレームワークrevelを使用して掲示板っぽいものを作ってみる

はじめに

今回は golang の revel framework を使用して掲示板っぽいやつを作ってみる。
掲示板っぽいと言っても基本的には APIJson を返すことにする。
しかし html を返す場合もやることはほぼ変わらない。

今回作成する API は一般的な掲示板でいうスレッドは存在せず
レスのみを扱う。(今後スレッド対応やログイン対応等を行っていくかもしれない)
endpoint は下記のようにする

http method path 説明
GET /comments 一覧
GET /comments/:id 詳細
POST /comments 登録
DELETE /comments/:id 削除

revel is 何 ?

revel は高機能な重量級の framework で Ruby で言うと sinatra というより
Rails に近い framework になります。

環境

$ go version
go version go1.4.2
$ mysql --version
mysql  Ver 14.14 Distrib 5.6.13

revel を install する

下記で install できる

$ go get github.com/revel/revel # revel framework を取得
$ go get github.com/revel/cmd/revel # revel command を取得

プロジェクトを作成してみる

とりあえずハロワが表示されるとこまで進める。

$ revel new github.com/shiro16/golang-bbs # rails new みたいなやつ
~
~ revel! http://revel.github.io
~
Your application is ready:
   ${GOPATH}/src/github.com/shiro16/golang-bbs

You can run it with:
   revel run github.com/shiro16/golang-bbs
$ cd ${GOPATH}/src/github.com/shiro16/golang-bbs
$ revel run # applicaton の起動

これで「http://localhost:9000/」にアクセスすると
「It works!」が表示される。

API の endpoint を定義する

routing を追加する

routing は「config/routes」で管理されている
下記のように編集する。
今回の Api の path は「/api/v1」をベースにする

GET     /                                       App.Index
+ GET     /api/v1/comments                        ApiV1Comments.Index
+ GET     /api/v1/comments/:id                    ApiV1Comments.Show
+ POST    /api/v1/comments                        ApiV1Comments.Create
+ DELETE  /api/v1/comments/:id                    ApiV1Comments.Delete
controller を作成する

今回は api で共通して使うであろう Error 処理等をまとめて記述する
ベースとなる controller も作成しておく

// app/controllers/api/v1/v1.go
package controllers

import (
        "github.com/revel/revel"
        "github.com/shiro16/golang-bbs/app/utils"
        "net/http"
)

// 埋め込みによって revel.Controller をラップした ApiV1Controller を定義する
type ApiV1Controller struct {
        *revel.Controller
}

// エラーの際に返す Json 用の構造体
type ErrorResponse struct {
        Code    int    `json:"code"`
        Message string `json:"message"`
}

// 正常な際に返す Json 用の構造体(今回は1種類で統一する)
type Response struct {
        Results interface{} `json:"results"`
}

// 引数として渡されて interface にリクエストの Json の値を格納する
func (c *ApiV1Controller) BindParams(s interface{}) error {
        return utils.JsonDecode(c.Request.Body, s)
}

// Bad Request Error を返すやつ
func (c *ApiV1Controller) HandleBadRequestError(s string) revel.Result {
        c.Response.Status = http.StatusBadRequest
        r := ErrorResponse{c.Response.Status, s}
        return c.RenderJson(r)
}

// Not Found Error を返すやつ
func (c *ApiV1Controller) HandleNotFoundError(s string) revel.Result {
        c.Response.Status = http.StatusNotFound
        r := ErrorResponse{c.Response.Status, s}
        return c.RenderJson(r)
}

// Internal Server Error を返すやつ
func (c *ApiV1Controller) HandleInternalServerError(s string) revel.Result {
        c.Response.Status = http.StatusInternalServerError
        r := ErrorResponse{c.Response.Status, s}
        return c.RenderJson(r)
}

これでベースとなる処理が完了したので comments controller を作成していく

// app/controllers/api/v1/comments.go
package controllers

import (
        "github.com/revel/revel"
        "github.com/shiro16/golang-bbs/app/controllers"
)

type ApiV1Comments struct {
        ApiV1Controller
}

func (c ApiV1Comments) Index() revel.Result {
        r := Response{"index"}
        return c.RenderJson(r)
}

func (c ApiV1Comments) Show(id int) revel.Result {
        r := Response{"show"}
        return c.RenderJson(r)
}

func (c ApiV1Comments) Create() revel.Result {
        r := Response{"create"}
        return c.RenderJson(r)
}

func (c ApiV1Comments) Delete(id int) revel.Result {
        r := Response{"delete"}
        return c.RenderJson(r)
}

これで controller の作成は完了。
DB へ接続してデータを取得する等は次で行います。
実際に起動してみると下記のようになるかと。

$ revel run github.com/shiro16/golang-bbs

# 別窓等で
$ curl http://localhost:9000/api/v1/comments
{
  "results": "index"
}
$ curl http://localhost:9000/api/v1/comments/1
{
  "results": "show"
}
$ curl -X POST http://localhost:9000/api/v1/comments
{
  "results": "create"
}
$ curl -X DELETE http://localhost:9000/api/v1/comments/1
{
  "results": "delete"
}

DB 周りの処理を作成する

現状 ORM が revel には無い為自分で好きなものを使う。
revel の samples では gorp が使われているが
今回は gorm を使用する。

validation には validator を使用する。

DB の接続情報を追加

golang_bbs_development」という名前の DB 名にしています。

// conf/app.conf
[dev]
mode.dev = true
results.pretty = true
watch = true
watcher.mode = "normal"
log.trace.output = off
log.info.output  = stderr
log.warn.output  = stderr
log.error.output = stderr
db.info = "root@/golang_bbs_development?charset=utf8&parseTime=True
model を作成する

gorm を使用するので取得する
validator も取得しておく

$ go get github.com/jinzhu/gorm
$ go get gopkg.in/validator.v2

comment model を作成

// app/models/comment.go 
package models

import (
        "time"
)

type Comment struct {
        ID        uint64     `gorm:"primary_key" json:"id"`
        Nickname  string     `sql:"size:64" json:"nickname" validate:"max=64"`
        Body      string     `sql:"size:255" json:"body" validate:"min=1,max=255"`
        CreatedAt time.Time  `json:"created_at"`
        UpdatedAt time.Time  `json:"updated_at"`
        DeletedAt *time.Time `json:"deleted_at"`
}

DB への接続などの初期化処理を作成

// app/controllers/gorm.go
package controllers

import (
        _ "github.com/go-sql-driver/mysql"
        "github.com/jinzhu/gorm"
        "github.com/revel/revel"
        "github.com/shiro16/golang-bbs/app/models"
        "log"
)

var DB *gorm.DB

func InitDB() {
        db, err := gorm.Open("mysql", dbInfoString())
        if err != nil {
                log.Panicf("Failed to connect to database: %v\n", err)
        }

        db.DB()
        db.AutoMigrate(&models.Comment{}) # ここで table の作成を行っている
        DB = &db
}

func dbInfoString() string {
        s, b := revel.Config.String("db.info")
        if !b {
                log.Panicf("database info not found")
        }

        return s
}

上記を呼び出す処理を追記

// app/init.go
- import "github.com/revel/revel"
+ import(
+         "github.com/revel/revel"
+         "github.com/shiro16/golang-bbs/app/controllers"
+ )

func init() {
....
+ revel.OnAppStart(controllers.InitDB) // 28行目くらいに
}

DB 周りの処理の作成が完了し、
残すは作成した model を controller で実際に使用する処理を残すのみ

controller で model を使用する

comments controller を編集していく

package controllers

import (
	"github.com/revel/revel"
+ 	"github.com/shiro16/golang-bbs/app/controllers"
+ 	"github.com/shiro16/golang-bbs/app/models"
+ 	"gopkg.in/validator.v2"
)

type ApiV1Comments struct {
	ApiV1Controller
}

func (c ApiV1Comments) Index() revel.Result {
+ 	comments := []models.Comment{}

+ 	if err := controllers.DB.Order("id desc").Find(&comments).Error; err != nil {
+ 		return c.HandleInternalServerError("Record Find Failure")
+ 	}

+ 	r := Response{comments}
- 	r := Response{"index"}
	return c.RenderJson(r)
}

func (c ApiV1Comments) Show(id int) revel.Result {
+ 	comment := &models.Comment{}

+ 	if err := controllers.DB.First(&comment, id).Error; err != nil {
+ 		return c.HandleNotFoundError(err.Error())
+ 	}

+ 	r := Response{comment}
- 	r := Response{"show"}
	return c.RenderJson(r)
}

func (c ApiV1Comments) Create() revel.Result {
+ 	comment := &models.Comment{}

+ 	if err := c.BindParams(comment); err != nil {
+ 		return c.HandleBadRequestError(err.Error())
+ 	}

+ 	if err := validator.Validate(comment); err != nil {
+ 		return c.HandleBadRequestError(err.Error())
+ 	}

+ 	if err := controllers.DB.Create(comment).Error; err != nil {
+ 		return c.HandleInternalServerError("Record Create Failure")
+ 	}

+ 	r := Response{comment}
- 	r := Response{"create"}
	return c.RenderJson(r)
}

func (c ApiV1Comments) Delete(id int) revel.Result {
+ 	comment := models.Comment{}

+ 	if err := controllers.DB.First(&comment, id).Error; err != nil {
+ 		return c.HandleNotFoundError(err.Error())
+ 	}

+ 	if err := controllers.DB.Delete(&comment).Error; err != nil {
+ 		return c.HandleInternalServerError("Record Delete Failure")
+ 	}

+ 	r := Response{"success"}
- 	r := Response{"delete"}
	return c.RenderJson(r)
}

これで各 endpoint にアクセスしてみる

$ revel run github.com/shiro16/golang-bbs
# 別窓等で
$ curl -H "Content-type: application/json" -X POST -d '{"nickname":"shiro16", "body":"test comment"}' http://localhost:9000/api/v1/comments
{
  "results": {
    "id": 1,
    "nickname": "shiro16",
    "body": "test comment",
    "created_at": "2015-08-13T21:20:46.681910871+09:00",
    "updated_at": "2015-08-13T21:20:46.681910871+09:00",
    "deleted_at": null
  }
}
# validation がちゃんと機能しているかチェック
$ curl -H "Content-type: application/json" -X POST -d '{"nickname":"shiro16", "body":""}' http://localhost:9000/api/v1/comments
{
  "code": 400,
  "message": "Body: less than min"
}
$ curl http://localhost:9000/api/v1/comments
{
  "results": [
    {
      "id": 1,
      "nickname": "shiro16",
      "body": "test comment",
      "created_at": "2015-08-13T12:20:47Z",
      "updated_at": "2015-08-13T12:20:47Z",
      "deleted_at": null
    }
  ]
}
$ curl http://localhost:9000/api/v1/comments/1
{
  "results": {
    "id": 1,
    "nickname": "shiro16",
    "body": "test comment",
    "created_at": "2015-08-13T12:20:47Z",
    "updated_at": "2015-08-13T12:20:47Z",
    "deleted_at": null
  }
}
$ curl http://localhost:9000/api/v1/comments/2 
{
  "code": 404,
  "message": "record not found"
}
$ curl -X DELETE http://localhost:9000/api/v1/comments/1
{
  "results": "success"
}

まとめ

こんな感じで雑な部分もありますが revel を使って基本的な処理の作成が完了しました。
validation に関しては model にてチェックを行った方がいいかと思いますが、
今回は時間の関係で controller にて行っています。
今回作成したものはこちらで公開しています。
API 以外の処理も追加していますので参考にしてください。

今回の説明以外の詳しい内容は下記を参考にするといいと思います。
とくに sample を配布しているので「go get github.com/revel/samples」で取得して
見てみるといいかと思います。
Welcome to Revel, the Web Framework for Go!

GMOペパボに入社しました

はじめに

タイトルの通りGMOペパボに入社しました。
7月1日から働き始めてるので実際には約10日程たってます。
なんで転職したかとか周りの人にあまり説明+お知らせをしていないので
それ用の記事と現在の自分の考えをメモっておくように記事を書いてます。

転職しようと思った訳

前職もweb系のエンジニアとしてRuby書いていたわけですが、
今年(2015年)の初め頃から、
社内でRubyRailsを使用したサービス開発で学びや技術的な刺激を受ける機会が激減したと感じ始めた。
激減した理由としては当時1年半くらいRuby書いていて、
社内でRubyをメインで使用して開発しているエンジニアとしては経験年数が長くなり知識が増えたことが
原因だと思っている。

その為、技術的な刺激を求め社外のRuby系の勉強会などに参加したりしていたが
普段自分が何気なく書いているコードに関しても、もっと良い方法があるんじゃないか?
とか色々考えるようになり、転職をしようかと考えた。

ただ、すぐに転職活動を行わなかったのはまだ当時の会社でやりたいことがあったこと
担当していたサービスを任せられるエンジニアを育成すること
その他色々な理由で2015年後半に転職活動をしようと思っていたのです。

2015年後半…?

「え、おまえ後半入って速攻で転職してるやん」ってなると思うのですが
理由は色々あるのです。
タイミング的なあれだったり、ゴールデンウィークからの社会復帰に失敗したからとか
大人の事情とか、そういうものだと思ってください。
悪いことはしてません

なんでペパボ?

転職する一番の理由がRubyの凄いエンジニアのいる会社で働いて自分の技術力をもっと磨きたいなので
とりあえず頭に浮かんだ会社が2社ありその片方がペパボでした。

pepabo.com

とりあえず話聞きたいと思い上記の制度を利用しお話を聞きに行くことに
@kentaro,@hsbtのお二人を指名させていただきました。
現状抱えている課題や今後人が増えていく上で起こりえるであろう課題等、本当に細かいことまでお答えしていただき
当時はそれなりに人数の多い会社で働いていたのでRubyでの開発以外のそういう部分の課題の解決にも携われたら面白いだろうなと思い受けてみたら、ありがたいことに採用して頂いたという経緯です。
頭に浮かんだもう一社についてはお祈…

実際どうなの?

1,2週間働いただけなのでまだまだわからないこと多めですが、
3日目くらいからSlackで@udzuraさんと技術的な会話が出来たり、
技術的な刺激を受ける機会は多いなと感じている。
やらなければいけないことや技術的な課題も多々あるのでやり甲斐も凄く感じていて
楽しくなりそうな気配はしている。

さいごに

こんな経緯で今回GMOペパボで働くことになりました。
今後に関して当分はRubyでサービスの開発を行っていくとのがメインになるかと思いますが、
解決したい課題も多々あるので、
インフラや基盤の方でサービスを作るエンジニアを支えるという立場を経験するのも面白そうだなという思いがあります。

ペパボに興味がある方はペパランチョンで話を聞きに来るも良し下記から申し込むのも良しかと思います。

pepabo.com


このエントリーはもし誰かに怒られたら消します

PHPでmemcachedに保存されたセッション情報をRubyで扱う

経緯

PHPで作成されたシステムで発行されたsession idを元に
Rubyからそのセッションに格納されている情報を知りたいかもしれないという状況が
あるかもしれない。
無事社会復帰を果たした会社で話題になったので調べてみた。
前々職の際にPHPmemcachedに関して結構調査してたりしたので大分行けそうな気はしていた。
途中経過をメモ

環境

php.iniにて下記のように設定

session.save_handler = memcached
session.save_path = "localhost:11211"

memcachedlocalhostのport11211で動かしています。

PHPにてsessionに何かを保存する

今回は適当なデータを格納するスクリプトを作成
session.phpというファイル名で保存

#!/usr/bin/php
<?php
session_start();

echo $_SESSION['count'] = rand(1, 100);
$_SESSION['testkey'] = 'hoge';

echo session_id();
echo "\n"
?>

実行してsession idを確認してみる

$ php session.php
66
f1sk3iprn07l2vu6a5e09dqd86 # これがデータを格納したsession id

memcachedにどのようなデータが格納されているか確認する

memcached-toolっていうコマンドを使ってmemcachedをいじれたりする

$ memcached-tool localhost:11211 dump
Dumping memcache contents
  Number of buckets: 1
  Number of items  : 1
Dumping bucket 3 - 1 total items
add memc.sess.key.f1sk3iprn07l2vu6a5e09dqd86 0 1435838226 30
count|i:66;testkey|s:4:"hoge";

先ほどのphpスクリプトを実行して表示されたsession idの前に
「memc.sess.key.」が付いている。
ここをみるとPHPが勝手に付ける仕様のようだ。

セッションに格納していたデータは「count|i:66;testkey|s:4:"hoge";」このようにシリアライズ
されて保存されている。
phpの標準のserialize関数ではなくmemcachedの独自のシリアライズっぽい?

Rubyで取得してみる

memcachedのデータを扱うgemはそのままevan/memcached · GitHubを使ってみたのですが、ここでエラーが出たので断念。
phpで独自のシリアライズ保存されたデータだからそりゃそうなのだが…

ということでmperham/dalli · GitHubこちらを試してみた。

$ gem install dalli
$ irb
irb(main)> require 'dalli'
irb(main)> dalli_client = Dalli::Client.new('localhost:11211')
irb(main)> dalli_client.get "memc.sess.key.f1sk3iprn07l2vu6a5e09dqd86"
=> "count|i:66;testkey|s:4:\"hoge\";"

これでシリアライズされたデータが取得出来た。

まとめ

PHPで生成されたsession idを元にsessionに格納されたデータ(シリアライズされたデータ)を
取得することに成功した。

とりあえず今日はここまで
TODOは以下

しかし久しぶりにPHP書いたな

技術書をまとめ買いしたよ

ネタがあまりないので雑談
なぜか2週間くらい暇になり、
なぜか会社でRubyを教えたm君にAmazonギフト券を貰ったので
3冊ほどまとめ買いしたものと最近読んだ1冊をまとめておく。
本当はkindleのセールの際に買おうと思ったのだがセールが終わってしまい買えなかったので
このタイミングで買った

買った本

自分はインフラエンジニアではないが、いろいろやったりしているので
一般的なインフラエンジニアと認識の違いがないかを知りたかったので基本的な本を選んでみた。
大きな認識の違いがなかったが、細かい部分を知れたことと幾つかのコマンドやオプションを新しく知れたのでよかった。

インフラ/ネットワークエンジニアのためのネットワーク技術&設計入門

インフラ/ネットワークエンジニアのためのネットワーク技術&設計入門

ネットワーク関連があまり得意ではないので
勉強したくて読んでいる(もう少しで読み終わる)
図解が多くイメージがしやすい。

Amazon Web Services パターン別構築・運用ガイド

Amazon Web Services パターン別構築・運用ガイド

AWSを知らないで許されるのは小学生までだよねー」と最近思っている(危機感を感じている)ので
買った上記のネットワーク本が終わったら読む予定

クラウドつながりでOpenStackも面白そうなので買った。

まとめ

インフラの本ばかりになってしまいましたが、
インフラエンジニアに転向する訳ではありません。
(いずれそっち方向に進むのは結構ありだと思っているけど)
Serverspec本を書い忘れていたので、
上記の本を読み終わったらServerspec本を購入する予定

golangのフレームワークkochaを使ってみた その2

shiro-16.hatenablog.com
こちらの続き

modelを作成する

今回はmysqlを使用します。
今回作成したtableは下記です。

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

modelを自動生成する。

$ kocha g model user
    create directory app/model
              create app/model/user.go
    create directory db
              create db/config.go

dbのconfigの変更
デフォルトではsqlite3の設定が記述してあるようです。

$ vi db/config.go
- Driver: kocha.Getenv("KOCHA_DB_DRIVER", "sqlite3"),
- DSN:    kocha.Getenv("KOCHA_DB_DSN", filepath.Join("db", "db.sqlite3"))
+ Driver: kocha.Getenv("KOCHA_DB_DRIVER", "mysql"),
+ DSN:    DSN:    kocha.Getenv("KOCHA_DB_DSN", "user:password@/kocha"), // 各時の環境に合わせてuser名とpassword,db名を設定

modelを変更

$ vi app/model/user.go
- Id int64 `db:"pk" json:"id"`
+ Id   int64
+ Name string

controllerを作成

$ kocha g controller users
              create app/controller/users.go
              create app/view/users.html

controllerを変更

$ vi app/controller/users.go
func (us *Users) GET(c *kocha.Context) error {
        var users []model.User
        err := db.Get("default").Select(&users)
        if err != nil {
                return c.RenderError(500, nil, nil)
        }

        return c.Render(map[string]interface{}{
                "ControllerName": "Users",
                "users": users,
        })
}

viewを変更

$ vi app/view/users.html
<h1>This is Users</h1>

<ul>
  {{ range $.users }}
    <li>{{ .Name }}</li>
  {{ end }}
</ul>

動作確認

$ kocha run
kocha: you can be setting for your app by the following environment variables at the time of launching the app:

    KOCHA_ADDR="127.0.0.1:9100"
    KOCHA_DB_DRIVER="mysql"
    KOCHA_DB_DSN="user:password@/kocha"

Starting...

Listening on 127.0.0.1:9100
Server PID: xxxx

上記が表示されたら前回同様http://127.0.0.1:9100/で「Welcome to Kocha」が表示され
http://localhost:9100/usersで「This is Users」 + 「user.nameのリスト」が表示されます。

Routingに関しての補足

routingに関してはcontrollerを作成したときに自動で追記されていました。

var routes = RouteTable{
        {
                Name:       "root",
                Path:       "/",
                Controller: &controller.Root{},
        }, {
                Name:       "users",
                Path:       "/users",
                Controller: &controller.Users{},
        },
}

疑問 + その3に関して

1.Railsのshowとかその他get methodを追加したい場合とかは
各controllerのGETで処理を分岐させたりするのだろうか?

2.Migrationの機能もあるようなのでその辺の使い方

上記2点をその3で書ければいいかなと思っていますが、
その3に関しては未定です...mm

golangのフレームワークkochaを使ってみた その1

経緯

golangを学習し始めて2週間〜3週間くらい、
ちょっとわかってきたので
そろそろフレームワーク使ってみたいなと思い調べ始めた。
ちなみにgolangの学習は下記のページや書籍など

A Tour of Go
web上でさくっと出来る

Effective Go — プログラミング言語 Go ドキュメント v0.1 documentation
ドキュメントてきな

Go For Perl Mongers
すごくためになった

taizo - Qiita
会社の先輩のqiita初心者がつまずく点がいっぱいまとまってあった

Amazon.co.jp: 基礎からわかる Go言語: 古川 昇: 本
基本的なことはこれ読めばわかる気がする

kochaを選択した理由

Kocha web application framework for Go

サンプルをチラっと見た感じだとRailsっぽいじゃんという理由で要はなんとなくです。
強いて言うならRails好きだからです。ちなみに和製らしいです。

ほかにもmartini,revelあたりは使ってみようと思っているので後々記事を書くかもしれない。

serverを立ち上げる

$ go get github.com/naoina/kocha

これでkochaを取得してくる

$ go get github.com/naoina/kocha/cmd/...

これでkochaコマンドが使える

今回はアプリケーション名をexample-kochaで作成します。
※GOPATH部分は各自の環境によってかわります。

$ kocha new example-kocha
    create directory /GOPATH/src/example-kocha/app/controller
              create /GOPATH/src/example-kocha/app/controller/root.go
    create directory /GOPATH/src/example-kocha/app/view/error
              create /GOPATH/src/example-kocha/app/view/error/404.html.tmpl
              create /GOPATH/src/example-kocha/app/view/error/500.html.tmpl
    create directory /GOPATH/src/example-kocha/app/view/layout
              create /GOPATH/src/example-kocha/app/view/layout/app.html.tmpl
              create /GOPATH/src/example-kocha/app/view/root.html.tmpl
    create directory /GOPATH/src/example-kocha/config
              create /GOPATH/src/example-kocha/config/app.go
              create /GOPATH/src/example-kocha/config/routes.go
              create /GOPATH/src/example-kocha/main.go
    create directory /GOPATH/src/example-kocha/public
              create /GOPATH/src/example-kocha/public/robots.txt

初めてrails newした時と同じ気持ちを味わえましたね!

serverを立ち上げる

$ cd /GOPATH/src/example-kocha
$ kocha run
kocha: you can be setting for your app by the following environment variables at the time of launching the app:

    KOCHA_ADDR="127.0.0.1:9100"

Starting...

Listening on 127.0.0.1:9100
Server PID: xxxxx

これでhttp://127.0.0.1:9100/にアクセスすると
「Welcome to Kocha」が表示されると思います。

処理の流れをざっくりと追う

config/routes.go内で下記のようにrouting周りの記述されている。
routingを追加する場合はここに追記する感じになるっぽい

var routes = RouteTable{
        {
                Name:       "root",
                Path:       "/",
                Controller: &controller.Root{},
        },
}

Path(今回の場合は/)へのアクセスの際にControllerに格納された各controllerを呼ぶ。

Controllerのinterfaceがどのようになっているのかは下記を参照
https://github.com/naoina/kocha/blob/master/controller.go#L27-L64

HTTPの各methodでcontroller側のどのmethodを呼ぶかを解決しているのが下記
https://github.com/naoina/kocha/blob/master/router.go#L93-L121

今回の場合はapp/controller/root.goが対象のcontrollerになり
GETでアクセスしているので下記のmethodが呼ばれる。

func (r *Root) GET(c *kocha.Context) error {
        return c.Render(map[string]interface{}{
                "ControllerName": "Root",
        })
}

表示されているviewはapp/view/root.html.tmpl

ざっくりしすぎですがこんな感じ

今後

controller, model周りの実装もあるのですが
長くなりそうなのでその2にて書いていきます。

kochaのソースを読むと構成とかとても勉強になる
間違いがあればご指摘いただけますと幸いです。