[emacs] Emacs LispでWebAppの開発に必要なN個のこと

求められている気がしたので、EmacsLispについて知っていることを書いておこうと思った。

パッケージマネージャ

Cask を使うのが今風。先日CartonというPerlのパッケージマネージャと名前がかぶっていたので、改名された。

ちなみに、普段使いであれば、Emacs24.xから標準で入っているpackage.elを使うか、el-getを使う。

アプリケーションサーバ

イベントドリブンなElnodeと、httpd.elをpure Emacsで書きなおした simple-httpd.el がある。

他にもElservもあるが入手困難。

リクエストパラメータの処理

Elnodeであれば、elnode-http-param関数からassoc listで取れる。
simple-httpd.elは、得意の正規表現で頑張る。

ルーティング

Elnodeであれば、elnode-hostpath-dispatcherというよく見るようなディスパッチャーがある。
それ以外はちょっとよくわかりません。

データベース

強引にCLI経由でアクセスする mysql.el と、TCPで頑張ってドライバを書いた pg.el がある。

NoSQLなMongoDBもpure Emacsでアクセスできる。

拙作の edbi.el を使えば、Perl DBI でアクセスできるものなら大抵のDBにアクセスできそう。

ORMのようなものは、今のところ見たこと無い。

ビューのレンダリング

そもそもEmacsなのでテキスト処理は得意なはず。orgmodeの機能を使って変換するのもいいかもしれない。

Mustacheという標準的テンプレート形式のテンプレートエンジンや、PerlのText::MicroTemplate的なものがある。(2013/09/11 追記。kitokitokiさんブコメより)

JSONであればjson.elがあるが多少遅いので、LispらしくprincしてS式で返すのが速い。

HTTPクライアント

標準のurl.elがあるが、@tkfさんのrequest.elが使いやすくておすすめ。

テストフレームワーク

標準で ert というフレームワークがあるので、Emacs24時代はこれを使うのが普通みたい。

Ecukes という、 Cucumber 的な振る舞い記述式のテストフレームワークがある。
GUIの操作を記述してテストする場合は、こちらを使うと大変便利。

Caskと組み合わせてgithubとTravis-CIでテストを回すのがクール。

アプリケーションフレームワーク

すみません。ちょっとよくわかりません。
エディタの上で動くMVCフレームワークであれば、今 Widget MVC というものを作っていますが、多分求められているものと違う気がします。


以上。

CSSガイドラインを翻訳してみた

Harry Roberts氏によるCSSガイドライン(High-level guidelines from writing manageable, maintainable CSS)のドキュメントが素晴らしすぎたので、勢いで翻訳してみました。(ロシア語、中国語、フランス語に引き続いて4番目...)

個人的な主観で見所を選んでみると、以下のような感じです。

  • クラスの名前付け方法(ネーミングルール)
  • セレクターの設計、考え方
  • !importantの使い所

最近見るようになって来たBEM命名法についても説明してあります。

そもそもWebのデザインをどうするかという問題は置いておくと、CSSを書くという事は、どうやってそのデザインを実現するかというHowTo的なものと、どうのように名前付けやエレメントの設計を行なってメンテナンス可能にするかというノウハウ的なものという技術が必要とされると思っています。

前者のコーディングHowTo的なものは山のように記事や本があるのですが、後者のドキュメントはあまり見たことがありません。枝葉的なCSSのコーディング規約だと、以下のようなものがあります。

これらよりも、もっと設計レベルのノウハウをずっと探していたのですが、著名なデザイナーから盗めみたいな感じでしかなかったので(自分がCSSコミュニティにあまり詳しくなかったというのもあるかもしれません)、CSSの設計は確立されてないみたいなので難しいなと思っていました。

そんな時、このドキュメントとSMACSSを見て大変感動しました。

まさにこれが欲しかったものでした。待ってたらそのうちネタ帳みたいな人が翻訳してくれるかなと期待していたのですが、BEMについても一向に広まる気配がなかったので、出張の移動時間などで適当に翻訳してみました。

low specificity など適当な日本語が見つからないものもあったり、日本語の変なところもあると思いますので、ご意見やプルリクをいただけると大変嬉しいです。(原文チェック用に日本語・英語併記のものもあります)

コメントでspecificityについて教えてもらいました。 (2013/09/05 追記)

2013年上半期、近況など

