2017年8月31日木曜日

lumo(javaに依存しないclojurescriptビルドツール)でnodejsアプリを作る方法


個人的に興味のある内容なので、自分の言葉でまとめます。

背景

clojureとはjavaの上で動くlisp系(関数型)言語です。
clojurescript(以後cljs)とはjavascript(以後js)をclojureで書ける言語です。

cljsをjsに変換するにはjavaを必要とするleiningenなどのツールを使うことが多いですが、javaに頼らずとも変換できる仕組みがあるようです。
今回はその仕組みの1つであるlumoを利用して、nodejsのアプリをcljsで書いて動かしてみました。

全体像

この順に説明していきます。
  1. 動作環境
  2. lumoのインストール
  3. lumo + replでhello world
  4. lumo + ファイルでhello world
  5. cljsをjsにビルドして、nodeコマンドでhello world
  6. lumo + express
  7. cljsをjsにビルドして、nodeコマンドでexpressを実行
  8. まとめ
  9. 参考

動作環境

Linux(ubuntu) + nodejs + npmで動作確認しました。
$ node -v
v8.4.0
$ npm -v
5.3.0

lumoのインストール

下記のコマンドでインストールします。
sudo npm i -g lumo-cljs

しかし、バグが残っているようで、自分が試した時は下記のエラーが出てインストールできませんでした。
$ sudo npm install -g lumo-cljs
/usr/local/bin/lumo -> /usr/local/lib/node_modules/lumo-cljs/bin/lumo.js

> lumo-cljs@1.7.0 install /usr/local/lib/node_modules/lumo-cljs
> node scripts/npm_install.js || nodejs scripts/npm_install.js

internal/streams/legacy.js:59
      throw er; // Unhandled stream error in pipe.
      ^

Error: EACCES: permission denied, open 'lumo_linux64.zip'
sh: 1: nodejs: not found
npm ERR! file sh
npm ERR! code ELIFECYCLE
npm ERR! errno ENOENT
npm ERR! syscall spawn
npm ERR! lumo-cljs@1.7.0 install: `node scripts/npm_install.js || nodejs scripts/npm_install.js`
npm ERR! spawn ENOENT
npm ERR! 
npm ERR! Failed at the lumo-cljs@1.7.0 install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/asuki/.npm/_logs/2017-08-31T14_21_26_038Z-debug.log

上記のエラーが出た場合、「--unsafe-perm」というオプションを付けて、下記ようなコマンドを実行すればインストールできました。
sudo npm i -g lumo-cljs --unsafe-perm

インストールできたら、下記のコマンドでlumoのバージョンを確認できます。
lumo --version

自分が試した時は1.7.0でした。

lumo + replでhello world

replとは、対話型のインターフェース(プログラムを入力したら、それがすぐに実行される入力欄)です。
下記のコマンドでreplを起動できます。
lumo -r

下記のコマンドでHello world.と出力できます。
(prn "Hello world.")

jsのconsole.logも使えます。
(js/console.log "Hello world")

exitでreplを終了できます。
exit

replでプログラムを実行できました。
「だから何なの?」と感じるかもしれませんが、「この処理は、こう書いたら動くかな?」など小さな単位で試行錯誤をするときに便利なことがあります。

lumo + ファイルでhello world

今度はファイルに処理を記述して、それを実行してみます。
ファイルを管理するために、プロジェクトを作ります。
mkdir -p ~/gitprojects/lumo-practice
cd gitprojects/lumo-practice
npm init

ファイルを置くディレクトリを作ります。
cd gitprojects/lumo-practice
mkdir src/hello_world

下記のcljsファイルを作ります。
src/hello_world/core.cljs
(ns hello-world.core)

(defn greeting []
  (js/console.log "Hello world."))

(defn -main []
  (greeting))

下記のコマンドで実行できます。
lumo -c src -m hello-world.core

lumoコマンドでプログラムを実行できました。

