Clojure+LeiningenでJavaFXプログラミング
JavaFX GUIプログラミング〈Vol.1〉をちょいちょい読み始めたので、 JavaFXアプリをClojure + Leiningenで開発する方法についてまとめる。
- 作者: 大村忠史
- 出版社/メーカー: カットシステム
- 発売日: 2012/09
- メディア: 単行本
- クリック: 2回
- この商品を含むブログ (4件) を見る
プロジェクトの作成
なにはともあれプロジェクトが無くては始まらないのでプロジェクトを作成
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 scalaでcsvファイルをダウンロードするサンプル
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を入力して
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と入力して
更に、
DevLove関西はいかにして私を駆動したか
気がつけばDevLove関西が終わってもう1週間経ちました。
なのに、未だブログを書かず、会社に何かを持ち帰ったわけでもなく、なにもアウトプットが無いままの1週間でした。
これは、「ブログを書くまでが勉強会」や「自分の周りを変えるまでが勉強会」という言葉に照らし合わせると「kuchitamaのDevLove関西は終わっていない」という事になります。
というのも、DevLove関西の懇親会の後からずっと自分の中にもやもやしたものがわだかまっていてそれをどこか納得のいくカタチに落とし込むまではアウトプットのしようがなかったのです。まだ、まとまりきらない部分もありますが、とりあえず思いついた事を思いついたままに書いてみます。
もやもやの正体
先に言ってしまうと、もやもやの正体は自分とDevLoveのスタンスの違いでした。
DevLove関西の直後はこのもやもやしたものが、きっと懇親会LTでどんずべりした恥ずかしさやふがいなさだと思っていました。でも、よくよく考えるとスベる事には慣れているし、今更そんなことを気にしていたら今の会社にいられるはずも無いので、何かが違うと思いました。何が違うのかな〜っと考えているとふと、目に入ったのが、@yohhatuさんのプレゼンタイトル「開発現場を駆動せよ -DevLOVE関西Driveがもたらすもの-」でした。
DevLoveは全体的な雰囲気として「現場」を改善しようとしていました。
それに対して僕は現場の変更に対して全く興味がなく、「自分」のスキルをのばすことに意識が集中していました。今回の会では、主催者の@yohhatuさん@papandaさんのセッションを始め、スクラムなどチームの改善が念頭にあったように感じます。
このスタンスの差がもやもやの根源でした。
なんで興味が無かったのか
現場を変える事になぜ興味が無かったか。
1つには、アジャイルを学ぶ必要性を感じていなかったというのがあります。
現在の弊社のチームマネジメントにそこまで不満が無かったというのがあります。改善も意見を挙げれば、検討された上で取り入れてもらえるので、改めてアジャイル、スクラムを取り上げる必要性も感じていません。
また、弊社ではアジャイルを崩して取り組んでいることもあります。フリューなりに崩して、やりやすく改めたアジャイルプロセスを実施しているので、あらためて原型を学ぶ必要性をあまり感じてもいませんでした。
もう一つには、自分の器用貧乏っぷりが我慢出来ず、なにか得意分野といえるようなものを探している最中ということが挙げられます。
そのため、焦点が自分が身につける技術、考え方に向いていて、周囲を変える事に向かなかったのだと思います。
そして駆動へ
結局、DevLove関西に参加してもやもやが残ったという事は、発表者の方々や周りの人の熱量に感化されたという事なんだと思います。
感化された以上は、なにかしないといつまでも胃のあたりが落ち着きません。幸い、興味ないといいつつやたらと本を集める癖があるので、部屋にはアジャイルな見積もりと計画作りや、アジャイルソフトウェア開発奥義などの本が積んであります。まずはこの辺りの本を読み込んでみるところから初めてみようと思います。
自分も開発現場の一部であると考えれば、自分一人の取り組みでも、DevLoveは開発現場を駆動させたということになると思います。
というわけで、DevLove関西2012は熱いイベントだった!
変態アドベントカレンダーinSummerを振り返る
この記事は、変態アドベントカレンダーinSummer えっと…最終日!の記事です。
前回は、dproject21さんの、「フィボナッチ数は変態です」でした。
今日のお題
気がつけば変態アドベントカレンダーも最終日。
夏コミも今日で終わりです。
という事なので、この唐突に始まった変態アドベントカレンダーという謎の変態イベントについて振り返ってみようと思います。
今更変態を定義する
今回、変態アドベントカレンダーをやってみて、参加者のみなさんが直面した問題があったように思います。
それは、
変態とは何か
これは、ひょっとすると我々自称変態の永遠の命題かもしれません。
辞書に載っている「変態」という言葉は、どうやら我々がこのアドベントカレンダーの文脈で使っている「変態」とはどうもしっくり来ないように感じます。
そう、我々は新しい変態の定義を求めているのです。
いわば、変態ニュージェネレーションです
さて、この変態ニュージェネレーションですが、僕は次のように定義します。
「変態とは、ものごとを自分に都合良く楽しむこと、またはそのような人」
このアドベントカレンダー、主催者の僕がすっかり忘れてたのに、意識高い参加者の皆さんのご厚意で勝手に始まってました。
やっぱ、こういう主体性ってやる人が楽しんでないと生まれないと思うんです。
それに、記事の中には、割とえげつないしんどい体験談もありました。
しかし、そんな話題ですらおもしろおかしくネタにしちゃう。
そんなバイタリティが見え隠れしていました。
なので、永遠の命題とか言っときながら、一旦区切りのためにこの楽しむというキーワードで変態ニュージェネレーションを定義したいと思います。
変態とは何でも楽しんじゃうイカしたやつらだ!
まとめ
- 変態アドベントカレンダーinSummerおつかれさまでした
- 俺たちゃ変態ニュージェネレーション
- 変態は楽しみ続けるよ
- ところで、連載とか言ってたのどこいった?
- 最後の最後で全くIT感がないze