Clojureの基礎〜名前空間〜

Clojureの基礎〜正規表現を扱う〜 - oknknicの日記」に引き続き、基礎メモ。
名前空間についてメモ。

現在の名前空間の切替:in-ns

(必要なら名前空間が新規作成される)
(REPL起動時のデフォルト名前空間は「user」)

(in-ns 'my-name-space)

;なお、新規作成された名前空間では、
;Java の java.lang パッケージは自動的に import されているが、
;clojure.core は use されていないので、 clojure.core を use する。
(clojure.core/use 'clojure.core)
Clojureライブラリのロード:require
(require 'clojure.xml)

;現在の名前空間に別名を作成
(require '[clojure.xml :as xml])
現在の名前空間に読み込み:refer
(refer 'clojure.xml)
require+refer:use
(use 'clojure.string)
Javaパッケージのインポート:import
;(import '(パッケージ クラス名1 クラス名2 ...))
(import '(java.io InputStream File))
現在の名前空間(*ns*)の設定+use+require+import:nsマクロ
(ns my-name-space
  (:use [hiccup.core]
        [clojure.java.io :only (writer)])
  (:require [clojure.string :as str])
  (:import (java.lang.File)))
名前空間のpublic varsの一覧
(dir clojure.xml)
引数のシンボルを現在の名前空間で解決した結果
; 例:Stringの名前解決
(resolve 'String)
ネームスペース関連の関数検索
(find-doc "ns-")

参考

プログラミングClojure 第2版

プログラミングClojure 第2版

Clojureの基礎〜正規表現を扱う〜

Colojureの基礎〜状態を扱う〜 - oknknicの日記」に引き続き、基礎メモ。
正規表現の扱い方についてメモ。

clojure.string - Clojure v1.5 (stable)

正規表現置換:clojure.string/replace
(doc clojure.string/replace)

(clojure.string/replace "<hoge><hage>" #"<(\w*)>" "{$1}")
;-> "{hoge}{hage}"
(参考)一覧表示
(dir clojure.string)

clojure.core - Clojure v1.5 (stable)

正規表現検索:clojure.core/re-find
(re-find #"<\w*>" "aaa<hoge>bbb<hage>")
;-> "<hoge>"
正規表現検索(結果を遅延シーケンスで返す):clojure.core/re-seq
(re-seq #"<\w*>" "aaa<hoge>bbb<hage>")
;-> ("<hoge>" "<hage>")

正規表現の構文

下記(java.util.regex.Patternのドキュメント)を参照。

Ring周りについて頭を整理 その2〜Ringを生で使ってみる〜

Ring周りについて頭を整理 - oknknicの日記」に引き続き、Ring理解ネタ。
今回はRingを生で使ってみることで、理解を深める。

HelloWorld

  • プロジェクト(通常)の新規作成
lein new hello-ring
  • project.clj の編集
(defproject hello-ring "1.0.0-SNAPSHOT"
  :description "FIXME: write"
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [ring/ring-core "1.2.0"]
                 [ring/ring-jetty-adapter "1.2.0"]])
  • core.clj の編集
(ns hello-ring.core)

;ハンドラの定義
(defn handler [request]
  {:status 200                           ;レスポンスコード
   :headers {"Content-Type" "text/html"} ;レスポンスヘッダ
   :body "Hello World"})                 ;レスポンスボディ(String or ISeq or File or InputStream)
  • 起動(REPLから)
cd hello-ring
lein repl

(use 'ring.adapter.jetty) ;jettyアダプタの読み込み
(use 'hello-ring.core)
(run-jetty handler {:port 3000}) ;上記で定義したハンドラを指定して、jettyを起動
  • 接続確認

http://localhost:3000/

  :plugins [[lein-ring "0.8.5"]]
  :ring {:handler hello-ring.core/handler} ;ハンドラを指定
    • 起動
lein ring server

リクエストの中身を見てみる

  • core.clj
(ns hello-ring.core
    (use [clojure.string :only (join)]))

(defn handler [request]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body (join "<br/>"
            ["[server-port]"        (:server-port request)
             "[server-name]"        (:server-name request)
             "[remote-addr]"        (:remote-addr request)
             "[uri]"                (:uri request)
             "[query-string]"       (:query-string request)
             "[scheme]"             (:scheme request)
             "[request-method]"     (:request-method request)
             "[content-type]"       (:content-type request)
             "[content-length]"     (:content-length request)
             "[character-encoding]" (:character-encoding request)
             "[headers request]"    (:headers request)
             "[body]"               (:body request)])})

;もちろんこんな書き方もできる 
;(map
;  #(str "[" % "]" "<br/>" (% request) "<br/>")
;  (keys request))

レスポンス生成関数を使ってみる

  • response関数(基本的な200番のレスポンスを生成)
(ns hello-ring.core
    (:use ring.util.response))

(defn handler [request]
  (response "Hello ring"))

;以下のレスポンスが生成される
;{:status 200
; :headers {}
; :body "Hello ring"}
  • content-type関数(コンテンツタイプの設定)
(ns hello-ring.core
    (:use ring.util.response))

(defn handler [request]
  (-> (response "Hello World")
      (content-type "text/plain")))

;以下のレスポンスが生成される
;{:status 200
; :headers {"Content-Type" "text/plain"}
; :body "Hello ring"}
  • redirect関数
(ns hello-ring.core
    (:use ring.util.response))

(defn handler [request]
  (redirect "http://www.google.com"))

;以下のレスポンスが生成される(googleのホームページへリダイレクトされる)
;{:status 302
; :headers {"Location" "http://www.google.com"}
; :body ""}

その他関数(file-response等)や詳細情報は下記を参照のこと。

ミドルウェアかましてみる

  • wrap-params(パラメータマップを生成するミドルウェア。キーは「:params」)
(ns hello-ring.core
    (:use ring.util.response
          ring.middleware.params))

(defn handler [request]
  (response (str (:params request))))

(def middleware-applied-handler
  (wrap-params handler)) ;引数のハンドラにミドルウェア(ここではwrap-params)をかました新規ハンドラを生成
  • project.clj のハンドラ指定部分を以下のとおり変更
  :ring {:handler hello-ring.core/middleware-applied-handler}
  • 確認

http://localhost:3000/?param01=val01

Colojureの基礎〜状態を扱う〜

Clojureの基礎 〜プロトコル・データ型・レコード〜 - oknknicの日記」に引き続き、基礎メモ。
状態の扱い方についてメモ。

状態を扱うオブジェクト

ref
  • 概要
    • 変更不可なオブジェクトへの、変更可能な参照
    • 参照の変更は、トランザクション内(dosyncマクロ)でのみ可能
  • 生成:ref
    • バリデーション関数は :validator で指定
  • 参照先の取得:
    • deref関数
    • @リーダマクロ
  • 参照の更新(同期):
    • ref-set
    • alter (参照先の読み出しと参照の更新を同時実行)
    • commute (alterの特殊版。具体的には、更新操作の順序が不定である)
  • 参照が他のトランザクションから変更されないように保護:
    • ensure
アトム
  • 概要
    • refの軽量版
    • トランザクションに参加しない。 → 他の値との同期更新が不要な場合に使う
  • 生成:atom
  • 参照の更新(同期)
    • reset!関数
    • swap!
エージェント
  • 概要
  • 参照の更新(非同期):
    • send (ref における commute に似ている。戻り値はエージェントである(非同期なので処理結果はこの時点では判らないため))
    • send-off (必要に応じて別のスレッドプールが割り当てられる版の send。つまり、ブロックしない)
  • 参照の更新エラーのロールバック
    • clear-agent-errors (エージェントの更新で一度でもエラーが発生すると、そのエージェントは読み出し不可になる。この関数で、エラー発生前の状態に戻すことができる)
  • 同期化
    • await (現在のスレッドあるいはエージェントからsendされたアクションがすべて完了するまで、現在のスレッドをブロック)
    • await-for (await のタイムアウト指定版)

束縛(var)

ルート束縛
  • defやdefnの呼び出しで束縛した場合
  • メタデータ「:dynamic」が付与された場合はダイナミックスコープをもつ
  • 任意のスレッドからアクセス可能である
スレッドローカルな束縛
  • bindingマクロで束縛した場合
  • ダイナミックスコープをもつ
  • スレッドローカル(bindingを実行したスレッドにおいて、binding本体の実行を抜けるまでの間、そのスレッド内の任意の場所からのみ参照可能)
  • letの構造に似ているが、letとの違いはダイナミックスコープである点である

(補足)動的束縛によるAOP的な実装

  • bindingによって、その実行内部における任意の束縛を置き換え可能である
  • 例えば以下の用に、呼び出し先の内部で呼び出している関数を書き換えるようなコードを記述できる
(defn ^:dynamic mylogic [] (何らか処理))
(defn ^:dynamic mymain  [] (mylogic))

(defn -main []
  (binding [mylogic #(do (開始ログ出力) (mylogic) (終了ログ出力))]
    (mymain)))

参考

プログラミングClojure 第2版

プログラミングClojure 第2版

Clojureの基礎 〜プロトコル・データ型・レコード〜

今更ながら、Clojureの基礎メモ。
今回は独自型定義周りを。

プロトコル

  • 概要
  • 記法
    • プロトコルの定義:defprotocolマクロ
    • データ型(およびクラス)へのプロトコル追加:extend関数、extend-typeマクロ(少し見易い)、extend-protocolマクロ(複数のクラスへのプロトコル追加を一塊で記述)

データ型

  • 概要
    • Javaのクラスの代替
    • デフォルトで変更不可
    • メソッド定義は、プロトコル(あるいはインタフェース)経由で行う必要がある
  • 記法
    • データ型の定義:deftypeマクロ
    • 無名データ型の定義:reifyマクロ
    • Javaクラスの継承(extends):proxyマクロ
レコード
  • 概要
    • ある特徴をもったデータ型
    • 具体的には、PersistentMapを実装したデータ型、つまり「プロトコルを実装できるマップ」である
    • マップなので、assocおよびupdate-ibで操作できる
    • なお、Clojureでは領域固有の情報はマップでモデリングすることが推奨されているので、そのような場合に用いることができる
  • 記法
    • レコードの定義:defrecordマクロ

参考

プログラミングClojure 第2版

プログラミングClojure 第2版

Ring周りについて頭を整理

Clojure(Compojure)で作成したWEBアプリをGlassFishにデプロイ - oknknicの日記」に続き、ClojureによるWebアプリネタ。
デファクトである Ring について、頭を整理しておく。

Ring

  • PythonWSGIRubyのRackにインスパイアされたWebアプリケーションライブラリ
  • HTTPの詳細を抽象化するユニファイドAPIを提供
  • Ringを用いて記述されたClojure Web Applicationは、Java Servletコンパイルできる
  • war ファイルへのパッケージングもできる
  • 多彩なミドルウェア(後述)が提供される
構成

 リクエストの流れ:クライアント -> アダプタ -> ミドルウェア -> ハンドラ
 レスポンスの流れ:ハンドラ -> ミドルウェア -> アダプタ -> クライアント

アダプタ
  • RingはJava Servletのアダプタを組み込みでもっている。これにより、Java WAS にデプロイできる Java Servletへのコンパイルが可能になっている
  • Ringは組み込みのJettyを用いるアダプタ(ring-jetty-adapter)も組み込みでもっている

 などなど

Ring関連サードパーティライブラリ

→Compojureはこの位置づけ

Compojure

  • Ringのハンドラの記述に特化したDSL
    • MVCでいうところのコントローラ部分(リクエストのルーティング部分)を補助するライブラリ
  • RubySinatraにインスパイアされた

参考

Clojure Programming: Practical Lisp for the Java World (English Edition)

Clojure Programming: Practical Lisp for the Java World (English Edition)

Clojure in Action: Elegant Applications on the JVM

Clojure in Action: Elegant Applications on the JVM

Clojure(Compojure)で作成したWEBアプリをGlassFishにデプロイ

ClojureでのWEBアプリ開発にあたって、各種ライブラリのメモ - oknknicの日記」の続き(?)。
Clojure(Compojure)で作成したWEBアプリをGlassFishにデプロイしてみたのでメモ。

Compojureプロジェクトの新規作成、warの生成

lein new compojure myapplication

cd myapplication

lein ring uberwar

GlassFishにデプロイ

1. GlassFish Server Open Source Edition(Windows版) 4.0 (Java EE7 対応)のダウンロード・インストール
 http://dlc.sun.com.edgesuite.net/glassfish/4.0/release/glassfish-4.0-windows.exe
 (インストールディレクトリは「C:\glassfish4」)
2. コンソール画面接続確認
 http://localhost:4848
3. warのデプロイ

cp target\myapplication.war C:\glassfish4\glassfish\domains\domain1\autodeploy\myapplication.war
  • > 同ディレクトリに「myapplication.war_deployed」というファイルが作成されればOK

 (あるいは、上記WEB画面の「Applications」メニューからDeployできる)
4. デプロイしたアプリへの接続
 http://localhost:8080/myapplication
 https://localhost:8181/myapplication
 (URLは、上記WEB画面の「Applications」メニューのLaunchリンク押下で確認可能)