cljsをjsにビルドして、nodeコマンドでhello world

ビルドの設定ファイルを作ります。
ファイル名は「build.cljs」とかでも良いのですが、後で別のbuildファイルも書くので、「build_hello_world.cljs」というファイル名にしました。
src/build_hello_world.cljs
(require '[lumo.build.api :as b])

(b/build "src"
  {:main 'hello-world.core
   :output-to "index_hello_world.js"
   :optimizations :advanced
   :target :nodejs})

lumoのビルドはなぜかmainを認識してくれないので、下記のようにmainの指定をhello_world/core.cljsの末尾に追記します。
src/hello_world/core.cljs
(set! *main-cli-fn* -main)

下記のコマンドでビルドできます。
(環境によって、数十秒から数分かかります。)
lumo -c src build_hello_world.cljs

ビルドができたら下記のコマンドでjsファイルをnodeのプログラムとして実行できます。
node index_hello_world.js

動きました。

lumo + express

lumo環境下のcljsはnodejsのライブラリを利用できます。
nodejsでwebシステムを作るのに利用できるexpressというライブラリをcljsで利用してみました。

expressをプロジェクトに追加します。
npm i express --save

express用のディレクトリを作ります。
mkdir src/express_sample

下記のプログラムを作成します。
src/express_sample/core.cljs
(ns express-sample.core)

(def express (js/require "express"))
(def app (express))

(.get app "/"
      (fn [req res]
        (.send res "Hello world. sample")))

(.get app "/sample"
      (fn [req res]
        (.send res "sample page home")))

(.listen app 3000
         (fn []
           (js/console.log "started-express")))

下記のコマンドでexpress-sample.coreを実行します。
lumo -c src -m express-sample.core

警告文は出ますが、動きました。

ブラウザで「http://localhost:3000」にアクセスすると「/」で定義したページが表示されます。

sampleのリンクをクリックすると、「/sample」のパスで定義したページも表示されます。

cljsでexpressを利用できました。

cljsをjsにbuildして、nodeコマンドでexpressを実行

expressを利用したcljsをビルドするために、下記のファイルを作成します。
src/build_hello_world.cljs
(require '[lumo.build.api :as b])

(b/build "src"
  {:main 'hello-world.core
   :output-to "index_hello_world.js"
   :optimizations :advanced
   :target :nodejs})

lumoのビルドはなぜかmainを認識してくれないので、下記のようにmainの指定をhello_world/core.cljsに追記します。
src/build_express_sample.cljs
(require '[lumo.build.api :as b])

(b/build "src"
  {:main 'express-sample.core
   :output-to "index_express_sample.js"
   :optimizations :simple
   :target :nodejs})

注意すべきなのは、optimizationsをsimpleにしているところです。
noneにするとビルドに失敗します。
advancedにすると、expressの関数名が置き換えられてしまって、期待通りに動きません。

下記のコマンドでビルドできます。
lumo -c src build_express_sample.cljs

ビルドできたら下記のコマンドでjsファイルをnodeのプログラムとして実行できます。
node index_express_sample.js

lumoのコマンドで実行した時のように「http://localhost:3000」にアクセスすると、ページを確認できます。

expressを利用したcljsをビルドして、nodeで実行できました。

まとめ

nodejsの環境下で、replの起動、cljsの実行、cljsをjsにビルド、することができました。
これにより、javaに頼らずともlisp系の言語であるclojurescriptでnodejsアプリをれることが分かりました。
個人的に嬉しいです。

共有する内容は以上です。

lumoだけではcljsのライブラリを利用しにくかったので、calvinを使ってhiccup(html作成サポートのためのcljsライブラリ)を利用する記事も書きました。
よかったらこちらもどうぞ。
calvinを使ってhiccupを利用したnodejsアプリを作る方法

参考

lumo
Compiling ClojureScript Projects Without the JVM
asukiaaa/bootstrapped-cljs-practice lumo-only

0 件のコメント :