Quantcast
Channel: Big Sky
Viewing all 121 articles
Browse latest View live

MinGW コンパイラで argv をワイルドカード展開させない方法

$
0
0

コマンドラインから「あ*」等とワイルドカードを使用してコマンドを起動した場合、UNIX とは異なり Windows ではプロセス自身が引数を展開します(内部的にではありますが)。

main 関数の argc/argv は MSVC コンパイラであれば setargv.obj というオブジェクトファイルをリンクする事で展開される様になりますが、MinGW ではデフォルトでワイルドカード展開が実行されてしまいます。しかしながら、このワイルドカード展開は各コンパイラベンダーの実装次第という所があり、時に異なる挙動となる場合があります。

そこでワイルドカードは自前でプログラムで展開するのが一番良い訳ですが、MinGW の場合は既述の通り、デフォルトが展開動作となっています。しかし以下のたった1行のおまじないを入れる事で、引数展開が無効となります。

#include <stdio.h>

#define WIN32_LEAN_AND_MEAN  1
#include <windows.h>

int _CRT_glob = 0// おまじない

int
main(int argc, char **argv) {
  int i;
  printf("command line: %s\n", GetCommandLine());
  for (i = 0; i < argc; i++)
    printf( "argv[%d]: %s\n", i, argv[i]);
  return 0;
}

おまじないの行をコメントアウトした場合

C:\dev>a あ*
command line: a  あ*
argv[0]: a
argv[1]: あ.txt

おまじないを実行した場合

C:\dev>a あ*
command line: a  あ*
argv[0]: a
argv[1]: あ*

アドホックすぎてかなり嫌ですね。どのコンパイラでも同じ動作をさせたいのならば、GetCommandLineW()CommandLineToArgvW()を使うべきだと思います。


html をコマンドラインからパースするなら pup が便利

$
0
0

2014年でも html を解析してゴニョゴニョするなんて要件はまだまだある訳で、そんな時に便利なのが pup というコマンドです。

EricChiang/pup - GitHub

README.md pup pup is a command line tool for processing HTML. It reads from stdin, prints to stdout,...

https://github.com/EricChiang/pup

通常、こういったツールは perl や ruby、python 等で提供されランタイムがインストールされていない環境で動かすのはちょっとした手間が発生していました。しかし pup ならば golang で出来ているのでバイナリ1つあれば動かせます。

使い方は、例えばこのサイトのパーマリンクのHTMLを得たいならば

curl -s http://mattn.kaoriya.net/ | pup a.permalink

とするだけ。CSS セレクタで指定します。またテキストを得たいならば

curl -s http://mattn.kaoriya.net/ | pup a.permalink text{}

といった具合です。一応バイナリリリースもされていますが現状 Windows で色付き表示(-cフラグ)でエスケープシーケンスが表示されてしまいます。

pull-requestを送ってあるので、うまく行けばマージして貰えると思います。

追記: マージされました

Windows でも PDCurses で UTF-8 を表示出来るようにした。

$
0
0

PDCursesはその名の通り Public Domain な curses 実装な訳ですが、Windows の実装こそありますが如何ともし難い問題がありました。

PDCurses には、ワイド文字列関数とバイト列で貰う実装があります。UNIX 系アプリケーションの多くは内部文字列がほぼ UTF-8 となり、C言語で受け渡される文字列は既成事実として UTF-8 になってしまいました。

しかしながら日本の Windows ではバイト列と言えば Shift_JIS な訳で、そのまま移植してしまうと文字化けが発生します。各アプリケーション側がワイド文字列でコードを書いてくれれば良いのですが、そんな事を気にする人はまずいません。ましてや各アプリケーションが UTF-8 と MBCS (Multi Byte Character Set) の変換を行うのは酷な話です。出来れば内部文字列は UTF-8 のままで Windows でも正しく表示されて欲しいのです。

mattn/pdcurses - GitHub
https://github.com/mattn/pdcurses

という訳で pdcurses に手を入れてみました。もし pdcurses.dll をリンクしていて表示が文字化けしているアプリケーションがあれば、dll を入れ替えるだけで文字化けが解消するかと思います。

今日はこれを使って、コマンドラインベースのプレゼンテーションツールである mdp を Windows で動かしてみました。

visit1985/mdp · GitHub

A command-line based markdown presentation tool.

https://github.com/visit1985/mdp

mdp 自身は大した事をやってないので、ncurses の代わりに pdcurses をリンクする様な修正を入れただけです。

diff --git a/Makefile b/Makefile
index 63a1feb..54f8327 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@
 
 CFLAGS   = -O3 -Wall
 LDFLAGS  = -s
-LDLIBS   = -lncurses
+LDLIBS   = -lpdcurses
 OBJECTS  = cstring.o cstack.o markdown.o parser.o viewer.o mdp.o
 DESTDIR ?= /usr/bin
 
diff --git a/include/viewer.h b/include/viewer.h
index 68c7a13..f6e70fb 100644
--- a/include/viewer.h
+++ b/include/viewer.h
@@ -32,7 +32,7 @@
  *
  */
 
-#include <ncurses.h>
+#include <curses.h>
 
 #include "parser.h"
 #include "cstack.h"
diff --git a/viewer.c b/viewer.c
index ba76f2f..ed83196 100644
--- a/viewer.c
+++ b/viewer.c
@@ -22,7 +22,6 @@
  */
 
 #include <locale.h> // setlocale
-#include <ncurses.h>
 #include <stdlib.h>
 #include <string.h> // strchr
 #include <unistd.h>

sample.mdも日本語にして正しく動作する事が確認出来ました。

mdp1
mdp2
mdp3
mdp4

21 世紀のエディタである Atom を最強にする

$
0
0

Atom 最高ですね!!

でも、Atom は 21 世紀のエディタです。まだ 21 世紀になって 14 年しか経っていないので、20 世紀最強のエディタに比べてまだまだ足りない機能があるのはしょうがないですね!!

86 年後に勝負しよう!!

って感じですね。

みんなが拡張書けば 86 年がドンドン縮んでくると思うのですが、ぶっちゃけまだまだです。 なので、最強にするための拡張書いてみました。

open-vim

Atom で今開いているファイルを Vim で開く拡張です。

「あー、Vim だと xxx 出来るのに〜」

とか脳みそがまだ 20 世紀な時とか

「あー、このファイルデカすぎて Atom で編集すると重い……」

みたいなかわいそうな作業している時とか(2050 年位のマシンだとさくさくだと思いますけど!!)にお使いください。

21 世紀に戻る

command! OpenAtom !start atom %

こんな感じのを .vimrc に書いておけばいいんじゃないでしょうか?

Back To The Future !!!

p.s.

Emacsの話はしていません

参考資料: 21 世紀のエディタである Atom を最強にする - yoshiori.github.io

ネタです。ご理解下さい。

Golang 1.4 で導入される generate を一足早く試してみる

$
0
0

先日、golang の開発リポジトリに generate が入りました。

Go generate: A Proposal

The go build command automates the construction of Go programs but sometimes preliminary processing is required, processing that go build does not support.

https://docs.google.com/document/d/1V03LUfjSADDooDMhe-_K59EgpTEm3V8uvQRuNMAEnjg/edit

皆さんが期待している様な物なのかそうでないのか分かりませんが、ひとまずこの提案書を見る限り

  • 使うのはライブラリユーザではなくライブラリ作者
  • go build で自動で generate してくれる機能はない
  • shell 的なワンライナーは実行出来ない

どちらかと言うと使用するのは開発時で、バイナリを同梱する目的で golang のソースを吐くまでの手順であったり、構造体に特殊なメソッドを生やしたりという目的で使われます。また yacc のソースから golang のソースを吐くなどといった用途も考えられます。生成される物が golang のソースに限られている訳でもありません。

この特殊なメソッドを吐き出す、と聞くとどうしても generics を思い浮かべる方が多いと思いますが、上記の通り「自動では生成されない」という制限がある事から期待されている使い方は現状出来ません。

今日はこの新しく入った generate を使って、どの様な効果が得られるのかを genというツールを使って説明したいと思います。

まず以下のコード(food.go)を用意します。

//go:generate gen -force