去年ぐらいから、マネージメントからサービス開発に仕事がシフトしてきたため、すっかりはてなを書くモチベーションが下がってしまいました。コード書いたりサーバー構築作業で日々満足してしまっているのが原因と思われます。

前回記事から半年ぐらい経つので、近況をアップデートなどを。

最近の成果

githubにて「ドキュメントが一切ない」というバグが立てられてしまって、むきーってなってドキュメントを書きました。

とはいえ、まだ途中です。

epcは、いつもお世話になっております @tkf さんの jedi.el で拾って頂きまして、世界中のパイソニスタさんの元でひっそりと動いているようです。意図通りに使っていただけて嬉しく思っております。

edbi, ctable でいくつかバグ修正行なっています。

JavaScriptのエディタ支援環境 Tern がすごすぎたので、関西Emacsで紹介したり、VimやEmacsのパッチをコミットしたりしました。

関西Emacsの感想や、Ternの紹介などを書こうと思いつづけて、かなり経過してしまいました。すいません。すいません。

widget-mvcやctableの非同期化が目下取組中の案件です。

Emacsコミュニティ

Emacs JP というサイトが出来ました。

ここのIssuesというプロジェクトにて、Issueとして質問やトラブルなどを登録すると、日本語の分かるEmacsエンジニアがよってたかって答えてくれると思います。

RubyMagazineを参考にして、Emacsについての道標的な記事を書きました。Emacsやってみたいけどインストールはどうしたらいいか、情報はどこ見ればいいかなどの疑問に答えられるような、とりかかりになる情報を自分が知ってる範囲でまとめてみました。

このサイトを、 http://vim-jp.org/ ぐらいかっこよくて便利なサイトにしてやりたいと思っているところです。

あと、LingrにEmacs部屋があります。

あんまり常駐している人が居なくて過疎っていたのですが、lingrでの発言を拾って適当にツイートしてくれるボットと、lingr内でelispを評価して返すボットを書いてみました。

以前よりは多少人が集まるようになったように思います。もしよろしければ、こちらにもぜひどうぞ。

今年のEmacs/Vim温泉予定

昨年やりましたEmacs/Lisp温泉ですが、今年もやりたいなと考えております。場所は九重付近の温泉付き貸切別荘などを予定しておりますので、興味がある方は、以下の登録画面から希望の日程などを教えていただければと思っております。Vimな人もエディタを愛する仲間として大歓迎でございます。


こちらからは以上です。

手元の init.el を淡々と紹介する

Emacs Advent Calendar 2012の記事です。昨日は @syohex さんのEmacsでポモドーロテクニック - Qiitaでした。

さて、今回は手元の init.el のネタを淡々と紹介してみます。
みんな知っているよねというネタばかりだとは思いますが、逆に珍しかったりするものもあるかもしれないので、すこしでもお役に立てれば幸いです。

構造化

まず、設定ファイルの構造化ですが、いろいろ試した結果以下のような感じで分けることで定着しました。

  • init.el 以下のファイルを読み込み
    • base 外部ライブラリに依存しない標準Emacsの設定
    • frame GUIなど見た目周りの設定
    • utils 便利ツールの読み込み、設定
    • modes 各種modeの読み込み、設定
    • keybinds グローバルキーバインドの設定
    • completions, e2wm, howm, org, wl, ... 各種機能ごとの大まかな設定

メインの環境がWindowsやLinuxなどを行ったり来たりしていたので、移行の手間がなるべく楽になるようにしています。
base だけはOSやバージョンにあまり依存しないようにしておいて、最低限の動きを確保した上で、他のファイルをあわせていく感じです。
Emacsを動かすのはメインの環境だけなので、環境を見て分岐することはもうなくなりました。(メイン以外はVimが多いです。Windowsだとsakuraとか。)

キーバインドの設定を一箇所で行うようにしています。これで、キーバインドがかぶってしまって、どこで設定していたのか迷わなくなる心配がなくなりました。

この形で完全に納得しているわけではなく、もっといい方法はないかなと模索しています。

便利ツール色々

その場のファイル名やURLを開く ffap

細かいですが、必須です。

自動繰り返し dmacro

どう考えてもキーボードマクロの方が正確で確実だったりするのですが、
ちょっとしたマクロはずっとこっちを多用しています。

やっぱり「モード(状態)」が無い設計がいいのかもしれません。

ウインドウ切り替え elscreen

この手のものであれば、 windows.el か elscreen だと思いますが、elscreenを使っています。

