Kuchitama Tech Note

はてな記法がいつまでたっても覚えられないので、はてなダイアリーからマークダウンが使えるこっちに引っ越してきました。

突然のVimコマンドを作った

なぜ作ったのか

基本的には思いつきが全て。 ただ理由を挙げるとするなら、最近vimでいろいろプラグインを使って遊んでたりするので、 自分でも作ってみようかなぁとか思ってたのが大きい。 今回、プラグインではなくコマンドを作成したのは、ここ(C++でゲームプログラミング 【Vim Advent Calendar 2012】Vim プラグインを github で公開するまで【1日目】を読んだから。

なので、そのうちプラグイン化するつもり。

vimスクリプトについて調べるのには、下の3つの情報源が本当に役立った。 ありがとうございました。

解説(というか自分のためのメモ)

コマンドの定義

ユーザコマンドの定義は次の書式 :command[!] [{属性}...] {コマンド名} {実行するコマンド}

関数を呼び出すにはcall {関数}の書式で、callコマンドを通して関数を呼び出さなくては行けない。 それは、面倒なので、自作コマンドとして定義しておく事で、保存するときの:wとか終了の:qみたいなノリで自作関数を呼び出せる。

属性に-nargs={引数の数}を指定し、呼び出す関数に<args>を引数として指定する事で、コマンドの引数を関数に渡せる。

詳細はここを参照↓

Hack #158: ユーザコマンドを定義する

関数定義

関数のスコープにはグローバルスコープとスクリプトスコープがある。 グローバルスコープは関数名がg:から始まる関数のことで、グローバル域で有効になる。 今回だとg:totuzen関数がそれにあたる。

スクリプトスコープはs:から始まる関数で、その名の通りスクリプトファイル内でのみ有効になる。 なので、s:mozisu関数はスクリプトファイル内からしか呼び出せない。

モテる男のVim Script短期集中講座

日本語を含む文字数のカウント

文字数の取得はstrlen({文字列})だが、普通にutf-8のマルチバイト文字をstrlenすると、日本語の文字は1文字あたり3文字分カウントされてしまう。

:echo strlen('a')
#=> 1

:echo strlen('あ')
#=> 3

これを回避するために、substituteを使って、文字を全て1バイト文字に変換してしまってから、strlenで文字数を数える

substitute({ソース文字列},{置換元パターン},{置換文字列},{フラグ})

substitute("ほげfuga", ".", "x", "g")
#=> xxxxxx

strlen(substitute("ほげfuga", ".", "x", "g"))
#=> 6

名無しのvim使い → Vimスクリプトリファレンス → 03. 変数 → 文字列型

備忘録 Vimのスクリプトでの置換用関数

ソースコード全文

使い方

↑上のtotuzen.vimファイルどこかに保存して、:source totuzen.vimとコマンドを実行すればOK

:Totuzen "突然のvim"とかコマンドを入れると、いい感じに突然の死っぽくしてくれる

書いたはいいが…

ここまで書いて、そう言えばVim Advent Calendar 2012がまだ続いてたなー、vimmerの方々はどうやらクリスマス終了のお知らせを受け取ったみたいだなーとか思ったのだけど、このネタすでに増産されてて車輪の再発名もいいところだった… まぁ、当たり前だよね…

ましてや、はじめてVimプラグインを書いた話 - metropolisなんて、ネタかぶりもいいところ… しかも、僕は後発のくせに特に目新しい機能なんてないしね…

でも、あくまで自分の勉強用に始めた事なのでいいのです。 誰かやってるからって何もしないより、とりあえず自分の手を動かしてみる方がいいよね。 趣味コーディングだしね。

というわけで、アドベントカレンダーには参加せずにひっそりとブログ更新です。 (ちなみに、突然の死の先達たちのコードはまだ読んでないです。プラグイン化まで実現してから、ゆっくりと読ませて頂いて、自分の実装と比較するつもり)

Vimテクニックバイブル ?作業効率をカイゼンする150の技

Vimテクニックバイブル ?作業効率をカイゼンする150の技

Clojure+LeiningenでJavaFXプログラミング

JavaFX GUIプログラミング〈Vol.1〉をちょいちょい読み始めたので、 JavaFXアプリをClojure + Leiningenで開発する方法についてまとめる。

JavaFX GUIプログラミング〈Vol.1〉

JavaFX GUIプログラミング〈Vol.1〉

プロジェクトの作成

なにはともあれプロジェクトが無くては始まらないのでプロジェクトを作成

lein new javafx

で、とりあえずJavaFXを実行するにはjfxrt.jarが必要。 jfxrt.jarはMacOSXだと /Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home/jre/lib/jfxrt.jar にあるっぽいので、そこからコピって来た。 ここのディレクトリにパスが通っていれば問題ないと思うのだけど、デフォでは通ってないっぽい。 作成したプロジェクトディレクトリ直下にlibディレクトリを作成して、その中にjfxrt.jarをコピー。

ただ、手動でjarを追加すると、基本的にLeiningenさんはjarを認識してくれないので、project.cljにjarのパスを追加してやる必要がある。 パスを通すには、:resource-pathsというプロパティを追加してやる。

これでコンパイル時のクラスパスにlib/jfxrt.jarが追加される。 ついでに、:mainを追加して、lein runしたときにちゃんと実行されるように設定しておく。

JavaFX Applicationの作成

準備が完了したので、早速プログラムの作成に入る。 Leiningenのデフォルトだと、src/javafx/core.cljが作成されているのだけど、今回は、自分でsrc/kuchitama/javafx.cljを作成し、そこに実装した。 わざわざ自分でパッケージを切った経緯については、@t_nodaさんと@bouzuyaさんのやり取りを元にしている。詳しくはこちらを参照

で、肝心のプログラムだけどこんな感じ

これで、JavaFX GUIプログラミングのP.8あたりのサンプル相当の実装になる。 こいつをベースにいじって行けば、Clojureでいろいろ出来るハズ

課題

このサンプルだと、lein uberjarを実行したときに、生成されるスタンドアローンなjarの中にjfxrt.jarが含まれないので、実はスタンドアローンで動作しない。 これを解決するには、そもそも手動でのライブラリ追加をやめてちゃんとLeiningenさんに依存関係を解決してもらえばいいのだけど、oracleはjfxrt.jarをmavenで公開していないため、それは不可能になっている。 その対応として自分でローカルのmavenリポジトリを作成して、そこにjfxrt.jarを追加するという方法がある。(参考) 確かに、これが一番オーソドックスな方法だと思うのだけど、僕はわざわざjfxrt.jarだけを管理するためにローカル環境にmavenリポジトリを作りたくはなかったので、今回手動追加という方法をとった。 でも、スタンドアローンなjarがスタンドアローンに動作しないのは詐欺もいいところなのでなんとかしたい。 どなたか、方法をご存知の方がいらっしゃればご教授ください。

Play sendFileのfileNameパラメータの型

昨日の記事で、PlayのsendFileメソッドのとりあえずの使い方を紹介しましたが、その追加情報です。

昨日のサンプルでは

Ok.sendFile(
  content = java.io.File("hoge.csv"),
  fileName = _ => "fuga.csv"
)

このように書く事で、ダウンロードされるファイル名を指定出来ると説明したのですが、よくよく見るとfileName = _ => "hoge.csv"ってなんじゃこりゃと思いませんか?

少なくとも僕は思いました。 今日、@backpaper0さんにも、「あれなんなの?」って聞かれました。 ただ、昨日は力尽きて調べる気力がわかなかったので、今日追記情報を載せます。

まず、sendFileメソッドの実装なのですが、↓こんな感じになってます。

https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/scala/play/api/mvc/Results.scala Statusクラス(Line618-633)

/**
     * Send a file.
     *
     * @param content The file to send
     * @param inline Use Content-Disposition inline or attachment.
     * @param fileName function to retrieve the file name (only used for Content-Disposition attachment)
     */
    def sendFile(content: java.io.File, inline: Boolean = false, fileName: java.io.File => String = _.getName, onClose: () => Unit = () => ()): SimpleResult[Array[Byte]] = {
      SimpleResult(
        header = ResponseHeader(OK, Map(
          CONTENT_LENGTH -> content.length.toString,
          CONTENT_TYPE -> play.api.libs.MimeTypes.forFileName(content.getName).getOrElse(play.api.http.ContentTypes.BINARY)
        ) ++ (if (inline) Map.empty else Map(CONTENT_DISPOSITION -> ("attachment; filename=" + fileName(content))))),
        Enumerator.fromFile(content) &> Enumeratee.onIterateeDone(onClose)
      )
    }

ポイントは、引数のfileNameが fileName:java.io.File=> String = _.getName となっているところです。

これを括弧とか足して書き直すと、 fileName : {java.io.File => String} = _.getName みたいな感じになります。

つまり、fileNameは、

  • java.io.Fileを引数にとり、Stringを返す関数で、
  • デフォルトでは、def func(file:java.io.File) = {file_.getName}という関数が適用される

引数である、ということになります。

で、sendFile関数内では、 (if (inline) Map.empty else Map(CONTENT_DISPOSITION -> ("attachment; filename=" + fileName(content))))) このように、sendFileの第一引数であるcontentをfileNameの引数に渡しているので、contentのファイル名がContent-Dispositionヘッダに追加されることになります。

