NBBとExpressJSによる再ロードされたワークフロー


nbb ( nが表すものは何でも😄 ) 魅力的なプロジェクトです.それは力をもたらすbabashka to nodejs . 私は今週、それで遊んで、私が見つけたものを共有したいです.それはかなりクールです!

TLドクター


このブログ記事のコードを見ることができますhere . 供給this 依存関係としてclj -Spath and nbb --claspath .

ハローワールド


このプロジェクトのための強力な学習体験は、“ハローワールド”のWebサーバー、コマンドライン引数解析、およびデータベースをシミュレートする状態管理から成ります.
道に沿って、私は依存管理とツーリングについて何かを学ぶべきです.

ツーリング


ほとんど信じられないけどnbb NREPLサーバを起動できます.Emacsでは、すべてのクロージャモードコマンドが正しく動作しないということはちょっと面倒です.
NREPLサーバを起動するにはnbb nrepl-server .
それから、スペースマックで.cljs ファイル.Then SPC m i ( sesman-start ) とlocalhostに接続します.cider-connect-clj . この操作は、NREPLサーバに甘い2次元バッファを接続します.
現在働いていないものがたくさんありますcider-switch-to-repl-buffer ) 1 , しかし、あなたはそれに切り替えることができますSPC b b ( list-buffers ).
これまでのところnbb 's NREPLサーバーは、開発のこの初期段階で、ポーランドで私を吹き飛ばしました.

ヤルでコマンドライン引数を解析します。


