write ahead log

ロールフォワード用

w3mを使ってみる

テキストブラウザって意外と便利ですね.

使ったことなかったけど.

vim, emacsのバインドである程度動くのでそれほど抵抗なく使えると思います.

インストール

こんなものを使う人はとっくに調べて入れているでしょうが...

msys2環境では以下で入れました.

$ pacman -S w3m

起動

$ w3m [URL]

キー操作

よく使うのだけメモしておく.

カーソル移動

キー 動作
j 下へ
k 上へ
h 左へ
l 右へ
space 1ページ送る
b 1ページ戻す

ページ移動

キー 動作
B 戻る
(リンク上で) Enter リンク先へ移動
U URL指定エリア表示(その後Enterで移動)
R リロード

タブ

キー 動作
T 新しいタブを作成
C-q 現在のタブを閉じる
{ 左のタブへ移動
} 右のタブへ移動

ブックマーク

キー 動作
ESC + a 現在のページをブックマーク
ESC + b ブックマークリストを表示

終了

キー 動作
q 終了
Q 終了 (確認無し)

その他

キー 動作
C-c 現在の操作をキャンセル
u 現在のページのURLを表示

vim-table-modeを使う

markdown-vimを使ってみたけど, 高機能すぎたのでもうちょっとシンプルなものが欲しいなぁと.

つまるところGFMのテーブルを書くのさえ楽になればmarkdown書くこと自体はそんなに苦痛じゃない.
(っていうか, そういうシンプルな記法のはずだし)

良いのないかなぁと思っていたらvim-table-modeというのがちょこちょこお勧めされていた.

これがかなりいい.

インストール

Vundle使いました.

vimrcを変更.

" Plugin設定
" vundle.vimを使う
set rtp+=~/.vim/bundle/Vundle.vim/
call vundle#begin()
Plugin 'VundleVim/Vundle.vim'

Plugin 'dhruvasagar/vim-table-mode'  " <= ここ
let g:table_mode_corner="|"          " <= ここ

call vundle#end()

全部見たい方は僕のvimrcはここにあります.

vimを開いて以下を実行.

:BundleInstall

使い方

例えば以下の様な文書を書いているときに

| col1 | col2 | col3 |
|--|--|--|
|retsu| col2 | col3 |

ノーマルモードで

\tm

とすると.(table modeの略ですよね.たぶん)

| col1  | col2 | col3 |
|-------|------|------|
| retsu | col2 | col3 |

こんな感じできれいになります.

もう一度\tmとすれば通常の編集に戻りますし, かなりいいです.

これは末永く使わせてもらいそうです.

golangでzipを作る

archive/zipパッケージなるものがあります.

The Go Programming Language - Package zip

使い方

zipアーカイブは複数のファイルが入るので, 1つのio.Writerから複数ファイルのwriterを作成する構成をとっているようです.

  • zipWriter(io.Writerから作成)
    • writer(zipWriterから作成)
    • writer(zipWriterから作成)
    • writer(zipWriterから作成)

なので手順としては

  1. zip.NewWriterへio.Writerを渡してzipWriterを作る
  2. zipWriterから各ファイルのwriterを作る
  3. 作ったwriterへファイルの内容を書き込む

となります.

サンプルコード

先に動作を.

こんなフォルダを.

test/
├── bar.txt
├── foo.txt
└── test.txt

こんな感じで圧縮できます.