そのため、任意のファイル名を使用したいときは、fileNameに単純にStringを指定するのでなく、'java.io.File => String'型の関数として、_ => "hoge.csv"のように、何か引数を受け取るけど無視して"hoge.csv"を返す関数を指定しないといけないようです。 fileNameという引数名の型が関数というのは若干違和感がありますが、実装的にはScalaの特徴的な機能をつかっていて「なるほど」という感じですね。

Play2.0Scalaでファイルダウンロード

目的

play2.0Scalaでplay1の時にあったrenderBinaly的な事をしてcsvファイルをダウンロードしたかったので、調べた結果のまとめ。

参考

http://www.playframework.org/documentation/2.0/ScalaStream

とりあえず、このエントリ読むよりも上記公式情報を読むべき。 Serving files の項を読めば、ここでやる内容は十分。 英語が辛いとか、とりあえず動けばいい人だけ、ここから下を読んでください。

とりあえずダウンロード

play2.0 scalacsvファイルをダウンロードするサンプル

conf/routes

# Home page
GET  /            controllers.Application.index
GET  /download    controllers.Application.download

app/controllers/FileDownloadController.scala

package controllers

import play.api._
import play.api.mvc._
import java.io.File

object Application extends Controller {
  
  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }
  
  def download = Action {
    val file = new File("hoge.csv")
    
    Ok.sendFile(file)
  }
}