package food

// +gen *
type Food struct {
    Name string
    Price int
}

gen というツールが +genとなっている部分を扱います。

clipperhouse/gen - GitHub

README.md What’s this? gen is a code-generation tool for Go. It’s intended to offer generics-like fu...

https://github.com/clipperhouse/gen

gen はこの識別が付いている type 宣言から便利な関数群を作ってくれます。

food.goがあるフォルダで以下を実行します。

$ go generate

すると food_gen.goというコードが生成されます。少し大きすぎるので gistに貼りつけました。詳しくは gen の README を参照して頂きたいですが、配列を扱う上で便利な関数群が生成されます。

あとはこれを使って処理を書くのみとなります。

package main

import (
    "fmt"
    . "github.com/mattn/go-example/food"
)

func main() {
    foods := Foods{
        {"リンゴ"110},
        {"みかん"70},
        {"メロン"400},
    }

    foods.All(func(f *Food) bool {
        if f.Price < 200 {
            fmt.Println(f.Name)
        }
        return true
    })
}

この例は genを使いましたが、go-assetsgo-bindataでバイナリからソースファイルを生成したり、msgpを使って構造体を MessagePack 対応したりといった用途にも使用出来るかと思います。

ライブラリユーザではなくライブラリ開発者にとっては便利な機能だと思いますね。

golang で最近お気に入りの WAF「Goji」

$
0
0

Web アプリケーションを書くときは今までずっと

というスタンスを何故か貫いて来たんだけど、最近ようやく web.go をやめて goji を使う様になった。

Goji

A web microframework for Golang

https://goji.io/

理由としては

  • Sinatra ライクでありながら高度な正規表現マッチも使えるルータ
  • それでいて net/http コンパチ
  • 簡単に実装出来るミドルウェアスタック
  • グレースフルシャットダウン
  • そして何と言っても速い

ミドルウェアも一般的な Web アプリケーションを作るには十分な物が既に揃ってます

それでもやっぱり大きめな物は net/http で書くんだけど、小規模から中規模な物を一気にゴリゴリっと書く場合には goji は便利です。もちろん Windows でも動きます。

今日はこの goji と pongo2 というテンプレートエンジン、そして naoina さんが開発している ORM の genmai を使って Wiki を作ってみたいと思います。

ORM には genmai を使います。

おれのかんがえたさいきょうの ORM for Golang - 何気に大変
http://naoina.plog.la/2014/02/17/121743410109
naoina/genmai - GitHub

