ぐるっとぐりっど

日曜プログラマがいろいろ試してみたことを、後の自分のためにまとめておく場所

use-packageで最近知った機能3選

最近、.emacsの整理をしていて、use-packageがデフォルトでdiminishを付属させないことを知りました。

で、ついでにREADME.orgをあらためて読みかえしてたら、これまで知らなかった面白い機能がいくつかあることを知ったので紹介。
(package.elを使うと、emacs上でアップデートができてしまうので、破壊的な変更に気付かずに、いつのまにか挙動変わってる...ってことがしばしばあるのが厄介ですね)

ちなみに、use-packageの基本的な使いかたは、kai2nenobuさんの、この記事がお勧め。

qiita.com

imenuに対応させる

個人的に今回かなりのヒットだったのがこれ。

(setq use-package-enable-imenu-support t)
(require 'use-package)

とすると、emacslisp-modeの場合に、use-packageの各パッケージ設定がimenuに載ってきてくれる。
ポイントは、requireの前に書くこと。

f:id:grugrut:20190105202730p:plain
use-packageをimenuに対応

これまで、init.elから目的の設定を探すのに、isearchしてみたり、helm-swoopしてみたりと苦戦していたのだけど、一発でジャンプできるようになった。

use-packageの状況を確認する

(use-package-compute-statistics t)

しておくと、M-x use-package-reportで、各パッケージの読み込み状況(:configまで進んだとか、:initまでとか)がわかる。

f:id:grugrut:20190105203002p:plain
use-package-report

状況が変化した日時と時間がわかるのだけど、多分累計じゃないと思う。org-modeのloadがこんな短いわけがないので。

key-chordを設定する

key-chordは、emacsに同時押しを実現するパッケージです。
use-packageのマクロにこの設定を組み込むことができるらしい。

(use-package use-package-chords
  :ensure
  :config (key-chord-mode 1))

(use-package view
  :ensure
  :defer t
  :chords (("jk" . view-mode)))

こんな感じで、:chordsキーワードが使えるようになる。
ただ残念なのが、使えるのがオリジナルのkey-chordなので、暴発問題が残っている。
こちらの改良版の方が便利だと思うので使いどころかも(うまく上書きできればよさげだけど)

qiita.com

ちなみに、このマクロ拡張のための機能が提供されているようで、純正のもの以外にも、hydraを使えるようにするuse-package-hydraなんてのもあるみたい。

おわりに

README.orgを読むと、他にもいろいろ書いてある(:afterキーワードでandとかorとか使えるってことも知らなかった。使いどころは?だけども)ので、当然のことながら、原典をまずは読むべし。

Emacsで簡単にゲームが作れるgamegrid.el

この記事は、Emacsアドベントカレンダー 18日目の記事です。

qiita.com

ちょっとEmacsで、わけあって *1 ドット絵的なものを書きたく、いろいろ調べてるうちにgamegrid.elに出会いました。
gamegrid.elは、Emacs本体に同梱されている、まさにgridなgameを作るためのパッケージです。
gamegrid.elで調べると、いろいろ出てきますが、emacsに内蔵されているpongとかsnake、tetrisなんかに使われています。

そして、使いかたがよくわからず、ぐぐっても使ってみた的記事があっても使い方がよくわからなかった(みつからなかったけど、infoに該当セクションあったりするのかな?)ので、実際使われているものをソースコードリーディングしながら使ってみました。

ソースコードリーディングする上では、snakeゲームのソースコードがわかりやすかった。tetrisはちょっと複雑。
github.com

作ったもの

github.com

f:id:grugrut:20181217225251g:plain
作ったもの

よくある上から降ってくるキャラを避けるゲーム。キー入力周りのところが若干おかしい。

内容と解説

理解しきれてないところもあるので、間違いあればマサカリください。

どうやって描画するか

(defconst sample1-player 0)
(defconst sample1-block 1)
(defconst sample1-blank 7)
(defconst sample1-border 8)
(defconst sample1-space 9)

https://github.com/grugrut/sandbox-gamegrid/blob/19418173db7c17a2899e8247c6166bb89e11bd27/sample1/sample1.el#L39-L43

gamegridの名のとおり、gridベースなので、x, y座標に対して、そのセルが何であるかを指定することになる。その指定のための定数定義。

(defvar sample1-player-options
  '(((glyph colorize)
     (t ?\040))
    ((color-x color-x)
     (mono-x grid-x)
     (color-tty color-tty))
    (((glyph color-x) [1 0 0])
     (color-tty "red"))))