このサンプルだと、ダウンロードできるファイル名は、uriからdownloadとなってしまう。 ファイル名そのままのhoge.csvとなります。 公式サイトだと、サンプルとしてPDFをダウンロードしているが、

This helper will also compute the Content-Type header from the file name, and add the Content-Disposition header to specify how the web browser should handle this response. The default is to ask the web browser to download this file by adding the header Content-Disposition: attachment; filename=fileToServe.pdf to the HTTP response.

とあるように、デフォルトはファイル名が自動で指定されるらしい。 なのに、ファイルがcsvだとうまくいかない。

ファイル名を指定

なぜか自動で解決してくれないので、明示的にファイル名を指定する。

app/controllers/FileDownloadController.scala

…

object Application extends Controller {
  
  …
  
  def download = Action {
    val file = new File("hoge.csv")
    
    Ok.sendFile(
      content = file,
      fileName = _ => "fuga.csv"
    )
  }
}

こうすると、ダウンロード出来るファイル名をfuga.csvのように、元のファイル名に関係なく指定する事が出来る。


一部、勘違いがあったので修正しました。 2013/01/12

引っ越しました

新・Kuchitama Tech note
http://kuchitama.hateblo.jp/

はてな記法がいつまでたっても覚えられず、
はてなブログならMarkdownで書けるから楽そうだな〜っと思って引っ越しました。

もうちょい更新頻度を上げようと思うので、今後ともよろしくお願いします。

Vimを変態にしてみた

このエントリは、変態アドベントカレンダー2012の4日目です。一日目は@backpaper0さんの「ノムリッシュJavaで厨二プログラミング」でした。

いよいよ12月になりアドベントカレンダーシーズンに入りましたが、みなさんブログ書いてますか?
まだアドベントカレンダーに参加していない方は、是非参加しましょう。
自分の持ってる暗黙的なノウハウを外化する絶好の機会です。

とはいえ、ブログ記事を書くのって結構大変ですよね。
しかし、我々ITエンジニアは日々ソースコードを書きまくっている経験から、「記述を効率化する術」を持っているはずです。
そう、コード補完です!
今回は、協力な補完機能を提供してくれるvimプラグインのNeoCompleCacheとNeoSnippetを紹介しつつ、さらに変態アドベントカレンダー向けに補完設定を作ってみます。
Vimmerな変態の方も、変態なVimmerの方も、Vimmerでない変態の方も、ぜひご活用ください。

下準備