普通だとバッファのヘッダー行にタブが表示されるのですが、ヘッダー行を占領してほしくないので、フレームのタイトルに一覧を表示させています。以下その設定コードです。

(defvar elscreen-my-title-maps '()) ; タイトルの変換をする

(defun my-elscreen-truncate-screen-name (screen-name truncate-length &optional padding)
  (let ((truncate-length (max truncate-length 4)))
    (cond
     ((> (string-width screen-name) truncate-length)
      (concat (truncate-string-to-width screen-name truncate-length nil) "~"))
     (padding
      (truncate-string-to-width screen-name truncate-length nil ?\ ))
     (t
      screen-name))))

(defun elscreen-frame-title-update ()
  (when (elscreen-screen-modified-p 'elscreen-frame-title-update)
    (let* 
        ((screen-list (sort (elscreen-get-screen-list) '<))
         (screen-to-name-alist (elscreen-get-screen-to-name-alist))
         (tab-width (elscreen-e21-tab-width))
         (title (mapconcat
		   (lambda (screen)
		     (format 
              "%s"
              (let ((label 
                     (elscreen-e21-tab-escape-%
                      (my-elscreen-truncate-screen-name
                       (reduce (lambda (x f) (funcall f x)) elscreen-my-title-maps
                               :initial-value (get-alist screen screen-to-name-alist))
                       tab-width t))))
                (if (eq screen (elscreen-get-current-screen))
                    (concat "【" label "】") label))))
		   screen-list " ")))
      (if (fboundp 'set-frame-name)
	  (set-frame-name title)
	(setq frame-title-format title)))))

(eval-after-load "elscreen"
  '(add-hook 'elscreen-screen-update-hook 'elscreen-frame-title-update))

;; elscreen-my-title-maps の使い方例

(defun skype--elscreen-title-name-map (x)
 (if (string-match "Skype\\(Chat\\|Message\\):\\[\\(.*\\)\\]$" x)
     (let* ((title (match-string 2 x))
            (buf (get-buffer x))
            (missed (if buf (skype--chat-missed-p 
                             (buffer-local-value 'skype-chat-handle buf))
                      nil)))
       (concat (if missed "★" "☆") title))
   x))
(add-to-list 'elscreen-my-title-maps 'skype--elscreen-title-name-map)

あんまり見ることが無いので、たまに確認するぐらいならこれで十分かなという感じです。

C-w で単語削除、

シェルと同様の操作はかなり快適です。また、もともとの機能も無駄になっていません。

(defun kill-region-or-backward-kill-word ()
  (interactive)
  (if (region-active-p)
      (kill-region (point) (mark))
    (backward-kill-word 1)))

; (global-set-key (kbd "C-w") 'kill-region-or-backward-kill-word)
クオートの取り換え

", ', ` をトグルします。RubyやJSでクオート変えることがよくあるので。

transpose-chars-or-swap-quotes を C-t に割り当てています。

(defvar swap-quotes-list
  '((?\" ?\') (?\' ?\`) (?\` ?\")))

(defun transpose-chars-or-swap-quotes (arg)
  (interactive "p")
  (or (swap-quotes) (transpose-chars arg)))

(defun swap-quotes ()
  (interactive)
  (catch 'break
    (dolist (i swap-quotes-list)
      (let ((target-char (car i))
            (replaced-char (cadr i))
            (prev-pos (point)))
        (if (= (char-after (point)) target-char)
            (save-excursion
              (forward-char 1)
              (let ((next-pos (re-search-forward (char-to-string target-char))))
                (if next-pos
                    (subst-char-in-region prev-pos next-pos target-char replaced-char)
                  (message "The corresponding quote is not found.")))
              (throw 'break t)))))
    nil))
スペースを一気に削除

コードをコピペしたり行をつなげたりすると、スペースを大量に何とかする必要があって、そんな時によく使ってます。カーソールの場所がスペース文字かそうでないかで動きが変わります。

kill-word-or-delete-horizontal-space を M-d に割り当てています。

(defun kill-word-or-delete-horizontal-space (arg)
  (interactive "p")
  (let ((pos (point)))
    (if (and (not (eobp))
             (= (char-syntax (char-after pos)) 32)
             (= (char-syntax (char-after (1+ pos))) 32))
        (prog1 (delete-horizontal-space) 
          (unless (memq (char-after pos) '(?( ?) ?{ ?} ?[ ?]))
            (insert " ")))
      (kill-word arg))))
let にアスターを後で付ける

アスターなるべく付けないほうがいいのかなと思っていて、そうすると後で必要になった時にカーソールを移動するのが面倒なので、よく使ってます。


日本語逆変換

間違えて確定して逆変換したいことがたまにあるのですが、IMによってはサポートしてなかったりするのでEmacs内で逆変換を実現させてみました。形態素解析などを使って強引に漢字からローマ字に変換して、キー入力エミュレートして変換させます。

mozcとatokとanthyとかの連文節変換を仮定しています。MeCabかChaSenが必要です。

下はIIIMECFでいろいろ実験してた時の動画ですが、逆変換の動きもあります。



js2-mode

moozさんのところのjs2を使っています。最高です。

JSには決まったクラスの書き方があまりなくて、各エディタや支援ツールが解析に苦労しているところです。
js2でもそこをうまく考えて設計してあり、予めメジャーなクラスシステムのパターンが登録してあるのですが、ASTのパターンを使って独自のクラスシステムも登録できるようになっています。

例えば、以下のようなプロトタイプ指向なクラスシステムがあった時、

// Class1をクローンして上書き
var Class2 = clone(Class1, {
    method1: function() { ... },
    field1: 123
});

以下のようなコードでこのパターンを登録できます。

(eval-after-load 'js2-mode
  '(progn
     (require 'js2-imenu-extras)
     (push 
      `(:framework object-clone
                   :call-re   ,(concat "\\_<clone(" js2-mode-identifier-re "\\s-*,\\s-*")
                   :recorder  js2-imenu-record-object-clone-extend)
      js2-imenu-extension-styles)
     (js2-imenu-extras-setup)))

(defun js2-imenu-record-object-clone-extend ()
  (let* ((node (js2-node-at-point (1- (point)))))
  (when (js2-call-node-p node)
    (let* ((args (js2-call-node-args node))
           (methods (second args))
           (super-class (first args))
           (parent (js2-node-parent node)))
      (when (js2-object-node-p methods)
        (let ((subject (cond ((js2-var-init-node-p parent)
                              (js2-var-init-node-target parent))
                             ((js2-assign-node-p parent)
                              (js2-assign-node-left parent)))))
          (when subject
            (js2-record-object-literal methods
                                       (js2-compute-nested-prop-get subject)
                                       (js2-node-abs-pos methods)))))))))

多少認識の甘いところがあるのですが、だいたいうまくいっています。

MochikitとかEXT.jsなどのクラスシステムとかでも、うまく認識させることができそうです。

smartchr でPHPの頻出文字を入力

こんなのを多用するのもどうかとは思いますが、そういう仕事も多いので、以下のような感じで使っています。

(eval-after-load "php-mode"
  '(progn
     (define-key php-mode-map (kbd "F") (smartchr '("F" "$" "$this->")))
     (define-key php-mode-map (kbd ">") (smartchr '(">" " => ")))
     (define-key php-mode-map (kbd "?") (smartchr '("?" "<?php `!!' ?>" "<?php echo `!!' ?>")))
     (define-key php-mode-map (kbd "a") (smartchr '("a" "array(`!!')")))
     (define-key php-mode-map (kbd "[") (smartchr '("[" "[`!!']" "[\'`!!'\']")))
     ))

(eval-after-load "sgml-mode"
  '(progn
     (define-key sgml-mode-map (kbd "\"") (smartchr '("\"" "\"`!!'\"" "&quot;`!!'")))
     (define-key sgml-mode-map (kbd ">") (smartchr '(">" "&gt;")))
     (define-key html-mode-map (kbd "=") (smartchr '("=" "<%= `!!' %>")))
     (define-key html-mode-map (kbd "%") (smartchr '("%" "<% `!!' %>")))
     ))

smartchr にするか yasnippet にするかは、悩みどころです。

キー入力数と言うよりは、リズムとか気持ちよさの問題なのかなと思っています。



以上です。

明日(すいません。今日ですが。。。)は id:uhiaha888 さんです。よろしくお願いいたします。

Emacs/Lisp温泉やりました

熊本という場所とEmacs/Lispというネタで、温泉合宿が成立するのかとても不安でしたが、蓋を開けたら予想以上に人が集まりました。また、温泉当日もかなり盛り上がりまして、初回としてはかなり成功したのではないかと思っています。自分もとても楽しかったです。

参加して頂きました皆様、ご協力ありがとうございました。また、残念ながら都合により参加出来なかった皆様、また次回どこかで温泉やると思いますのでぜひその時もまた検討してみてください。

準備・運営メモ

人気の黒川・久住・湯布院あたりを探していたのですが、半年以上前から予約しないと難しかったようです。あと、車でしか行けないような場所もあるため、温泉を取るか交通の便を取るかで迷いました。結局、人数にも余裕があって交通の便もいい玉名温泉にしました。今回のこの選択は割と良かったと思います。バスをチャーターするとかワゴンを調達して、もう少し輸送のことを考える余裕があれば、秘境のグレードの高い温泉も行けるかもしれません。

これまでに参加してきたEmacsの会の経験から、温泉に集まってノートPC広げるだけで面白くなるはずという確信を持っていたのですが、みんな黙々とPCに打ち込むようなことがあるとまずいと思い、OSTを準備していました。結果、全く出番がないくらいみんな持ちネタを発表して盛り上がりました。Emacsの勉強会では、誰かのPCをプロジェクターに写して操作を眺めるだけで盛り上がれるところがいいですね。

食事・温泉後のハカソンはチームとか作るような事を考えていたのですが、自分も含めて眠そうな人も少なくなかったので、それぞれ自由に活動するようにしました。午前2時ぐらいまではみんな起きていたのですが、その後は2/3ぐらいの人は寝ていたと思います。ということで、ハカソンの時間というのは実質あまりなかったので、ハカソン目的の温泉合宿にするなら、2泊ぐらいするといいのかなと思いました。

仕事が少し忙しくて、人数の確認がすこし甘かったところが良くなかったです。ただ、ATND登録の参加率100%だったので、幹事としては大変助かりました。

当日のタイムスケジュールを作っていたのですが、それを紙に印刷して掲示しておくと良かったかなと思いました。次に何をするのか分からなくて不安にならないように努めたつもりですが、もし不手際がありましたらすみません。次回改善したいと思います。

参加者の内訳は、大阪・京都5人、岡山1人、福岡2人、佐賀1人、熊本5人でした。関西Emacsの皆さん、遠くから来ていただいてありがとうございました。熊本・佐賀の皆さんには、現地の人の輸送などで大変助けて頂きました。ありがとうございました。

内容など

twitterでいろいろ書いたので、ハッシュタグ #Emacs温泉 を追いかけるとなんとなく雰囲気がつかめるのではないかなと思います。

Vim使う人もかなりいて、仲良くキャッキャウフフしていたので、実態はEmacs/Vim温泉でした。

朝の風景。徹夜組はぐったり。

机の上の風景。プロジェクターで投影。


自分は、ctable.elやwidget-mvc.elなどを紹介したり作ったりしていました。widget-mvc.elについてはそのうち紹介できるかもしれません。ただ、最近は久々にコード書く仕事で詰まっていて、なかなか時間が取れてないです。プルリクエストも溜まっていてすみません。

今回面白い内容はたくさんあったのですが、やはり@nom4476さんのorg操作がとても感動でした。自分のEmacs力は遠く及ばないと認識しました。

次回温泉

九州と大阪の間の岡山でやろうとかいう話もありました。福岡だと二日市温泉とか交通が便利でいいかもしれません。会社や知人のプライベート温泉付き別荘が阿蘇や由布院にいくつかあるので、車をチャーターしてそういう所に行くというのもいいなあと思っています。ということで、もしまた集まりそうな雰囲気がありましたら次回温泉があると思います。

レポートリンク

もし、書いたよという人がおられましたら、トラックバックかご連絡いただけたら、こちらにまとめたいと思います。

近況

4月頃にメインマシンのハードディスクが飛んで、はてなにアップする予定の記事がいくつかなくなってしまって、記事がなかなか書けずにおりました。

仕事も忙しくて、記事やgithubの対応が出来てなくてすいません。

Emacs/Lisp温泉やります

温泉とLispの地である熊本で、EmacsとLispをネタに泊まりこみで集まるイベントを行います。

  • 日程 2012/08/25 1泊2æ—¥
  • 場所 熊本玉名 白鷺荘別館
  • Emacs / Lisp 温泉 : ATND

初回なので、まだどんな風になるか決まっていませんが、明星和楽のようなクリエイティブなイベントになるといいなと思っています。

定員は16人としていますが、まだ宿に余裕があるようなので、補欠の人でもある程度(20人ぐらい?)受け入れられそうです。8月5日ごろに人数の確定をする予定です。

EmacsやLispが好きな人、ぜひよろしくお願いいたします。