https://github.com/grugrut/sandbox-gamegrid/blob/19418173db7c17a2899e8247c6166bb89e11bd27/sample1/sample1.el#L83-L90

(defun sample1-display-options ()
  "."
  (let ((options (make-vector 256 nil)))
    (dotimes (c 256)
      (aset options c
            (cond ((= c sample1-blank)
                   sample1-blank-options)
                  ((= c sample1-player)
                   sample1-player-options)
                  ((= c sample1-block)
                   sample1-block-options)
                  ((= c sample1-border)
                   sample1-border-options)
                  ((= c sample1-space)
                   sample1-space-options)
                  (t '(nil nil nil)))))
    options))

https://github.com/grugrut/sandbox-gamegrid/blob/19418173db7c17a2899e8247c6166bb89e11bd27/sample1/sample1.el#L127-L143

先の定数あたりも使って、どのセルをどう描画するかを決めるところ。Xの場合は、 [1 0 0]のようにvectorで、r g bを0から1の範囲で設定する。
ターミナルの場合は、"red"のように、指定できる。ターミナルの場合になにが使えるのかは調べてない。
また、Xを使う場合は、画像をxpm形式で指定できるはず。未指定の場合は、デフォルトのブロック画像が使われる。

この辺を駆使するため、以下の初期化処理でいろいろ設定する。

初期化処理と毎ターンの描画

基本的にはメジャーモード書いて作る感じ。それ以外のgamegrid特有の内容だと以下な感じ

(defun sample1-start-game ()
  "."
  (interactive)
  (sample1-reset-game)
  ;;(use-local-map sample1-mode-map)
  (gamegrid-start-timer sample1-tick 'sample1-update-game))

https://github.com/grugrut/sandbox-gamegrid/blob/19418173db7c17a2899e8247c6166bb89e11bd27/sample1/sample1.el#L154-L159

gamegrid-start-timerで何ミリ秒間隔でどの関数をコールするかを設定できる。このタイマー間隔がFPS維持するような仕組になってるかどうかは未調査。
sample1-reset-gameの中では、 (gamegrid-kill-timer)でタイマーを念の為リセット + 以下のような感じで、gamegrid-init-bufferにより、gamegridで描画する範囲と初期値を指定している。

(defun sample1-init-buffer ()
  "."
  (gamegrid-init-buffer sample1-width (+ 2 sample1-height) sample1-space)
  (let ((buffer-read-only nil))
    (dotimes (y sample1-height)
      (dotimes (x sample1-width)
        (gamegrid-set-cell x y sample1-space)))))

https://github.com/grugrut/sandbox-gamegrid/blob/19418173db7c17a2899e8247c6166bb89e11bd27/sample1/sample1.el#L173

ついでに、gamegrid-set-cellを使うことで、指定したセルを別の内容に塗り替えることができる。
なので、updateの内容も、変更点を計算して、キャラが移動する場合は、前の時刻のセルを消して、新しい座標のセルを描画する、とかの処理をすることになる。

ゲームの終了

(defun sample1-stop-game ()
  "."
  (interactive)
  (gamegrid-kill-timer)
  (gamegrid-add-score "sample1-scores" sample1-total-cell))

https://github.com/grugrut/sandbox-gamegrid/blob/19418173db7c17a2899e8247c6166bb89e11bd27/sample1/sample1.el#L210-L214

gamegrid-add-scoreを使うことで、ハイスコアランキング(ただしローカル)を簡単に作れて便利

その他

グリフをブロック以外に自分が描画したものも使えるらしいので、試してみたい。

まとめ

なんか旨い使いかたないかなーと思うのですが、更新が激しいものを作るには適してない(結構チューニングが必要)ようなので、ピクロス的なゲームとか作るのには向いてそうです。

*1:挫折したけど途中まで自分も秋口ぐらいからNESエミュレータ作ろとせっせと書いてた。なので、gongoさんのアドベントカレンダーがとても楽しみです

カップラーメンの新発売情報をいつでもチェックできるようにした(by golang)

好きなカップラーメンは、ぶぶか油そばです。こんにちは。

カップラーメンって日々あたらしい商品が出てて、コンビニのカップラーメンコーナーをみて、新商品をチェックしているのですが、昔流行ったスクレイピングすりゃいいんじゃね? という考えに至りました。あとチェックするのは好きですが、食べるのは健康のために控えてます。

そんなわけで、使い慣れようと思っているgolangでちゃちゃっと作ってみました。

http://www.grugrut.net/tools/ramen.html

利用技術

golangスクレイピングするのには、goqueryを使ってます。CSSセレクタライクな構文でさくっととってこれるので手軽でした。

https://github.com/PuerkitoBio/goquery

ページの作成には、標準のhtml/templateを使ってます。普段templateはHttpResponseWriterにくわせるぐらいしかしていなかったのですが、まあ普通にFileWriterにくわせればファイル作成もできるだろうとやってみると、当然のようにできました。bootstrap使うとデザインセンスが皆無でもそれなりのものができて便利。

	file, err := os.Create("ramen.html")
	if err != nil {
		log.Fatalln(err)
	}

	tmpl := template.Must(template.ParseFiles("tmpl.html"))
	tmpl.Execute(file, recordList)

まあページこだわってないとはいえ、もうちょっとデザインは考えたいのと、コンビニの商品とかスナック菓子とか、チェックしたいものはたくさんあるので、汎用化をさせて作りやすくしたいものです。

2017年の振り返り

2年ぶり3回目の1年間の振り返りまとめ。
前回も2年ぶり2回目だったらしいので、隔年でまとめたくなるのかな?

なんか最近時間の流れがあっというまで、今年どんな年だったかすっかり忘れそうなので、やっぱりまとめたほうがよいよなと思う。完全に自分用のまとめです。

github

githubをちゃんと使うようになった。今さらかよ、という感じは自分でもするのだけど、githubが社内プロキシでブロックされてるようなアリエンティな会社なので、使い道がなかった。
ちゃんと使うといっても、まだまだ自分のVPSでgitサーバ立ててたのを、外部サービスに配置しただけ感は否めない。

とはいえ、プルリク送ってみて、マージされたりしてるので、第一歩としては十分だろう。年末だし自分を積極的に褒めていく。
github.com

また、7月ぐらいから毎日コミットするぞ運動を開始して、3か月ぐらい続けた。結構穴だらけなのは、外で飲んでて0時過ぎてしまったことに多く起因する。反省である。
f:id:grugrut:20171231094108p:plain

本当はもっとコミットしてたはずなんだけど、.emacs.dリポジトリをdotfilesリポジトリに移動して(それも単なるmvで)、.emacs.dリポジトリを消してしまったため、そこの情報が多分消失している。.emacs.dは、もともとdropbox管理だったり、ともすれば単なるファイルサーバ配置だったりしてたので、今でもカジュアルに履歴を消してしまう。

emacs

slackとかlingrのinit.el読書会とか、色々なコミュニティに参加した。オンラインだけじゃなくて、もくもく会などにも参加して、そこで知らなかった便利パッケージを教えてもらったりするなどした。LTもプライベートで初めてやって、お仕事の発表と違ってまた楽しかった。

パッケージも作ってMELPAに登録することもできたし。
github.com

学習のまとめ

これまでこっちのブログに適当なメモもがんばってまとめたものも一緒くたにしてたけど、なんかメモは独立させようかな、というのとhugoにも興味があったので、別ブログを作ってみた。ドメインあるのに有効活用これまでしてなかったというのもある。

http://www.grugrut.net/til/

これは以前書いたToday I Learnedをhugoでpublishしたものだ。
grugrut.hatenablog.jp
なので、githubリポジトリに同じ情報がある。org-modeで書いているので、とりあつかいもしやすい。

今後、sandboxリポジトリも作って、書き散らすコードはそっちでまとめて、うまく連携させれないか模索するつもり。

来年の抱負

去年までに比べれば、今年はわりといろいろと精力的に取り組んだほうだと思うけど、なんだかんだ仕事や飲みいくのを理由にさぼってたところがあるので、もっと形になるものを来年は取り組んでいくつもり。

その他

なんかここ1年で白髪がどばっと増えた。3年後ぐらいには半分ぐらい白くなってるんじゃなかろうか。

emacs25で追加されたdynamic moduleを使ってemacs上でパケットキャプチャしてみる

これは、 Emacs Advent Calendar 2017の4日目の記事です。

昨日は、j8takagiさんの連想リストのUPSERTでした。自分はどうせassoc使うと前の方から見て最初にequalだったものを取得するし、と、同じキーが入っても気にせずappendで更新したいキーをつっこんでます。更新型RDBMS的な。

dynamic moduleとは

去年の9月にリリースされたemacs 25.1に追加された機能で、リリース時のメールでは、リリースのハイライトの一番上に君臨しております*1

この辺の説明読んでて、これまで誤解してたのですが、この機能は「Emacsで、ネイティブの共有ライブラリが読めるよ」ということではなくて、「共有ライブラリに、emacsの関数を書いて、emacsでrequireできるよ」が正しいです。
任意のライブラリファイルが読めて、emacsの橋渡しというかラッパーは、emacs lispで書けばいいのかと勝手に思ってたのですが、共有ライブラリのところから自分で書いて、emacsから呼びだす関数やらもろもろ自分で作らないといけないようです。わかってから考えれば当然なのですが。

ちなみに、共有ライブラリは、やはりCで書くのが基本のようですが、調べてみるとgolangでもできるようです。というか本質的には共有ライブラリがコンパイルできる言語であればきっとなんでもよいはず。

ということで、思った以上に使うの面倒そうだったので、実際どんな感じで使うのか、試してみました。
普段はgolang書いてますが、golang使えることがわかったのが、Cで書いては消しを繰り返し、内容を自分で消化したあとだったので、今回は、Cで。

作ったもの

emacsでパケットキャプチャできるもの。先行の有名なソフトに敬意を示して、emacsharkと名付けてみた。今はすべてのIP通信をキャプチャして、送信元と送信先IPアドレスを表示するだけになってます。

f:id:grugrut:20171203130243g:plain

ソースコードは、githubにあります。

github.com

dynamic module用の書き方

既存のもの参考にしたり、ドキュメントを参照しながら書いたのだけど、結構クセがあるので、はまったところや感心したところなどをまとめておく

GPL互換ライセンスであることを明記する
int plugin_is_GPL_compatible;

https://github.com/grugrut/emacshark/blob/master/emacshark.c#L23

GPL互換であることを表明しておかなければならないらしい。 GPLって動的リンクするものまでは問わなかった気がするけど、どうせEmacsプラグインGPL互換ライセンス以外選択する気ないので、深くは考えてない

関数の定義
static emacs_value
Femacshark_init(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{

https://github.com/grugrut/emacshark/blob/master/emacshark.c#L51-L53

/* Bind NAME to FUN.  */
static void
bind_function (emacs_env *env, const char *name, emacs_value Sfun)
{
  /* Set the function cell of the symbol named NAME to SFUN using
     the 'fset' function.  */

  /* Convert the strings to symbols by interning them */
  emacs_value Qfset = env->intern (env, "fset");
  emacs_value Qsym = env->intern (env, name);

  /* Prepare the arguments array */
  emacs_value args[] = { Qsym, Sfun };

  /* Make the call (2 == nb of arguments) */
  env->funcall (env, Qfset, 2, args);
}

#define DEFUN(lsym, csym, amin, amax, doc, data)                        \
  bind_function (env, lsym,                                             \
                 env->make_function(env, amin, amax, csym, doc, data))

  DEFUN ("emacshark-init", Femacshark_init, 0, 0, "Init emacshark", NULL);

https://github.com/grugrut/emacshark/blob/master/emacshark.c#L129-L158

前半が処理を書くところ、後半がemacsで呼ぶための設定を書くところです。
前半のFemacshark_initの第二引数のnargsに相当するところがemacs側で呼んだ際の引数になり、例えば(some-func a b c)としたら、[a, b, c]が配列としてわたされてくる。
あとは、数値を返したければ

return env->intern(env, "nil");

すればいいし、数値や文字列を返したければ、

return env->make_integer (env, 42);
return env->make_string(env, str, len); // strがchar配列、lenが文字列長(\0のカウントは不要)

とすればよい。

後半で、ラッパー部分を定義していて、関数名や引数の最小数、最大数のほか、docstringなんかも書けるので、cでemacs lispを書いているような、そんな気分。

関数を定義する際は、戻り値がemacs_value型の関数を書いたうえで、さらに、いろいろとしないといけないので、結構面倒。
公式のサンプルソースで、なんか関数や関数マクロ駆使して、いいかんじに書く方法紹介されてたので、そのまんま真似したけど、emacs-module.hをincludeするんだから、そっちになんかいい感じにまとめておいてくれたほうが、もっとうれしい。

ポインタの受け渡し

引数渡して、処理した結果を返しておわり、という純粋な関数だけでなく、継続して処理したいという場合があると思います。
今回作ったパケットキャプチャもそうで、init関数で、デバイスをオープンしたりパケットキャプチャを開始して、get関数を呼ぶたびにinit関数で初期化したところから情報をとってくるようになってます。

init関数では初期化情報をemacs側に返却、get関数では返却された情報を返して、それを元にライブラリ側は処理をする必要があるわけです。

もちろん、その辺もできるようになってまして、emacsにポインタを返却する場合は、

return env->make_user_ptr(env, free, handle); //handleがポインタ

https://github.com/grugrut/emacshark/blob/master/emacshark.c#L79

emacsから受けとる場合は、

  pcap_t *handle = env->get_user_ptr(env, args[0]);
  if (env->non_local_exit_check(env) != emacs_funcall_exit_return) {
    return env->intern(env, "nil");
  }

https://github.com/grugrut/emacshark/blob/master/emacshark.c#L88-L91
でいけます。受けとるだけなら、get_user_ptrだけでよいのですが、その後のif文で、問題ないかチェックしていて、おかしいポインタがわたされた場合は、emacs側にもエラーメッセージをミニバッファに表示させることなどができるようになっています。
これができることに気付かず最初開発中は、セグメンテーション違反(通称セグフォ)でemacsごと落ちまくって大変でした。
make_user_ptrの2番目の変数は、開放時に呼ばれる関数のようです。freeしてるだけですが実はlibpcapなんでもろもろcloseしないといけないのでは、、、?という疑惑があります。

所感

もうちょっとカジュアルに使えるものかと思ったけど、結構面倒。とはいえ、だいたい雛形決まってるので、一度書いてしまえば、Makefileソースコードも半分以上は使い回せる気がする(だからこそ、本体側で吸収してくれればもっとカジュアルに書けるのでは、ということでもある)

emacshark自身の展望としては、フィルタを設定できるようにする、とか、(おそらくバグで)initメソッド呼んだとき、ひとつパケットを受けとるまで待機状態に入ってしまうので、その辺をなんとかする、とか、もうちょっと一般的に作れたらmelpaへの登録を試みる、とか、野望は幅広く持っています。

いっけんとっつきにくい、というか前提が多すぎで本質でないところにとらわれてしまうのですが、なんちゃってで書いても、なんだかんだ動くもの作れる(このemacsharkもバグ修正とか細かいところ除けば大筋は、いちからでも2時間ぐらいでできました)ので、ぜひなんかおもしろパッケージが出てくること楽しみにしています。

明日は、ncaqさんです。自作のパッケージを公開するとのことらしいので楽しみですね。

*1:余談ながら、去年のadvent calendarでは、下から二番目のxwdigetについて書きました

emacs25で追加されたdynamic moduleのサンプルを写経して動かしてみた

github.com

XWidgets芸人になるつもりはないので、dynamic moduleもどんなことできるか軽く動かしてみた

公式のサンプルに必要なことは一式書いてあるので、読むとだいたいわかる。いきなり他の英語の解説記事読むより、これに目をとおしてからの方が理解しやすかった。

https://github.com/emacs-mirror/emacs/blob/emacs-25/modules/mod-test/mod-test.c

できること

いくつかの規定どおりのソースをもとにしたsoファイルを作っておけば、emacsから、elispで書いたパッケージと同じようにrequireして、関数を呼ぶことができる

というか、Cのソースのほうで、provideを書いてるので、関数の実体だけCで書いて、あとはemacsパッケージを作っているような気分

とりあえず引数をうけとって、結果を返す関数は書けたので、なんか応用はききそう


なんかgolangで書くこともできるらしく、まあsoファイル作ってよみこむってのがわかれば、そうだよね、という気持ち

Writing Emacs modules with Go

あまりelisp力も高くないので、その辺を高めつつ面白そうなのを作れたらよい

Dockerのコンテナ間で通信させてリバースプロキシを作る

サーバを再構築するついでに、これまでサーバに直nginxなり各種サービスを入れていたのを、Dockerコンテナに載せてみることにした。

プライベートなページもいくつかあったり、監視ソフトの管理画面もあるので、その辺隠しておきたいので、nginxでリバースプロキシにして、BASIC認証でなんちゃって防御してるんだけど、ぐぐってもnginxでリバースプロキシを作る方法(というか、他のコンテナと通信する方法)がいまいち出てこなかったので、調べてなんとかした。

構成

こんなかんじ

f:id:grugrut:20171015210153p:plain

  • nginxがコンテナで動作していて、TCP80とホストのTCP80を対応させておく
  • /grafanaにアクセスされたら、grafanaにアクセスできるようにする
  • grafanaはnginxとは異なるコンテナで動作しており、grafanaはtcp3000で待ち受けている

設定

docker-composeを使っている

nginx側
  • docker-compose.yml
version: '2'
services:
  webserver:
    image: nginx
    volumes:
      - /path/to/www:/usr/share/nginx/html
      - ./default.conf:/etc/nginx/conf.d/default.conf
    ports:
      - "80:80"

networks:
  default:
    external:
      name: shared
  • nginxの設定
(前略)

        location /grafana/ {
          proxy_pass http://grafana:3000/;
        }

(後略)
grafana側
  • docker-compose.yml
version: '2'
services:
  grafana:
    image: grafana/grafana
    volumes:
      - ./grafana.ini:/etc/grafana/grafana.ini
    expose:
      - "3000"

networks:
  default:
    external:
      name: shared
  • grafanaの設定
root_url = http://example.com/grafana
設定のポイント

nginxのリバースプロキシ設定の、URLを、docker-compose.ymlで設定したサービス名にすること。
ここをlocalhostにしたり、ブリッジのIPアドレスにするとうまくいかない。

また、grafana側は、3000ポートをホストと結びつける必要はない(ホスト内部でしか通信しない)ので、portではなくてexposeでよい。

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)