NeoCompleCacheとNeoSnippetはプレフィクスにNeoとついている事からわかるように、どちらもShougoさんによるVimプラグインです。
非常に強力な補完機能でvimによる作業を支援してくれます。
もともとはNeoCompleCacheだけだったのですが、バージョンを重ねるごとに機能が追加されていき、NeoCompleCacheのバージョン7からそれぞれ別のPluginに分割されました。
国産プラグインなので日本語のインストール手順はそろっているだろうと思ったら、あらまぁ無いじゃないですか!しかも、NeoCompleCacheとNeoSnippetでGitHubに書いてあるインストール手順がズレてる。もういっそ全部まとめてまるっと手順を書いちゃいます。

プラグインの取得

とりあえず、NeoSnippet公式のインストール方法に従って、NeoBundleをインストールして、NeoBundle経由でNeoCompleCacheとNeoSnippetをインストールします。
NeoBundleのインストール方法については、こちらにまとめました。

で、NeoBundleがインストールできてさえしまえば後は簡単です。
.vimrcに以下を追記して、

NeoBundle 'Shougo/neocomplcache.git'
NeoBundle 'Shougo/neosnippet.git'

vimを立ち上げて次のコマンドを実行するだけ

:NeoBundleInstall

これで、NeoCompleCacheとNeoSnippetがインストールできます。
お手軽ですね。

設定

インストールできたら、それぞれの設定です。
次の内容を.vimrcに追記したらOKです。
本当はいろいろ設定できるのですが、とりあえず最低限の設定だけ。

"--------------------------------------------------------------------------
" neocomplcache
let g:neocomplcache_enable_at_startup = 1

" Enable omni completion. Not required if they are already set elsewhere in .vimrc
autocmd FileType css setlocal omnifunc=csscomplete#CompleteCSS
autocmd FileType html,markdown setlocal omnifunc=htmlcomplete#CompleteTags
autocmd FileType javascript setlocal omnifunc=javascriptcomplete#CompleteJS
"--------------------------------------------------------------------------
" neosnippet

" Plugin key-mappings.
imap <C-k>     <Plug>(neosnippet_expand_or_jump)
smap <C-k>     <Plug>(neosnippet_expand_or_jump)

" SuperTab like snippets behavior.
imap <expr><TAB> neosnippet#expandable() ? "\<Plug>(neosnippet_expand_or_jump)" : pumvisible() ? "\<C-n>" : "\<TAB>"
smap <expr><TAB> neosnippet#expandable() ? "\<Plug>(neosnippet_expand_or_jump)" : "\<TAB>"

" For snippet_complete marker.
if has('conceal')
  set conceallevel=2 concealcursor=i
endif

いよいよvimを変態にする

VimにHentaiファイルタイプを認識させる

では、ブログ記事作成を便利にするsnipet作成に入りましょう。
今回は、textファイルを補完するために、.txtの拡張子のファイルをhentaiファイルタイプとして扱います。

ファイルタイプをvimに登録するには、.vim以下にftdetectフォルダを作り、そこにファイルタイプ判別のvimスクリプトを追加します。
今回は、下記のファイルを追加しました。

これで、vimは.txtを拡張子に持つファイルを開いたときにファイルタイプをhentaiファイルタイプとして認識してくれます。

vimにHentai辞書を追加する

NeoCompleCacheは、ユーザ定義の辞書ファイルを追加する事ができ、追加した辞書ファイルを元に単語補完を行う事が出来ます。
今回は、適当に変態アドベントカレンダーに登場しそうな単語と変態アドベントカレンダー参加者の名前を登録したhentai.dictを作りました。
辞書ファイルのパスは自由に設定出来るので、今回はとりあえず.vim以下にdictフォルダを作成し、その中に保存しました。

僕がatndから抜き出すタイミングで名前が載ってない人がいるかもしれません。
別に、変態認定してないわけではないです。

この変態辞書を NeoCompleCache に読み込ませるために.vimrcに下記を追記します。

"辞書の登録
let g:neocomplcache_dictionary_filetype_lists = {
  \ 'default' : '',
  \ 'hentai' : $HOME.'/.vim/dict/hentai.dict',
  \ }

これでvimを起動すると、変態辞書による補完が有効になります。
hを入力して+n を押すと、入力候補が表示されます。

vimに変態Snippetを登録する

さらに、vimを変態アドベントカレンダー向けにカスタマイズしていきましょう。
NeoSnippet を利用する事で、スニペット補完が出来るようになります。
スニペット補完とは、よく書く定型文のひな形を用意しておいて、その一部だけを書き換える事で記述効率をあげる補完手法です。
Snippetについてはこちらの記事が詳しいです。
Vimのsnippetについてあまり知らなかったので設定してみたら便利過ぎてつらい