package main import ( "database/sql""fmt""time" _ "github.com/mattn/go-sqlite3" // _ "github.com/g...

https://github.com/naoina/genmai

genmai、とても便利です。フォルダ構成はルートにアプリケーション、view ディレクトリにテンプレート、assets に静的ファイルを置きます。テンプレートエンジンには pongo2 を使います。pongo2 は jedieでも使っています。

genmai を食す際の構造体は以下の通り。

type Page struct {
    Id        int64     `db:"pk"`
    Title     string    `db:"unique" json:"title"`
    Body      string    `json:"body"`
    URL       string    `db:"-"`
    Deleted   bool      `json:"deleted"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

SQLite3 のダイアレクトを指定して Page テーブルを作ってもらいます。

db, err := genmai.New(&genmai.SQLite3Dialect{}, "./wiki.db")
if err != nil {
    log.Fatalln(err)
}
if err := db.CreateTableIfNotExists(&Page{}); err != nil {
    log.Fatalln(err)
}

Wiki なのでルーティングは以下の通り。

  • 静的ファイル /assets, GET
  • ページ一覧 /, GET
  • ページ /wiki/:title, GET
  • 編集画面 /wiki/:title/edit, GET
  • 更新処理 /wiki/:title, POST

goji だと以下の様に登録します。

goji.Get("/assets/*", http.FileServer(http.Dir(".")))
goji.Get("/", showPages)
goji.Get("/wiki/:title", showPage)
goji.Get("/wiki/:title/edit", editPage)
goji.Post("/wiki/:title", postPage)

ページの表示処理は以下の様にしました。

func showPage(c web.C, w http.ResponseWriter, r *http.Request) {
    wiki := getWiki(c)
    db := wiki.DB

    var pages []Page
    err := db.Select(&pages, db.From(&Page{}), db.Where("title""=", c.URLParams["title"]))
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    var page Page
    if len(pages) > 0 {
        page = pages[0]
    }
    if page.Title == "" {
        page.Title = c.URLParams["title"]
    }
    page.URL = wiki.PageURL(&page)
    tpl, err := pongo2.DefaultSet.FromFile("page.tpl")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    tpl.ExecuteWriter(pongo2.Context{"page": page}, w)
}

そしてテンプレート view/page.tplは以下の通り。pongo2 はテンプレートのキャッシュに対応しているので処理が書けたら起動したままテンプレートファイルを編集する事が出来てとても便利です。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ page.Title }}</title>
    <link rel="stylesheet" href="/assets/css/style.css" media="all">
</head>
<body>
    <div id="content">
        <h1 class="title">{{ page.Title }}</h1>
        <div class="body">
        {{ page.Body | markdown }}
        </div>
        <span class="date">Updated at: {{ page.UpdatedAt | date: "2006/01/02 03:04:05" }}</span>
        <br />
        <a href="{{ wiki.URL }}">Page index</a>
        <a href="{{ page.URL }}/edit">Edit this page</a>
    </div>
</body>
</html>

編集画面や登録処理も同様の手順で作ると、以下の様な Wiki ページが出来上がります。

y-u-no

ソースコードは以下に置いておきます。興味のある方はどうぞ。

mattn/goji-wiki - GitHub
https://github.com/mattn/goji-wiki

慣れたら簡単!golang の便利な和製 WAF、「kocha」

$
0
0

昨日は naoina さんの genmai を使って wiki を書きましたが、今日は同じく naoina さんが開発している kocha を使って簡単な SPA アプリを書いてみたいと思います。

Kocha web application framework for Go

A convenient web application framework for Go

http://naoina.github.io/kocha/

巷の golang の WAF の殆どは、どちらかというと Sinatra 風の、自前でルーティングハンドラ兼コントローラを書いてくタイプの物ですが、kocha はどちらかというと rails 寄りの WAF に位置します。

rails gでコントローラを生成したりモデルを作ったりといった、コマンドラインベースのジェネレータを使ってガシガシと開発していくフローを実現出来ます。以下手順を追って説明します。

アプリケーションひな形を作る

$ kocha new github.com/mattn/kocho
    create directory /home/mattn/dev/godev/github.com/mattn/kocho/app/controller
              create /home/mattn/dev/godev/github.com/mattn/kocho/app/controller/root.go
    create directory /home/mattn/dev/godev/github.com/mattn/kocho/app/view/error
              create /home/mattn/dev/godev/github.com/mattn/kocho/app/view/error/404.html
              create /home/mattn/dev/godev/github.com/mattn/kocho/app/view/error/500.html
    create directory /home/mattn/dev/godev/github.com/mattn/kocho/app/view/layout
              create /home/mattn/dev/godev/github.com/mattn/kocho/app/view/layout/app.html
              create /home/mattn/dev/godev/github.com/mattn/kocho/app/view/root.html
    create directory /home/mattn/dev/godev/github.com/mattn/kocho/config
              create /home/mattn/dev/godev/github.com/mattn/kocho/config/app.go
              create /home/mattn/dev/godev/github.com/mattn/kocho/config/routes.go
              create /home/mattn/dev/godev/github.com/mattn/kocho/main.go
    create directory /home/mattn/dev/godev/github.com/mattn/kocho/public
              create /home/mattn/dev/godev/github.com/mattn/kocho/public/robots.txt

この状態でも kocha runとすれば動作します。この辺は rails に似た動作になっていますね。golang の場合はシングルバイナリがウリですがもちろん kocha でも kocha buildとすればシングルバイナリが生成されます。

モデルを作る

このサンプルではユーザ登録画面を作ろうと思います。とは言っても属性は名前(name)だけとなります。

$ kocha g model user
              create app/model/users.go
生成されるコードは以下の通り。
package model

import (
    "github.com/naoina/genmai"
)

type User struct {
    Id int64 `db:"pk" json:"id"`

    genmai.TimeStamp
}

func (m *User) BeforeInsert() error {
    // FIXME: This method is auto-generated by Kocha.
    //        You can remove this method if unneeded.
    return m.TimeStamp.BeforeInsert()
}

func (m *User) AfterInsert() error {
    // FIXME: This method is auto-generated by Kocha.
    //        You can remove this method if unneeded.
    return nil
}

func (m *User) BeforeUpdate() error {
    // FIXME: This method is auto-generated by Kocha.
    //        You can remove this method if unneeded.
    return m.TimeStamp.BeforeUpdate()
}

func (m *User) AfterUpdate() error {
    // FIXME: This method is auto-generated by Kocha.
    //        You can remove this method if unneeded.
    return nil
}

func (m *User) BeforeDelete() error {
    // FIXME: This method is auto-generated by Kocha.
    //        You can remove this method if unneeded.
    return nil
}

func (m *User) AfterDelete() error {
    // FIXME: This method is auto-generated by Kocha.
    //        You can remove this method if unneeded.
    return nil
}

これに Name 属性を足します。

type User struct {
    Id int64 `db:"pk" json:"id"`
    Name string `json:"name"`

    genmai.TimeStamp
}

コントローラを作る

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

コントローラのひな形が生成されました。生成されるコードは以下の様になっています。

package controller

import (
    "github.com/naoina/kocha"
)

type Users struct {
    *kocha.DefaultController
}

func (us *Users) GET(c *kocha.Context) kocha.Result {
    // FIXME: auto-generated by kocha
    return kocha.Render(c)
}

func (us *Users) POST(c *kocha.Context) kocha.Result {
    // FIXME: auto-generated by kocha
    return kocha.Render(c)
}
これを、昨日の例を参考に以下の様に修正します。POST は name パラメータを貰ってモデルに登録します。
package controller

import (
    "github.com/mattn/kocho/db"
    "github.com/mattn/kocho/app/model"
    "github.com/naoina/kocha"
)

type Users struct {
    *kocha.DefaultController
}

func (us *Users) GET(c *kocha.Context) kocha.Result {
    var users []model.User
    err := db.Get("default").Select(&users)
    if err != nil {
        return kocha.RenderError(c, 500, err)
    }
    return kocha.RenderJSON(c, users)
}

func (us *Users) POST(c *kocha.Context) kocha.Result {
    name := c.Params.Get("name")
    if name == "" {
        return kocha.RenderError(c, 400"Bad request")
    }
    user := &model.User{Name: name}
    _, err := db.Get("default").Insert(user)
    if err != nil {
        return kocha.RenderError(c, 500, err)
    }
    return kocha.RenderJSON(c, user)
}

ビューを作る

今回作るサンプルアプリケーションは SPA (Single Page Application) なので、ルートのページのみ編集します。

root.htmlの編集前は以下の様になっています。

{{define "content"}}
<h1>Welcome to Kocha</h1>
{{end}}

これを弄って以下の様にしました。

{{define "content"}}
<script>
$(function() {
    function update() {
        $.getJSON("/users"function(data) {
            var $u = $('#users').empty();
            $.each(data, function(index, user) {
                $('<li/>').text(user.name).appendTo($u)
            });
        })
    }
    $('#send').click(function() {
        $.post("/users"{"name": $('#name').val()}function() {
            $('#name').val('');
            update();
        });
    });
    update();
})
</script>
<h1 class="title">ユーザ一覧</h1>
<label for="name">名前</label>
<input id="name" type="text">
<input id="send" type="submit" value="追加">
<ul id="users"></ul>
{{end}}

jQuery を使うので app/view/layout/app.htmlも以下の様に修正しました。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
  <link rel="stylesheet" href="style.css" media="all">
  <title>ユーザ一覧</title>
</head>
<body>
  {{template "content" .}}
</body>
</html>

名前欄に入力された文字列を Ajax で POST して、成功すれば一覧を更新する単純なアプリケーションが出来ました。

kocho

まとめ

始めに仕組みを覚えるまでに間違ったルータを作ってしまったりモデルが決まらなかったりしますが、一旦作り方を覚えてしまえば rails 並みの開発効率が生み出せると思います。

実際にこのサンプルアプリケーションを作るのに20分程度でした(僕は kocha はずいぶん前から遊んでいたので使い方は大体知ってはいましたが)。

ちなみに kocha、Windows でも動きます!(ここ大事)

例によって作ったプリケーションを github に置いておきます。

mattn/kocho - GitHub
https://github.com/mattn/kocho

送り主の分からない小包

$
0
0

今日、嫁から電話があり...


嫁「送り主の分からない小包が届いてる」

僕「どんなん?誰から?俺に?」

嫁「それが分からんねん。英語ばっかりや」

僕「なんて書いてる?」

嫁「フェーフェー、フェデークス...」

僕「それ、運送会社や」

嫁「英語ばっかりで分からんわ。開けていい?」

僕「いや、間違いかも知れんし」

嫁「宛名は合ってるねんで」

僕「なんやろ覚えが無い」

嫁「カードでなんか買い物したんちゃうん...」

僕「してないわー」

家で見ても、確かに送り主が分からない。おそるおそる開けて見ると















DSC01360

どどーん。なんか真っ黒いの出てきたと思ったら、ブランケット出てきた。しかも「Google」って書いてある。

DSC01362

かっ... かっちょ... かっちょええやん...

DSC01363

おーぷんそーす ぷろぐらむ おふぃす...

Google is sending out blankets to open-source developers | Hacker News

My perspective comes from the corporate world. On the one hand, it's nice to be acknowledged for wor...

https://news.ycombinator.com/item?id=5650075

これやーーーーー!

ありがとうございます!ありがとうございます!

大事にします!

ただ一つだけ...

出来れば送り主、書いて下さい!


ソースに一行追加するだけですべての HTTP 通信が環境変数指定のプロキシではなくIEの設定になるモジュールを書いた #golang

$
0
0

通常、golang のデフォルトの HTTP クライアントは環境変数 HTTP_PROXY もしくは HTTPS_PROXY を参照してプロキシに接続し、環境変数 NO_PROXY で指定されたホストが無視される仕組みになっています。しかし一般ユーザにとって環境変数の設定は、難易度の高い作業だったりします。

使用例

たとえばこういうコードに…

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
)

func main() {
    res, err := http.Get("http://www.google.com")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer res.Body.Close()
    io.Copy(os.Stdout, res.Body)
}

一行追加すると:

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"

    _ "github.com/mattn/go-ieproxy/global" // ← この行
)

func main() {
    os.Setenv("HTTP_PROXY""")
    os.Setenv("HTTPS_PROXY""")
    res, err := http.Get("http://www.google.com")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer res.Body.Close()
    io.Copy(os.Stdout, res.Body)
}

たったこれだけで、IE のプロキシ設定が引き継がれます。無ければデフォルトと同じ様に、環境変数で動作します。

仕組み

ieproxy は ieproxy.ProxyFromIE という関数を提供していて、これが http.Transport.Proxy を実装しています。この関数は http クライアントのプロキシ仲介有無を行っているのでつまりは http.Client の Proxy を設定できます。http.Cilent が HTTP リクエストを実行する際にはこの Proxy が呼び出されて判定処理が行われますが、ieproxy.ProxyFromIE はレジストリから IE のプロキシ設定を取得しつつプロキシの判定を行うような実装になっているので、このように HTTP 通信に引っかけてプロキシをすげかえることができるわけです。

さらに github.com/mattn/go-ieproxy/global パッケージでは http.DefaultTransport.Proxy を ieproxy.ProxyFromIE に書き換え(!)しているので、インポートするだけですべての HTTP 通信が(標準の http パッケージを利用していれば)勝手に IE のプロキシ設定になります。

注意: IE でしかビルド出来ません。

参考資料: ソースに一行追加するだけですべての HTTP 通信をロギングするモジュールを書いた #golang - 詩と創作・思索のひろば

mattn/go-ieproxy - GitHub
https://github.com/mattn/go-ieproxy

Re: GoLangでJavaのenumっぽいライブラリ作った話

$
0
0
GoLangでJavaのenumっぽいライブラリ作った話 - おいぬま日報

なんとかなるわけなんだけど、これを毎回書くのは面倒なので...

もっと良いやり方があったらぜひ教えて欲しいところです。

http://oinume.hatenablog.com/entry/introducing-goenum

golang でユーザ型の値を文字列化する場合 Stringer というインタフェースを実装します。具体的には

func (l Lang) String() string {
    switch l {
    case Go:
        return "Go"
    case Python:
        return "Python"
    case Ruby:
        return "Ruby"
    }
    panic("Unknown value")
}

上記サイトで書かれている様に String()という関数を実装します。実は golang のオフィシャルから stringer という、その名の通りのツールが提供されています。

まず、Stringer を実装していないコードを書きます。

package main

import (
    "fmt"
)

type Fruit int

const (
    Apple Fruit = iota
    Orange
    Banana
)

func main() {
    var fruit Fruit = Apple
    fmt.Println(fruit)
}

このまま実行すると 0と表示されます。ここで以下の様にして stringer というツールをインストールします。

$ go get golang.org/x/tools/cmd/stringer

そしてソースコードと同じディレクトリで以下の様に実行します。

$ stringer -type Fruit fruit.go

すると fruit_string.goというファイル名の以下のコードが生成されます。

// generated by stringer -type Fruit fruit.go; DO NOT EDIT

package main

import "fmt"

const _Fruit_name = "AppleOrangeBanana"

var _Fruit_index = [...]uint8{51117}

func (i Fruit) String() string {
    if i < 0 || i >= Fruit(len(_Fruit_index)) {
        return fmt.Sprintf("Fruit(%d)", i)
    }
    hi := _Fruit_index[i]
    lo := uint8(0)
    if i > 0 {
        lo = _Fruit_index[i-1]
    }
    return _Fruit_name[lo:hi]
}

あとはこのソースも含めてビルドし、再度実行してみると今度は Appleと表示される様になります。便利ですね。

尚、constで iota を使う場合ですが、上記の様に型を指定しておくと違う型の変数へ Apple を代入する際にエラーが出て便利です。

package main

import (
    "fmt"
)

type Fruit int
type Animal int

const (
    Apple Fruit = iota
    Orange
    Banana
)

const (
    Monkey Animal = iota
    Elephant
    Pig
)

func main() {
    var fruit Fruit = Apple
    fmt.Println(fruit)

    fruit = Elephant
    fmt.Println(fruit)
}
./fruit.go:26: cannot use Elephant (type Animal) as type Fruit in assignment

golang で vimgirl bot を書いた。

$
0
0
この記事は Go Advent Calendar 2014 16日の記事ではありません
docomo Developer support | NTTドコモ

Docomo が公開する API など開発者向けの情報を提供します。アプリケーションの開発にご活用ください

https://dev.smt.docomo.ne.jp/
DocomoruでBOTと雑に会話する - Qiita

docomoが提供している雑談対話APIを利用し...

http://qiita.com/r7kamura/items/55f398624dbce1c6dc14

DoCoMo が雑談対話APIというのを出していたのでサクッっとlingr botを書いてみた。

mattn/go-docomo - GitHub
https://github.com/mattn/go-docomo

lingr の vim 部屋上に vimgirl という名前の bot として動いています。

Vimgirl lingrbot

Vimgirl lingrbot これは これは、NTT DoCoMo が提供する雑談対話 API を利用して実装されたチャットサービス lingr の bot です。

http://mattn.tonic-water.com/vimgirl/

キャラクタは orgachem さんが描いている絵を使わせて頂きました。(CC BY)

vimgirl: こんにちわ
この様に vimgirl: のプレフィックスを付けて会話すると
vimgirl

適当な会話をしてくれます。ご活用下さい。

Software Design の Vim 特集に寄稿させて頂きました。

$
0
0

技術評論社さんの「Software Design 2015年1月号」の Vim 特集に寄稿させて頂きました。

  • 第1章:犬でもわかる!? Vim導入&カスタマイズの超基本……林田 龍一
  • コラム1:「とっつきにくい変態エディタ」だったVimが「私の素敵な相棒」に変わるまで……伊藤 淳一
  • 第2章:IDE並みの機能を軽快な動作で! 実用Tips&対策[プログラマ編]……mattn
  • 第3章:運用作業であわてないために 実用Tips&対策[インフラエンジニア編]……佐野 裕
  • 第4章:vim-markdownという選択 実用Tips&対策[文書作成編]……mattn
  • コラム2:Vimの真のチカラを引き出すパラダイムシフト Vimは編集作業をプログラムにする……MURAOKA Taro (a.k.a. KoRoN)

Vimmer なら目次だけでヨダレが出そうな内容です。さらに今回は犬さんこと Linda_pp さん、香り屋さんこと KoRoN さんにも執筆して頂きました。

正月そうそう Vim かよ!と思われるかもしれませんが、正月だからこそ Vim、いや Vim が無いと年が越せないとも言われます。(要出典)

ぜひ購読してニタニタして下さい。

Software Design (ソフトウェア デザイン) 2015年 01月号 [雑誌]Software Design (ソフトウェア デザイン) 2015年 01月号 [雑誌]

技術評論社 / ¥ 1,318 (2014-12-18)
 
発送可能時間:近日発売 予約可

C++ の WAF crow が mustache テンプレートエンジンをサポートしていた。

$
0
0

以前、Python の Flask からインスパイアされた C++ 製のとてもかっちょいい WAF「crow」を紹介しました。

Big Sky :: C++ 製 micro web framework「crow」を使って lingr の bot 書いてみた。

先日、github で crow という、python の flask からインスパイアされた C++ 製 micro web framework を見つけました。 ipkn/crow - GitHu...

http://mattn.kaoriya.net/software/lang/c/20140718122410.htm
ipkn/crow - GitHub
https://github.com/ipkn/crow

ひさびさ見てみたら、mustache テンプレートエンジンをサポートしていました。

{{ mustache }}

Logic-less templates

http://mustache.github.io/

さらに amalgamate というフォルダには1ファイルだけで使えるヘッダファイルも用意されています。

これはすごい。前回は lingr のボットを作ってみましたが、今回は MySQL を使った1行掲示板を書いてみました。

まず DDL は以下の通り。

CREATE TABLE bbs (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    text VARCHAR(100),
    created TIMESTAMP DEFAULT NOW()
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

掲示板のコードは以下の通り。

#include <memory>
#include <mysql_connection.h>
#include <mysql_driver.h>
#include <cppconn/prepared_statement.h>
#include <cppconn/resultset.h>
#include "crow_all.h"

int
main() {
  std::ifstream conf("config.json");
  if (!conf) {
    std::cerr << "config.json not found" << std::endl;
    return 1;
  }
  std::string json = {
    std::istreambuf_iterator<char>(conf),
    std::istreambuf_iterator<char>()};
  crow::json::rvalue config = crow::json::load(json);

  auto driver = sql::mysql::get_mysql_driver_instance();
  auto raw_con = driver->connect(
    (std::string) config["db_host"].s(),
    (std::string) config["db_user"].s(),
    (std::string) config["db_pass"].s());
  auto con = std::unique_ptr<sql::Connection>(raw_con);
  con->setSchema((std::string) config["db_name"].s());

  crow::SimpleApp app;
  crow::mustache::set_base(".");

  CROW_ROUTE(app, "/")
  ([&]{
    auto stmt = std::unique_ptr<sql::PreparedStatement>(
      con->prepareStatement("select * from bbs order by created"));
    auto res = std::unique_ptr<sql::ResultSet>(
      stmt->executeQuery());
    int n = 0;
    crow::mustache::context ctx;
    while (res->next()) {
      ctx["posts"][n]["id"] = res->getInt("id");
      ctx["posts"][n]["text"] = res->getString("text");
      n++;
    } 
    return crow::mustache::load("bbs.html").render(ctx);
  });

  CROW_ROUTE(app, "/post")
      .methods("POST"_method)
  ([&](const crow::request& req, crow::response& res){
    crow::query_string params(req.body);
    auto stmt = std::unique_ptr<sql::PreparedStatement>(
      con->prepareStatement("insert into bbs(text) values(?)"));
    stmt->setString(1, params.get("text"));
    stmt->executeUpdate();
    res = crow::response(302);
    res.set_header("Location""/");
    res.end();
  });

  app.port(40081)
    //.multithreaded()
    .run();
}

/で一覧表示、/postで投稿します。MySQL の操作は MySQL Connector/C++ を使いました。

MySQL :: MySQL Connector/C++ Developer Guide

Table of Contents [ +/- ] Preface and Legal Notices 1 Introduction to MySQL Connector/C++ 2 How to G...

http://dev.mysql.com/doc/connector-cpp/en/

HTML (mustache) は以下の通り。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>一行掲示板</title>
  </head>
  <body>
    <h2>一行掲示板</h2>
    <ul>
      {{# posts}}
      <li>{{id}}: {{text}}</li>
      {{/ posts}}
    </ul>
    <form action="/post" method="post">
      <input type="text" name="text"><input type="submit">
    </form>
  </body>
</html>

あとは MySQL への接続情報が書かれた config.jsonを用意すれば

{
    "db_host": "localhost",
    "db_user": "mysqluser",
    "db_pass": "mysqlpass",
    "db_name": "bbs"
}
一行掲示板

この様な1行掲示板が出来ました。もちろん入力チェック等は行っていませんので実際にはもう少しコードが長くなります。

crow、かっちょいいですね。

いつも通り、コードは github に置いておきます。

mattn/crow-bbs - GitHub
https://github.com/mattn/crow-bbs
C++テンプレートテクニック 第2版C++テンプレートテクニック 第2版
επιστημη
SBクリエイティブ / ¥ 3,024 (2014-04-17)
 
発送可能時間:通常2~3週間以内に発送

Vim script で streem を実装した。

$
0
0

この記事はVim Advent Calendar 2014 - Qiita 24日目の記事です。

Matz さんが streem という、ストリーム指向言語の開発を始めるらしいです。

お兄ちゃん

https://github.com/matz/streem

まだ文法の設計段階ではあるけど、それなのにかなりの量の pull-req がバンバンと来てて凄いなーと思いつつも「この pull-req 量だと僕には出番無いなー」と思ったのと「Matz さんがもしかしたら Go で streem を実装するかもしれない」という記事を読み「streem の他言語実装が一つ消えてしまう。これはまずい。」と思ったので、README.md に書かれているサンプルだけを頼りに streem を Vim script で実装してみました。

SlipStreem!

先日はネタで streem のマネをして golang で実装したりしましたが、本日ネタが無い中にどうしても Vim script で streem を実装したくなったのでやってみました。

どんなものか

とりあえずこんな事が出来ます。まず以下のバッファを用意します。

foo
bar
baz

そして以下のコマンドを実行します。

:%Streem {|x| x += " Matz" } | STDOUT

すると画面に

foo Matz
bar Matz
baz Matz

と表示されます。

どうやって動いているのか

まず、Streemコマンドは Vim の range から行を入力として扱います。つまり上記で言えば ['foo', 'bar', 'baz']となります。

そして引数のコマンドを解析し、AST(抽象構文木)に分解します。分解に使ったマッチパターンテーブルは以下の通り。

let s:tbl = [
\  ['stmts',
\    [
\      { 'type''node''match': ['stmt', ['lb''stmt']], 'eval''s:stmts' },
\    ],
\  ],
\  ['stmt',
\    [
\      { 'type''node''match': ['expr', ['|''expr']], 'eval''s:stmt' },
\    ],
\  ],
\  ['expr',
\    [
\      { 'type''node''match': ['expr_node''sp''==''sp''expr_node'], 'eval''s:op_eqeq' },
\      { 'type''node''match': ['if''sp''expr''sp''end'], 'eval''s:expr_if' },
\      { 'type''node''match': ['ident''('')'], 'eval''s:op_call' },
\      { 'type''node''match': ['ident''(''expr_node'')'], 'eval''s:op_call' },
\      { 'type''node''match': ['ident''sp''=''sp''expr_node'], 'eval''s:op_let' },
\      { 'type''node''match': ['ident''sp''+=''sp''expr_node'], 'eval''s:op_plus' },
\      { 'type''node''match': ['expr_node', ['sp''|''sp''expr_node']], 'eval''s:expr' },
\      { 'type''node''match': ['expr_node'], 'eval''s:expr' },
\    ],
\  ],
\  ['expr_node',
\    [
\      { 'type''node''match': ['{''sp''|''ident''|''sp''}'], 'eval''s:expr_func' },
\      { 'type''node''match': ['{''sp''|''ident''|''sp''stmts''sp''}'], 'eval''s:expr_func' },
\      { 'type''node''match': ['ident'], 'eval''s:expr' },
\      { 'type''node''match': ['number'], 'eval''s:expr' },
\      { 'type''node''match': ['string'], 'eval''s:expr' },
\    ],
\  ],
\  ['string', [{ 'type''regexp''match''\("[^"]*"\|''[^'']*''\)''eval''s:expr_string' }]],
\  ['number', [{ 'type''regexp''match''[0-9]\+''eval''s:expr_number' }]],
\  ['ident', [{ 'type''regexp''match''[a-zA-Z][a-zA-Z0-9]*''eval''s:expr_ident' }]],
\  ['+=', [{ 'type''string''match''+=''eval''' }]],
\  ['=', [{ 'type''string''match''=''eval''' }]],
\  ['|', [{ 'type''string''match''|''eval''' }]],
\  ['if', [{ 'type''string''match''if''eval''' }]],
\  ['end', [{ 'type''string''match''end''eval''' }]],
\  ['sp', [{ 'type''regexp''match''[ \t]*''eval''' }]],
\  ['lb', [{ 'type''regexp''match''[ \t]*[\\r\n;]\+[ \t]*''eval''' }]],
\  ['{', [{ 'type''string''match''{''eval''' }]],
\  ['}', [{ 'type''string''match''}''eval''' }]],
\]

これを YACC っぽくパースし、各ノードに分解します。例えば +=オペレータであれば以下のノードになります。

{'type''op_plus''value': [{'type''ident''value''x'}{'type''expr''value': [{'type''string''value''Matz'}]}]}

これを小さな VM (今回は時間が無くて streeem が実装している程の命令をサポート出来ませんでした)で実行します。STDOUT は入力を echo するだけのオブジェクトです。

また今回は残念ですが concurrency ではありません。メモリを使いシーケンシャルに実行しています。

ただ、これだけは覚えておいて下さい。

Vim に出来ないから実装しなかった訳ではない

やろうと思えば remote API を使い、複数立ち上げた vim と非同期通信を行いながら結果を集める事だって出来ます。

とここまで書きましたが、そろそろクドいし面倒臭くなってきたと思うので、どうやって動いているか知りたい人はソースを見てください。

mattn/streem-vim - GitHub
https://github.com/mattn/streem-vim

さいごに

さて、明日で Vim Advent Calendar 2014 が完走します(2日程抜けてしまいましたが)。皆さんお疲れ様でした。まさか開発されて20年近くにもなるテキストエディタでこれだけのブログ記事があがって来るとは誰が想像したでしょうか。

また今年も数多くの不具合報告が vim-jp へと寄せられ、数多くのバグが vim-jp によって修正されました。バグ報告頂いた皆さん、そしてパッチを書いてくれた vim-jp のメンバに感謝したいと思います。ありがとうございました。

来年もよい Vim 年になる事を祈って、僕の記事を終えさせて頂きます。

皆様、良いお年を。

Software Design (ソフトウェア デザイン) 2015年 01月号 [雑誌]Software Design (ソフトウェア デザイン) 2015年 01月号 [雑誌]

技術評論社 / ¥ 1,318 (2014-12-18)
 
発送可能時間:在庫あり。

Re: VimでURLをドメインだけに置換するコマンドを正規表現でうったら、本当に正規表現って意味不明なフォルムと思った。

$
0
0
VimでURLをドメインだけに置換するコマンドを正規表現でうったら、本当に正規表現って意味不明なフォルムと思った。 - Qiita
http://qiita.com/mochizukikotaro/items/b15170dccb18d84f8cd2
:%s/\([:\/]\)\@<!\/.*$//g
なんか、とても意味不明なコマンドだ。きっと、もっとスマートなものがあるのだろう。コードゴルフで言えばトリプルボギー的な感じなのでしょうか。知らないけど。
この問題は
  • URL を扱うので /が多く、置換の区切りを /にする場合エスケープが多くなる
  • very magic でないのでエスケープがさらに増える

この2点から生まれます。1点目は

:%s#\([:/]\)\@<!/.*$##g
#
:%s,\([:/]\)\@<!/.*$,,g

,をセパレータにする事で少しは解消するかと思います。2点目は \vを付ける事でキャプチャ \(\)()と書く事が出来ます。

ただし今回の例はパターンが短いので効果は出ませんが、もう少し長いパターンだと効果が表れます。

あと、この例では Vim 独自の \zsを使う事でもう少しシンプルに書く事も出来ます。

:%s!//[^/]\+\zs/.*!!g
  • //で始まり
  • /で無い物が続き
  • \zsそこまでは置換対象から外す
  • /.*を置換対象とする(※1)

結果、/.*つまり URL のパス部分(※1)だけが空白に置き換えられるので URL のホスト名までが残ります。

もちろんちゃんとしたURLで判定する場合は、もう少し細かな条件が必要となります。


ちっちゃなビルドシステム qo が激しく便利だった件

$
0
0

golang には go buildというビルド機能があり、C言語と golang をまぜた cgo というC言語拡張も同じコマンドでビルド出来ます。

その際、ソースコードのコメントに CFLAGS や LDFLAGS を自ら指定する事が出来るので

package gtk

// #include "gtk.go.h"
// #cgo pkg-config: gtk+-2.0
import "C"
import (
    "fmt"
    "log"
    "reflect"
    "runtime"
    "strings"
    "unsafe"

    "github.com/mattn/go-gtk/gdk"
    "github.com/mattn/go-gtk/gdkpixbuf"
    "github.com/mattn/go-gtk/glib"
    "github.com/mattn/go-gtk/pango"
)

上記は go-gtk のコードの一部。

そのライブラリやアプリケーションをビルドしたいユーザは特にライブラリへのパスを指定する事なく、ただ単に

go build

と実行するだけで実行モジュールが出来上がります。この便利さに目を付けた Pietro Gagliardi さんが qo という golang で書かれたプログラムを公開してくれています。

andlabs/qo · GitHub

Another build system for C/C++, I guess? Inspired by 'go build'

https://github.com/andlabs/qo

インストールは golang がインストールされている状態であれば

go get github.com/andlabs/qo

だけです。昔 golang のリポジトリから Makefile が消え去りビルド構成ファイルが何もないという状況を見て軽いカルチャーショックを受けたのだけど、今となってはとても心地良いし「あー、Makefile いらんかったんやー」とも思います。その心地よさを C/C++ で扱えるツールです。例えば gtk を使ったプログラムを書く場合

#include <gtk/gtk.h>

int
main(int argc, char* argv[]) {
  GtkWidget* window;
  GtkWidget* label;
  gtk_init(&argc, &argv);
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "helloworld");
  g_signal_connect(G_OBJECT(window), "delete-event", gtk_main_quit, window);
  label = gtk_label_new("Hello World!");
  gtk_container_add(GTK_CONTAINER(window), label);
  gtk_widget_show_all(window);
  gtk_main();
  return 0;
}
pkg-configというツールを使いますが、このツールから得られた CFLAGS や LDFLAGS をコマンドラインに渡すか、以下の様な Makefile が必要でした。
SRCS \
    foo.c

OBJS $(subst .c,.o,$(SRCS))

CFLAGS `pkg-config --cflags gtk+-2.0`
LIBS `pkg-config --libs gtk+-2.0`
TARGET = qo-sandbox

all : $(TARGET)

$(TARGET) : $(OBJS)
    gcc -o $@ $(OBJS) $(LIBS)

.c.o :
    gcc -c $(CFLAGS) -I. $< -o $@

clean :
    rm -f *.o $(TARGET)

しかし qo を使うと Makefile は消して良く、以下の様なコメントを書くだけで良いのです。

// #qo pkg-config: gtk+-2.0
#include <gtk/gtk.h>

int
main(int argc, char* argv[]) {
  GtkWidget* window;
  GtkWidget* label;
  gtk_init(&argc, &argv);
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "helloworld");
  g_signal_connect(G_OBJECT(window), "delete-event", gtk_main_quit, window);
  label = gtk_label_new("Hello World!");
  gtk_container_add(GTK_CONTAINER(window), label);
  gtk_widget_show_all(window);
  gtk_main();
  return 0;
}

あとはおもむろに

qo

を実行するだけです。毎回フルビルドするのではなく .qoobjというフォルダにビルド済みのファイルが格納されるので更新されたファイルのみビルドされます。また CFLAGS や LDFLAGS も直接指定する事が出来ます。

// #qo CFLAGS: -I/opt/sqlite3/include
// #qo LIBS: sqlite3
さらに golang の go buildと同様に、ファイル名に _windowsが付いている物は windows のみ、_386が付いている物は 386 系 CPU のみビルド対象となります。(サポートOS: windows, darwin, linux, freebsd, openbsd, netbsd, dragonfly, solaris)

リンクされる実行モジュールはディレクトリ名という割り切りなので、覚えてしまえば面倒臭さがなくなりとても心地よいです。

Makefile ほど細かな設定は出来ないですが、ちょっとした小さいプログラムを書きあげる際にとても便利なツールとなる事は間違いないと思います。

golang のリファクタリングには gofmt ではなく、gorename を使おう。

$
0
0

今まで golang で変数名や関数名のリネームには gofmt の -rオプションを使ってきましたが、これからは gorename を使いましょう。

文法を解析して正しくリネームしてくれるので、gofmt で起き得た誤爆も心配ありません。インストールは以下の様に実行します。

$ go get golang.org/x/tools/cmd/gorename

使用方法は以下の通り。

gorename: precise type-safe renaming of identifiers in Go source code.

Usage:

 gorename (-from <spec> | -offset <file>:#<byte-offset>) -to <name> [-force]

You must specify the object (named entity) to rename using the -offset
or -from flag.  Exactly one must be specified.

Flags:

-offset    specifies the filename and byte offset of an identifier to rename.
           This form is intended for use by text editors.

-from      specifies the object to rename using a query notation;
           This form is intended for interactive use at the command line.
           A legal -from query has one of the following forms:

  "encoding/json".Decoder.Decode        method of package-level named type
  (*"encoding/json".Decoder).Decode     ditto, alternative syntax
  "encoding/json".Decoder.buf           field of package-level named struct type
  "encoding/json".HTMLEscape            package member (const, func, var, type)
  "encoding/json".Decoder.Decode::x     local object x within a method
  "encoding/json".HTMLEscape::x         local object x within a function
  "encoding/json"::x                    object x anywhere within a package
  json.go::x                            object x within file json.go

           For methods, the parens and '*' on the receiver type are both
           optional.

           Double-quotes may be omitted for single-segment import paths
           such as fmt.  They may need to be escaped when writing a
           shell command.

           It is an error if one of the ::x queries matches multiple
           objects.

-to        the new name.

-force     causes the renaming to proceed even if conflicts were reported.
           The resulting program may be ill-formed, or experience a change
           in behaviour.

           WARNING: this flag may even cause the renaming tool to crash.
           (In due course this bug will be fixed by moving certain
           analyses into the type-checker.)

-dryrun    causes the tool to report conflicts but not update any files.

-v         enables verbose logging.

gorename automatically computes the set of packages that might be
affected.  For a local renaming, this is just the package specified by
-from or -offset, but for a potentially exported name, gorename scans
the workspace ($GOROOT and $GOPATH).

gorename rejects renamings of concrete methods that would change the
assignability relation between types and interfaces.  If the interface
change was intentional, initiate the renaming at the interface method.

gorename rejects any renaming that would create a conflict at the point
of declaration, or a reference conflict (ambiguity or shadowing), or
anything else that could cause the resulting program not to compile.


Examples:

% gorename -offset file.go:#123 -to foo

  Rename the object whose identifier is at byte offset 123 within file file.go.

% gorename -from '"bytes".Buffer.Len' -to Size

  Rename the "Len" method of the *bytes.Buffer type to "Size".

---- TODO ----

Correctness:
- handle dot imports correctly
- document limitations (reflection, 'implements' algorithm).
- sketch a proof of exhaustiveness.

Features:
- support running on packages specified as *.go files on the command line
- support running on programs containing errors (loader.Config.AllowErrors)
- allow users to specify a scope other than "global" (to avoid being
  stuck by neglected packages in $GOPATH that don't build).
- support renaming the package clause (no object)
- support renaming an import path (no ident or object)
  (requires filesystem + SCM updates).
- detect and reject edits to autogenerated files (cgo, protobufs)
  and optionally $GOROOT packages.
- report all conflicts, or at least all qualitatively distinct ones.
  Sometimes we stop to avoid redundancy, but
  it may give a disproportionate sense of safety in -force mode.
- support renaming all instances of a pattern, e.g.
  all receiver vars of a given type,
  all local variables of a given type,
  all PkgNames for a given package.
- emit JSON output for other editors and tools.

go-runewidthで試してみます。

https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L192-L199

runewidth パッケージの、IsAmbiguousWidth という関数の中にある、ivという変数名を ヒノノニトンにリネームしたいと思います。

$ gorename -from '"github.com/mattn/go-runewidth".IsAmbiguousWidth::iv' -to ヒノノニトン

Windows だと以下の様に実行します。

gorename -from """github.com/mattn/go-runewidth""".IsAmbiguousWidth::iv -to ヒノノニトン

git diff を見ると

diff --git a/runewidth.go b/runewidth.go
index 1050395..5f2c1b0 100644
--- a/runewidth.go
+++ b/runewidth.go
@@ -190,8 +190,8 @@ func RuneWidth(r rune) int {
 
 // IsAmbiguousWidth returns whether is ambiguous width or not.
 func IsAmbiguousWidth(r rune) bool {
-   for _, iv := range ambiguous {
-       if iv.first <= r && r <= iv.last {
+   for _, ヒノノニトン := range ambiguous {
+       if ヒノノニトン.first <= r && r <= ヒノノニトン.last {
            return true
        }
    }

正しくリネームされているのが分かります。この他にも、構造体のメンバ名、関数名、メソッド名、ローカル変数名を置換出来るだけでなく、ファイル名を指定し、かつオフセット位置を指定して、「ここにある何か」という指定方法も出来る為、今後 IDE との連携により一層便利になるのではないかと思います。

golang でしりとりbot 作った。

$
0
0

lingr で動かしてたしりとりbotをtwitterでも動く様にしてみました。

mattn/siritori-bot - GitHub
https://github.com/mattn/siritori-bot

golang で書かれています。sirtori_botにつぶやくと...

といった具合にしりとりで返信してくれます。辞書データは以下のURLにある「萌色用語の基礎知識」を利用させて頂きました。

通信用語の基礎知識 ダウンロード

ファイル 本体 マニュアルと辞書本体です。 マニュアル 通信用語の基礎知識(WDIC) 電算用語の基礎知識(TECH) 科学用語の基礎知識(SCI) 萌色用語の基礎知識(MOE) 文化用語の基礎知識(...

http://www.wdic.org/download.shtml

ソースコードで参考になる所はほぼありませんが、適当に遊んで下さい。

embulk-plugin-twitterstream 書いた。

$
0
0

Embulk が流行っているらしいのでプラグインを書いた。

先日は遊びで embulk-plugin-vim なんていうのを作ったのだけど

mattn/embulk-plugin-vim - GitHub
https://github.com/mattn/embulk-plugin-vim

今日のはもうちょっと遊んで、TwitterStream から入力を得る物を作った。設定は以下の様にします。

exec: {}
in:
  type: twitterstream
  consumer_key:        XXXXXXXXXXXXXXXXXXXXXX
  consumer_secret:     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  access_token:        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  access_token_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  count: 3
  columns:
    id_str
    name
    user.screen_name
out: {type: stdout}

columnscountはオプショナルです。

「それ、永遠に止まらないんじゃ!」っていう人もいるかと思いまして、count という設定で決まった数まで受けたら自動で止まる様になっています。

ruby 力無いので、ruby に詳しい皆様、ご査収下さい。

mattn/embulk-plugin-twitterstream - GitHub
https://github.com/mattn/embulk-plugin-twitterstream
ビッグデータの正体 情報の産業革命が世界のすべてを変えるビッグデータの正体 情報の産業革命が世界のすべてを変える
ビクター・マイヤー=ショーンベルガー
講談社 / ¥ 1,944 (2013-05-21)
 
発送可能時間:在庫あり。

IDE としての Vim

$
0
0

この文章は、http://yannesposito.com/Scratch/en/blog/Vim-as-IDE/で掲載されている「Vim as IDE」の翻訳文です。

文内の全てはの筆者による物であり、訳文の内容については私による物となります。意訳が若干入っています。間違い等あればご連絡下さい。

Vim as IDE

Main image

tl;dr: 如何に Vim を効率の良い IDE として使いこなすか

Learn Vim Progressively (訳者注: 日本語訳)では Vim が如何にテキスト編集に優れ、かつファイルを操縦できる偉大なテキストエディタであるかについて示した。この短い記事では、私が如何にして Vim を IDE として使っているかについて見る事が出来るだろう。そう、主に幾つかの素晴らしいプラグインを使う事により。


1. Vim Plugin Manager

Vim のプラグインは たくさんプラグインがある。それらを管理するのに私は vim-plugを使っている。

インストール方法:

mkdir -p ~/.vim/autoload
curl -fLo ~/.vim/autoload/plug.vim \
             https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

☞ 私の .vimrcには2つの役割がある。まず一つが私のプラグインの全てが含まれている部分。そして二つ目がそれらのプラグインの為に行っている個人的な設定が含まれている部分。以下のコードではそれを ...で分けて記載する。


2. 生き残る


2.1. カラースキーム

Solarized theme

何よりも前に、読みやすくコントラストの低いカラースキームを使って目を守るべきだ。

その為に私は solarized darkを使っている(訳者注: mattn も使っています)。これを追加するには ~/.vimrcに次の様に追加する必要がある:

call plug#begin('~/.vim/plugged')

Plug 'altercation/vim-colors-solarized'

call plug#end()

" -- solarized personal conf
set background=dark
try
    colorscheme solarized
catch
endtry

2.2. 最低限のエチケット

行の後ろに空白を見かけたら削除しているはずだ。

Trim whitespaces
Plug 'bronson/vim-trailing-whitespace'

:FixWhitespaceで余計な空白を掃除出来る。

そしてもう一つ、80カラム問題。

if (exists('+colorcolumn'))
    set colorcolumn=80
    highlight ColorColumn ctermbg=9
endif
80th column

3. File Management

プログラミングにとって密かに重要なスキル、それはプロジェクトの中からファイルを探し、そして見つけ出す能力だ。

巷では NERDTreeの様な物が好まれて使われている。これは古典的な左カラムにプロジェクトのファイルをツリー表示する。私はこれを使うのは辞めてしまったし、君たちもそのはずだ。

私は uniteに移行した。左カラムは無くなっている。ファイルを見つけるのが速い。だいたい OS X の Spotlight の様に動作する。

まず ag (the silver search)をインストールする。もし ackagのどちも知らなかったのであれば、君の人生はとても良くなるはずだ。これは単純だがとても本質的なツールだ。言うなればホルモン注射をした grepだ。

" Unite
"   depend on vimproc
"   ------------- VERY IMPORTANT ------------
"   you have to go to .vim/plugin/vimproc.vim and do a ./make
"   -----------------------------------------
Plug 'Shougo/vimproc.vim'
Plug 'Shougo/unite.vim'

...

let g:unite_source_history_yank_enable = 1
try
  let g:unite_source_rec_async_command='ag --nocolor --nogroup -g ""'
  call unite#filters#matcher_default#use(['matcher_fuzzy'])
catch
endtry
" search a file in the filetree
nnoremap <space><space> :split<cr> :<C-u>Unite -start-insert file_rec/async<cr>
" reset not it is <C-l> normally
:nnoremap <space><Plug>(unite_restart)

スペースを2回タイプする。ファイルの一覧が現れる。探したいファイルの文字をいくらかタイプしてみよう。選んでリターンをタイプするとビンゴしたファイルが横分割で開くはずだ。

Unite example

もし何か変になったら <space>rをタイプして unite のキャッシュをリセットしよう。

これで簡単にかつ効率的にファイルを名前で検索出来る様になったはずだ。

多くのファイルからテキストを探す。それには agだ:

Plug 'rking/ag.vim'
...
" --- type ° to search the word in all files in the current dir
nmap ° :Ag <c-r>=expand("<cword>")<cr><cr>
nnoremap <space>/ :Ag

:Agの後に空白を入れるのを忘れずに。

訳者注: ° は azerty キーボード配列だと装飾キーと1つのキーで押せる物です。日本語キーボードでは別の物で代用すべきです。

この2つはプロジェクトで最もパワフルに働くショートカットだ。°は私の azertyキーボードだととてもいい位置にある。*の近くのキーを使うべきだ。

では °はどの様に動作するのか?カーソルの下の文字列を読み取り、全てのファイルから検索する。関数がどこで使われているか探すのにとても便利だ。

<space>/をタイプすると、続く文字列で検索。プロジェクト内の全てのファイルから、そのテキストが現れる部分を検索する。

これでファイル間の操縦がとても簡単に出来る様になったはずだ。


4. 言語にとらわれないプラグイン


4.1. Git

Show modified lines

最後のコミット以降の変更行が見れる。

Plug 'airblade/vim-gitgutter'

And the “defacto” git plugin:

Plug 'tpope/vim-fugitive'

最後のコミットからくわえられた変更は :Greadでリセット出来る。:Gwriteで変更が登録出来る。

Reset changes

4.2. Align things

Plug 'junegunn/vim-easy-align'

...

" Easy align interactive
vnoremap <silent> <Enter> :EasyAlign<cr>

選んで Return、そして spaceだ。Returnを多めにタイプするとアライメントが変更される。

2つ目のコロンで揃えたいなら、Returnそして 2、最後に spaceだ。

Easy align example

4.3. 基本的な自動補完: C-n& C-p

Vim 基本的な自動補完システムを備えている。ショートカットは挿入モードで C-nC-pだ。殆どのケースにおいてこれらは、ほぼ申し分ないくらい良く動作する。例えば言語の構成済みではないファイルを開く場合など。


5. Haskell

私の現在の Haskell プログラミング環境はグレイトだ!

私はファイルを保存するたびに、エラーや、どのように自分のコードを改善すべきかといった提案をコメントに書いている。

だったらこれだ:

cabal install ghc-modghc-modをインストールするのを忘れるなよ:

" ---------- VERY IMPORTANT -----------
" Don't forget to install ghc-mod with:
" cabal install ghc-mod
" -------------------------------------

Plug 'scrooloose/syntastic'             " syntax checker
" --- Haskell
Plug 'yogsototh/haskell-vim'            " syntax indentation / highlight
Plug 'enomsg/vim-haskellConcealPlus'    " unicode for haskell operators
Plug 'eagletmt/ghcmod-vim'
Plug 'eagletmt/neco-ghc'
Plug 'Twinside/vim-hoogle'
Plug 'pbrisbin/html-template-syntax'    " Yesod templates

...

" -------------------
"       Haskell
" -------------------
let mapleader="-"
let g:mapleader="-"
set tm=2000
nmap <silent> <leader>ht :GhcModType<CR>
nmap <silent> <leader>hh :GhcModTypeClear<CR>
nmap <silent> <leader>hT :GhcModTypeInsert<CR>
nmap <silent> <leader>hc :SyntasticCheck ghc_mod<CR>:lopen<CR>
let g:syntastic_mode_map={'mode''active''passive_filetypes': ['haskell']}
let g:syntastic_always_populate_loc_list = 1
nmap <silent> <leader>hl :SyntasticCheck hlint<CR>:lopen<CR>

" Auto-checking on writing
autocmd BufWritePost *.hs,*.lhs GhcModCheckAndLintAsync

"  neocomplcache (advanced completion)
autocmd BufEnter *.hs,*.lhs let g:neocomplcache_enable_at_startup = 1
function! SetToCabalBuild()
    if glob("*.cabal") != ''
        set makeprg=cabal\ build
    endif
endfunction
autocmd BufEnter *.hs,*.lhs :call SetToCabalBuild()

" -- neco-ghc
let $PATH=$PATH.':'.expand("~/.cabal/bin")

では楽もう!

hlint on save

私はリーダー(訳者注: キープレフィックス) に -を使う。,は本来の仕様方法として良く使うからだ。

  • -htはカーソル下にあるブロックの型をハイライト表示する。
  • -hTは現在のブロックの型を挿入する。
  • -hhは選択のハイライトを消す。
Auto typing on save

6. Clojure

Rainbow parenthesis

私の仕事上での主言語のは Clojure だ。そして私の現在の vim 環境はまったくもって良い。lein-kibitの自動化に関する統合が足りていない。いつの日か勇気が湧けばやるつもりだ。しかし、原因である Clojure の非常に長い起動時間を考えると、便利な vim プラグインを作ることが出来るのか疑わしい。

実際に虹のカッコを見る事が出来るだろう(デフォルト値は solarized で壊れているけども)。

私は以前は pareditプラグインを使っていた。しかしそれはとても限定的だった。今は vim の概念をより首尾一貫した sexpを使っている。

" " -- Clojure
Plug 'kien/rainbow_parentheses.vim'
Plug 'guns/vim-clojure-static'
Plug 'guns/vim-sexp'
Plug 'tpope/vim-repeat'
Plug 'tpope/vim-fireplace'

...

autocmd BufEnter *.cljs,*.clj,*.cljs.hl RainbowParenthesesActivate
autocmd BufEnter *.cljs,*.clj,*.cljs.hl RainbowParenthesesLoadRound
autocmd BufEnter *.cljs,*.clj,*.cljs.hl RainbowParenthesesLoadSquare
autocmd BufEnter *.cljs,*.clj,*.cljs.hl RainbowParenthesesLoadBraces
autocmd BufEnter *.cljs,*.clj,*.cljs.hl setlocal iskeyword+=?,-,*,!,+,/,=,<,>,.,:
" -- Rainbow parenthesis options
let g:rbpt_colorpairs = [
    \ ['darkyellow',  'RoyalBlue3'],
    \ ['darkgreen',   'SeaGreen3'],
    \ ['darkcyan',    'DarkOrchid3'],
    \ ['Darkblue',    'firebrick3'],
    \ ['DarkMagenta''RoyalBlue3'],
    \ ['darkred',     'SeaGreen3'],
    \ ['darkyellow',  'DarkOrchid3'],
    \ ['darkgreen',   'firebrick3'],
    \ ['darkcyan',    'RoyalBlue3'],
    \ ['Darkblue',    'SeaGreen3'],
    \ ['DarkMagenta''DarkOrchid3'],
    \ ['Darkblue',    'firebrick3'],
    \ ['darkcyan',    'SeaGreen3'],
    \ ['darkgreen',   'RoyalBlue3'],
    \ ['darkyellow',  'DarkOrchid3'],
    \ ['darkred',     'firebrick3'],
    \ ]

Clojure での作業が非常にスムーズになる。コードの任意の部分を eval することができる。別の端末で Clojure REPL を手動で起動しておく必要がある。


7. 最後に

これらが役に立つであろうと願っている。

最後に大事な事を言い忘れたが、もし私の vim の設定を使ってみたいのであれば、ここから得る事が出来る:

github.com/yogsototh/vimrc


実践Vim 思考のスピードで編集しよう! (アスキー書籍)実践Vim 思考のスピードで編集しよう! (アスキー書籍)
Drew Neil
KADOKAWA / アスキー・メディアワークス / (2014-01-28)
 
発送可能時間:

Viewing all 121 articles
Browse latest View live