$ ./sample.exe test.zip test/*

コードは以下の通り.

package main

import (
    "archive/zip"
    "fmt"
    "io"
    "os"
)

func main() {
    if len(os.Args) < 3 {
        fmt.Println("Usage: %s [zipname] [src] [src2] ...")
        return
    }

    dest, err := os.Create(os.Args[1])
    if err != nil {
        panic(err)
    }

    zipWriter := zip.NewWriter(dest)
    defer zipWriter.Close()

    for _, s := range os.Args[2:] {
        if err := addToZip(s, zipWriter); err != nil {
            panic(err)
        }
    }
}

func addToZip(filename string, zipWriter *zip.Writer) error {
    src, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer src.Close()

    writer, err := zipWriter.Create(filename)
    if err != nil {
        return err
    }

    _, err = io.Copy(writer, src)
    if err != nil {
        return err
    }

    return nil
}

感想

たった50行程度でいつも使っているzipができるというのはなかなか感慨深いものがありました.

今度はzipの構成とか調べてみようかな.

余談

io.Writer使ってるんで当たり前ですが, http経由で渡すとか, そんなのも割と簡単に書けます.

package main

import (
    "archive/zip"
    "io"
    "net/http"
    "os"
)

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/zip")
    w.Header().Set("Content-Disposition", "attachment; filename=test.zip")

    zipWriter := zip.NewWriter(w)
    defer zipWriter.Close()

    for _, s := range os.Args[1:] {
        if err := addToZip(s, zipWriter); err != nil {
            panic(err)
        }
    }
}

func addToZip(filename string, zipWriter *zip.Writer) error {
    src, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer src.Close()

    writer, err := zipWriter.Create(filename)
    if err != nil {
        return err
    }

    _, err = io.Copy(writer, src)
    if err != nil {
        return err
    }

    return nil
}
$ ./sample.exe test/*

curlでRESTっぽくやる時に使うオプション

毎回調べるのめんどくさい.

HTTP Methodを切り替える

Xオプションを使います.

$ curl -X PUT [hostname]

パラメータを与える

dオプションを使います. GETでもPOSTでも使えるっぽいです.

$ curl -d arg=param1 -d arg1=param2 [hostname]

ボディへ直接データを入れる

data-binaryオプションを使います.

以下の例では「raw string」を直接送信します.

$ curl --data-binary "raw string" [hostname]

ファイル指定もできます.

@ファイル名で指定です.

$ curl --data-binary @filename [hostname]

HTTPヘッダを追加する

Hオプションを使います.

上記例と組み合わせると, xmlをapplication/xmlヘッダ付きで送信できます.

$ curl -H "Content-Type: application/xml" --data-binary @test.xml [hostname]

ベーシック認証

uオプションを使います.

$ curl -u user:password [hostname]

golangでメールを送る

golangのメールパッケージnet/smtpはなんというか, 非常に薄いです.

他のパッケージも同じで思想がハッキリ出ていますが, smtpパッケージも例外ではありません.

おかげでメールの仕組みを学ぶことが出来ました.

ただまぁ, メールは外部パッケージ使ったほうがいいかも....

できるだけ標準を使う志向の私もマルチバイトの処理がないのは流石に....

SMTPで平文 + ASCIIでメールを送る

一番泥臭いやり方.

日本語を入れたら文字化けした.

UTF-8を使った方法を別に学ぶ必要がありそう.

package main

import (
    "bytes"
    "log"
    "net/smtp"
)

const (
    // メールサーバのアドレス
    mailSvrAddr = "mail.svr.co.jp:25"
    // 送信者のメールアドレス
    mailSenderAddr = "twinbird@hoge.co.jp"
    // 受信者のメールアドレス
    mailRecepterAddr = "twinbird@hoge.co.jp"
    // CC:受信者のメールアドレス
    mailCCRecepterAddr = "twinbird@hoge.co.jp"
    // 件名
    mailSubject = "mail subject"
    // 本文
    mailText = `
  this is a 
  test mail
  `
)

func main() {
    c, err := smtp.Dial(mailSvrAddr)
    if err != nil {
        log.Fatal(err)
    }
    // メール送信者(送信元)の設定
    c.Mail(mailSenderAddr)
    // メール受信者(送信先)の設定
    c.Rcpt(mailRecepterAddr)
    // CCでのメール受信者(CC送信先)の設定
    c.Rcpt(mailCCRecepterAddr)

    // メールのボディを作成
    wc, err := c.Data()
    if err != nil {
        log.Fatal(err)
    }
    defer wc.Close()

    // バッファでボディの内容を構成していく
    buf := bytes.NewBufferString("")

    // Toを指定
    buf.WriteString("To:" + mailRecepterAddr)
    buf.WriteString("\r\n") // To終わり
    // CCを指定
    buf.WriteString("Cc:" + mailCCRecepterAddr)
    buf.WriteString("\r\n") // CC終わり

    // タイトル(件名)指定
    buf.WriteString("Subject:" + mailSubject)
    buf.WriteString("\r\n") // タイトル終わり

    // 送信元の指定
    buf.WriteString("From:" + mailSenderAddr)
    buf.WriteString("\r\n") // From終わり

    // メールヘッダ終わり
    buf.WriteString("\r\n")

    // 本文指定
    buf.WriteString(mailText)
    if _, err = buf.WriteTo(wc); err != nil {
        log.Fatal(err)
    }

    // メールセッション終了
    c.Quit()
}

TLS認証使ってメールを送る

複数の送信先が必要な場合はSendMailメソッドで楽したらダメな様です.

結局↑のやり方 + Authを使うみたい.

package main

import (
    "log"
    "net/smtp"
)

func main() {
    // 認証情報作成
    auth := smtp.PlainAuth(
        "",                 // identity(って何?これだけわからん)
        "user@example.com", // ユーザ名
        "password",         // パスワード
        "mail.example.com") // ホスト名

    to := []string{"twinbird@example.net"}
    msg := []byte("To: twinbird@example.net\r\n" +
        "Subject: Title\r\n" +
        "\r\n" +
        "This is the email body.\r\n")

    // メール送信
    err := smtp.SendMail(
        "mail.example.com:25", // 接続先メールサーバ
        auth,                 // 認証情報
        "sender@example.org", // 送信元アドレス
        to,                   // 送信先アドレス
        msg)                  // メッセージ
    if err != nil {
        log.Fatal(err)
    }
}

その他のパッケージを使う

githubで紹介されています

参考

smtp - The Go Programing Language

メールの仕組み - Geekなぺーじ

golang/go - Sending Mail

golang(net/http)でBasic認証する

net/httpでBasic認証を使ったのでスニペットとしてメモ.

package main

import (
    "fmt"
    "net/http"
)

const (
    basicAuthUser     = "user"
    basicAuthPassword = "password"
)

func main() {
    http.HandleFunc("/need", needAuthHandler)
    http.HandleFunc("/unneed", unneedAuthHandler)

    http.ListenAndServe(":8080", nil)
}

// 認証が要るハンドラ
func needAuthHandler(w http.ResponseWriter, r *http.Request) {
    // Basic認証のヘッダ解析に失敗したらokにfalseが入るみたい
    user, pass, ok := r.BasicAuth()
    // 入力された内容のチェック
    if ok == false || user != basicAuthUser || pass != basicAuthPassword {
        w.Header().Set("WWW-Authenticate", `Basic realm="auth area"`)
        http.Error(w, "needs authenticate", http.StatusUnauthorized)
        return
    }
    fmt.Fprintln(w, "authed")
}

// 認証不要のハンドラ
func unneedAuthHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello")
}

もう少し汎化して綺麗にするならこんな感じかしら.

package main

import (
    "fmt"
    "net/http"
)

const (
    // Basic認証のUserID
    basicAuthUser     = "user"
    // Basic認証のPassword
    basicAuthPassword = "password"
)

// 認証が必要なハンドラ用の型
type basicAuthHandler func(w http.ResponseWriter, r *http.Request, u string)

func main() {
    // ラッパーかます
    http.HandleFunc("/need", needBasicAuth(needAuthHandler))
    http.HandleFunc("/unneed", unneedBasicAuth(unneedAuthHandler))

    http.ListenAndServe(":8080", nil)
}

// 認証メソッド
// 必要ならDBとかにアクセス
func basicAuthenticate(user, pass string) bool {
    if user == basicAuthUser && pass == basicAuthPassword {
        return true
    }
    return false
}

// 認証が必要な場合のラッパ
func needBasicAuth(fn basicAuthHandler) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        user, pass, ok := r.BasicAuth()
        if ok == false || basicAuthenticate(user, pass) == false {
            w.Header().Set("WWW-Authenticate", `Basic realm="auth area"`)
            http.Error(w, "needs authenticate", http.StatusUnauthorized)
            return
        }
        fn(w, r, user)
    }
}

// 認証が不要な場合のラッパ
func unneedBasicAuth(fn basicAuthHandler) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        fn(w, r, "")
    }
}

// 認証が必要なハンドラ
func needAuthHandler(w http.ResponseWriter, r *http.Request, u string) {
    fmt.Fprintln(w, "authed")
}

// 認証が不要なハンドラ
func unneedAuthHandler(w http.ResponseWriter, r *http.Request, u string) {
    fmt.Fprintln(w, "Hello")
}

goでlifegame書いた

そういや書いたことないな.と思ったので書いた.

github.com

作ってみると中々面白く, 色々なパターンを試してみたくなる.

引き込まれるというのも納得である.

バグってなけりゃいいけど.
(テストとか書いてないし)

こんな感じ

block

glider

ハチの巣

参考

ライフゲーム - Wikipedia