今回は、変態アドベントカレンダーのテンプレートを登録するために、hentai.snipファイルを作成します。

.vimフォルダ以下にsnippetsフォルダを作って、このhentai.snipファイルを保存します。
このファイルを読み込むための.vimrcの設定はこちら

" スニペットファイルの保存ディレクトリのパスを登録
let g:neosnippet#snippets_directory='~/.vim/bundle/snipmate-snippets/snippets'

これで、変態アドベントカレンダーのひな形が登録出来ました。
haと入力して+kを打ち込むと、記事のひな形が出来上がります。

更に、で入力が必要な箇所に移動して書き換える事が出来ます。

まとめ

これで変態アドベントカレンダーの記事作成が大幅に効率化されました。
もちろん、この設定は工夫次第で普段のコーディングにも適用出来ます。
みなさんも、NeoCompleCacheとNeoSnippetを用いてより快適なvimライフを送ってください。
今回作成した、hentai.vim、hentai.dict、hentai.snipについてはgistに挙げているのでよろしければ参考にしてください。

Gist - vimを変態にするための設定ファイル群

明日ははがねのつるぎさんです。

DevLove関西はいかにして私を駆動したか

 気がつけばDevLove関西が終わってもう1週間経ちました。
なのに、未だブログを書かず、会社に何かを持ち帰ったわけでもなく、なにもアウトプットが無いままの1週間でした。
 これは、「ブログを書くまでが勉強会」や「自分の周りを変えるまでが勉強会」という言葉に照らし合わせると「kuchitamaのDevLove関西は終わっていない」という事になります。
 というのも、DevLove関西の懇親会の後からずっと自分の中にもやもやしたものがわだかまっていてそれをどこか納得のいくカタチに落とし込むまではアウトプットのしようがなかったのです。まだ、まとまりきらない部分もありますが、とりあえず思いついた事を思いついたままに書いてみます。

もやもやの正体

 先に言ってしまうと、もやもやの正体は自分とDevLoveのスタンスの違いでした。
 DevLove関西の直後はこのもやもやしたものが、きっと懇親会LTでどんずべりした恥ずかしさやふがいなさだと思っていました。でも、よくよく考えるとスベる事には慣れているし、今更そんなことを気にしていたら今の会社にいられるはずも無いので、何かが違うと思いました。何が違うのかな〜っと考えているとふと、目に入ったのが、@yohhatuさんのプレゼンタイトル「開発現場を駆動せよ -DevLOVE関西Driveがもたらすもの-」でした。
 DevLoveは全体的な雰囲気として「現場」を改善しようとしていました。
 それに対して僕は現場の変更に対して全く興味がなく、「自分」のスキルをのばすことに意識が集中していました。今回の会では、主催者の@yohhatuさん@papandaさんのセッションを始め、スクラムなどチームの改善が念頭にあったように感じます。
 このスタンスの差がもやもやの根源でした。

なんで興味が無かったのか

 現場を変える事になぜ興味が無かったか。
1つには、アジャイルを学ぶ必要性を感じていなかったというのがあります。
 現在の弊社のチームマネジメントにそこまで不満が無かったというのがあります。改善も意見を挙げれば、検討された上で取り入れてもらえるので、改めてアジャイルスクラムを取り上げる必要性も感じていません。
 また、弊社ではアジャイルを崩して取り組んでいることもあります。フリューなりに崩して、やりやすく改めたアジャイルプロセスを実施しているので、あらためて原型を学ぶ必要性をあまり感じてもいませんでした。
 もう一つには、自分の器用貧乏っぷりが我慢出来ず、なにか得意分野といえるようなものを探している最中ということが挙げられます。
 そのため、焦点が自分が身につける技術、考え方に向いていて、周囲を変える事に向かなかったのだと思います。

そして駆動へ

 結局、DevLove関西に参加してもやもやが残ったという事は、発表者の方々や周りの人の熱量に感化されたという事なんだと思います。
 感化された以上は、なにかしないといつまでも胃のあたりが落ち着きません。幸い、興味ないといいつつやたらと本を集める癖があるので、部屋にはアジャイルな見積もりと計画作りや、アジャイルソフトウェア開発奥義などの本が積んであります。まずはこの辺りの本を読み込んでみるところから初めてみようと思います。
 自分も開発現場の一部であると考えれば、自分一人の取り組みでも、DevLoveは開発現場を駆動させたということになると思います。
 というわけで、DevLove関西2012は熱いイベントだった!