始めましたyargs , そして機能している間にyargs 理想的ではなかった.
  • Yargsはコマンド/オプションに関する議論を支持します.
  • ユーザー引数を最初に提供することなく、コマンドとオプションを記述できない方法を次のコードに示します.
    (-> argv # argv should be unecessary
        yargs
        (.command ...)
        (.options ...)
    
  • 糸は処理後にプロセスを殺す--help
  • この動作はREPLでテストを難しくするので理想的ではありません.私は新しいプロセスを始めることなくヘルプ命令を作ることができなければなりません.
    幸運にも、パッケージされたBorkdudetools.cli with v0.3.0 NBBの.もちろん、サブコマンドを使う必要があるならば、Yargsはより良いオプションであるかもしれませんtools.cli 今のところ.

    ツールでコマンドライン引数を解析します。CLI

    tools.cli クロジュールと同じ作品.お気軽にこのセクションをスキップしてくださいtools.cli .
    アプリケーションのエントリポイントは、コマンドライン引数がvarargsとして渡される' main '関数です.nbb また、SEQに引数を詰め込み*command-line-args* .
    まず、作成hello_world.cljs 次にファイルをペーストします.
    (ns hello-world
      (:require [clojure.tools.cli :as cli]))
    
    (def default-port 3000)
    
    (def cli-options
      [["-p" "--port PORT" "Port number"
        :default default-port
        :parse-fn js/Number
        :validate [#(< 1024 % 0x10000) "Must be a number between 1024 and 65536"]]
       ["-h" "--help"]])
    
    (defn handle-args [args] (println args))
    
    (defn main
      [& args]
      (handle-args
       (cli/parse-opts
        args cli-options)))
    
    REPLでこれを試してみてくださいtools.cli 作品
    hello-world> (main)
    {:options {:port 3000}, :arguments [], :summary   -p, --port PORT  3000  Port number
      -h, --help, :errors nil}
    
    hello-world> (main "--port" "9093")
    {:options {:port 9093}, :arguments [], :summary   -p, --port PORT  3000  Port number
      -h, --help, :errors nil}
    
    hello-world> (main "--help")
    {:options {:port 3000, :help true}, :arguments [], :summary   -p, --port PORT  3000  Port number
      -h, --help, :errors nil}
    
    hello-world> (main "--port" "foobar")
    {:options {:port 3000}, :arguments [], :summary   -p, --port PORT  3000  Port number
      -h, --help, :errors [Failed to validate "--port foobar": Must be a number between 1024 and 65536]}
    
    
    cli/parse-opts コマンドライン引数を処理するために必要なすべてのコンポーネントを含むマップを生成します.2
  • :options : アプリケーションが使用するパラメータ
  • :summary : ヘルプドキュメント用にフォーマットできる文字列
  • :errors : 妥当性検査エラー.ここでカスタムエラーメッセージを見ることができます.
  • の定義を変えましょうhandle-args 何か役に立つことをする.
    (defn start-app [{:keys [port]}]
      (println "starting server on port" port))
    
    (defn print-help [summary]
      (println "hello world server")
      (println summary))
    
    (defn print-errors
      [{:keys [errors summary]}]
      (doseq [e errors]
        (println e))
      (print-help summary))
    
    (defn handle-args
      [{:keys [options summary errors] :as args}]
      (cond
        (seq errors) (print-errors args)
        (:help options) (print-help summary)
        :else (start-app options)))
    
    再びRPLから同じことを実行すること自由に感じなさい.あなたが何を渡すかに関係なく、フォーマットされたテキストを見るべきです.

    端末からの走行


    この次の課題は確かに私にいくつかのトラブルを与えたが、3つの発見は非常に助けた.3
  • エー--main <ns>/<fn> パラメータをnbb コマンドライン.
  • スクリプトを引数として渡すべきではありません.代わりに、CLASSPATHにある--classpath <dir1:dir2:...> .
  • nbb 自動的にクラスパスにカレントディレクトリが含まれます.
  • あなたがすべてのスクリプトを中央のディレクトリに加えることができて、あなたのシェルinitでそのディレクトリをデフォルトで含むことができて、彼らの名前またはファイルシステム場所を指定せずにあなたのスクリプトを走らせるので、Count - 2は特に顕著です.
    あなたが保存したディレクトリから実行していると仮定しますhello_world.cljs .
    $ nbb --main hello-world/main --help
    hello world server
      -p, --port PORT  3000  Port number
      -h, --help
    
    $ nbb --main hello-world/main
    starting server on port 3000
    
    $ nbb --main hello-world/main --port 9093
    starting server on port 9093
    
    $ nbb --main hello-world/main --port foobar
    Failed to validate "--port foobar": Must be a number between 1024 and 65536
    

    エクスプレスズ


    インストールプロセスexpressjs あなたがnodejsに精通しているならば、mundaneです.ファーストランnpm install express 式を取得するには次に、名前空間のフォームを変更してプロジェクトに利用可能にします.
    (ns hello-world
      (:require [clojure.tools.cli :as cli]
                ["express$default" :as express]))
    
    
    次のコードでサーバーを起動することができますが、まだそれをしないでください.簡単に迂回する必要がある.4
    (.listen
      (doto (express)
            (.get "/" (fn [_ res] 
                        (.send "hello, world"))))
      default-port)
    

    再ロードされたワークフロー


    あなたがclojure生態系に慣れていないならば、「再ロードされたワークフロー」と呼ばれているスチュアートシエラによってトレンディされた考えがありますほとんどの大きなクロージャアプリケーションが使用し、そこから選択する多くのライブラリがあります.
    基本的な考え方は、それがすぐに停止し、主要なプロセスを停止せずにstatutiveリソースを起動する方法を提供することです.それはキラーreplの経験の必要性です.5
    オプションを見直した後にweavejester/integrant 小さいので、1つだけ依存し、2つのソースファイルを合計します.
    インテグラントはnbb 現在の状態では、私はいくつかの機能を削除し、今はうまく動作します.Githubプロジェクトを見るcrinklywrappr/integrant .
    カット機能のショートリスト:
  • ESN設定
  • 仕様検証
  • それはnpm ノード依存性とclj clojure依存性のために.
    $ classpath="$(clj -A:nbb -Spath -Sdeps '{:aliases {:nbb {:replace-deps {com.github.crinklywrappr/integrant {:git/tag "v1.0.3" :git/sha "8462388"}}}}}')"
    
    $ nbb --classpath $classpath nrepl-server
    

    ExpressJSによるIntegrantの使用


    まず、ハンドラを定義しましょう.
    (defn hello-world [count]
      (fn [_ res]
        (swap! count inc)
        (.send res (str "Hello, World! (count: " @count ")"))))
    
    私たちはcount データベースをシミュレートする.サーバーを起動するたびに、サーバーに何人のリクエストをしたかをカウントし、0でカウントを再起動します.6
    Inteantで始まる最高の場所はConfigマップです.
    (ns hello-world
      (:require [integrant.core :as ig]
                ["express$default" :as express]
                [clojure.tools.cli :as cli]))
    
    (def config
      {:express/server {:port default-port :app (ig/ref :express/app)}
       :express/app {:handler hello-world :count (ig/ref ::count)}
       ::count {:start 0}})
    
    この設定マップは簡単です.各キー値ペアは将来のステートフルコンポーネントの構成を参照します.あなたは、(ig/ref <qualified-key>) 関数.
    次に、どのようにすべてを起動するには、積分を教えてください.このプロセスは、ig/init-key マルチメソッド最初のパラメータはコンポーネントに対応するキーです、そして、2番目のパラメータはそのコンポーネントのconfigのマップです.
    (defmethod ig/init-key :express/app [_ {:keys [handler count]}]
      (doto (express)
        (.get "/" (handler count))))
    
    (defmethod ig/init-key :express/server [_ {:keys [port app]}]
      (.listen app port))
    
    (defmethod ig/init-key ::count [_ {:keys [start]}]
      (atom start))
    
    サーバーだけが閉じられる必要があります.この方法はig/halt-key! マルチメソッドまた、サーバーオブジェクトである2番目のパラメータにのみ興味があります.この関数は有効です.
    (defmethod ig/halt-key! :express/server [_ server]
      (when (and (some? server) (.-listening server))
        (.close server)))
    
    REPLでこれをテストすること自由に感じなさい.
    hello-world> (def system (ig/init config))
    
    ; now visit localhost:3000/ and refresh a few times
    
    hello-world> (ig/halt! system)
    
    あなたがこのセクションを混乱させるとわかるならば、あなたに点検するよう奨励しましょうsystem または' canonical '積分を読み込む.あなたが私がいくつかの詳細について光沢があると感じるならば、そうすることは非常に啓発されます.

    すべてをまとめる


    私たちはカップルを定義しますstart/stop システムを上下に動かすプロセスを簡素化する機能.
    (def system (atom nil))
    
    (defn start
      "system is an atom"
      ([] (start config))
      ([config] (start config system))
      ([config system] (reset! system (ig/init config))))
    
    (defn stop
      "system is an atom"
      ([] (stop system))
      ([system]
       (when (map? @system)
         (swap! system ig/halt!))))
    
    最後に、再定義start-app 呼ぶstart を使っている.
    (defn start-app [{:keys [port]}]
      (-> config
          (assoc-in [:express/server :port] port)
          start))
    
    おめでとう!現在、コマンドライン消費とREPL開発に適したスクリプトがあります.
    hello-world> (start) ; or eg (start-app {:port 9093})
    hello-world> (stop)
    
    $ nbb --classpath $classpath --main hello-world/main --port 9093
    

    一歩一歩


    あなたはそれに気づくかもしれないctrl+c コマンドラインからサーバを停止する必要があります.それは良いですが、もしExpressJSが正しく自分の後にクリーンアップしない場合はどうですか?
    多分、それはすでにします:私は、専門家です.しかし、あなたがそうでない異なるサーバーに変わるならば、どうですか?それは私たちのフックに良いかもしれないstop 関数をsigintに設定する.
    (defn exit
      [& _]
      (stop)
      (.exit js/process 0))
    
    (.on js/process "SIGINT" exit)
    
    ハッピーハッキング!

    閉鎖的思考


    このプロセスの間、私が遭遇した唯一の'バグ'は、リクエストハンドラをpartial , 例えば(partial hello-world count) . それを働かせるために、私は2009年から閉鎖を返しましたhello-world . これが確かかどうか分かりませんnbb 問題またはexpressjs 問題
    好きですnbb . もしかしたらbb 😉. 最大の問題は、clojure依存性を特定して、それが現在JARRを読むことができない人間工学です.しかし、私はそれらの面の両方が改善することを望みます.
    私は、それが私をそれから使うのを止めると思いません.
    Emacsはそれがclojure replであると思います、しかし、それはNBBサーバーに接続しています-我々は少し混乱しました.畝
    arguments 今私たちにとって不可欠ではありません(main "foobar") , あなたは行動でそれを見ることができます.畝
    後で新しい発見clj ビルドツールもこれを行います.畝
    ほとんどのExpressjs“こんにちは、世界”チュートリアルはここで停止します.畝
    私の経験では、“clojure”が自動的に変更されたコンポーネントをeval(およびそれらの依存関係)を再起動します.私はどのツールがこの機能を提供するかわからない.そして、私はそれがこのアプローチで働くかどうか決定する運命を誘惑しませんでした.😁畝
    SQLiteのような実際のデータベースを使用して次のように良い学習ステップになります.畝