/home/matstani/weblog

programming log.

[ClojureScript] リモートのfigwheelに接続する

ClojureScriptの開発ツールFigwheelを利用すると、ソースコードの変更時に自動的にブラウザにロードされるほか、付属のREPLでは、コンソールに入力したClojureScriptコードをブラウザ上で実行することができます。
この時に利用されるWebSocket接続ですが、デフォルトでは接続先がlocalhostになっています。
figwheelプロセスをリモートサーバで実行している場合、デフォルトでは以下のようなエラーがブラウザのJavaScriptコンソールに表示されます。

1
2
Figwheel: trying to open cljs reload socket
ws://localhost:3449/figwheel-ws/flappy-bird-demo のサーバへの接続を確立できませんでした。

接続先のホストは、project.cljでキー{:cljsbuild {:builds [{:figwheel {:websocket-host 接続先}]}}で指定できます。
リモートサーバのホスト名やIPアドレスを直接記述することもできますが、:js-client-hostを指定すると、location.hostの値を設定してくれるので便利です。
設定例)
project.clj

READMEにあるのですが、見逃していました。

[Clojure] forでネストしたマップを扱う

Clojureで以下のようなネストしたデータ構造を扱いたい場合があります。

1
2
3
4
5
{:division1
 {:group1 [:staff1 :staff2]
  :group2 [:staff3 :staff4 :staff5]}
 :division2
 {:group3 [:staff6 :staff7]}}

ここからstaff要素を参照したい場合、手続き型言語ではforeach文を重ねるのが一般的だと思いますが、Clojureでmap関数やreduce関数をネストすると非常に読みづらくなってしまいます。
Clojureで構造や深さがあらかじめわかっているデータ構造を扱う場合は、forマクロが便利です。 以下のコードでネストしたキーと値の組み合わせが取り出せます。
nested.clj

reduceと組み合わせると値の更新ができます。
nested.clj

その他の便利な関数

特定の位置の値を扱う 

深さがわからないデータ構造を扱う

[Clojure] UbuntuでClojure & VIM開発環境

手順の覚書

Javaインストール

1
$ sudo apt-get install openjdk-7-jdk

leiningenインストール

1
2
3
$ curl -O https://raw.github.com/technomancy/leiningen/stable/bin/lein  
$ mv lein ~/bin  
$ chmod 755 ~/bin/lein  

vimインストール

1
$ sudo apt-get install vim

vim関連ファイルを作っておく

1
$ touch ~/.vimrc && mkdir ~/.vim && mkdir ~/.vim/bundle

プラグインの導入を簡便にするためにpathogen導入

1
$ mkdir -p ~/.vim/autoload ~/.vim/bundle && curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim

~/.vimrcに以下追加

1
2
3
execute pathogen#infect()
syntax on
filetype plugin indent on

vim-clojure-staticインストール

1
$ cd ~/.vim/bundle && git clone git://github.com/guns/vim-clojure-static

fireplace.vimインストール

1
$ cd ~/.vim/bundle && git clone git://github.com/tpope/vim-fireplace.git

paredit.vimインストール

1
$ cd ~/.vim/bundle && git clone git://github.com/vim-scripts/paredit.vim

新規プロジェクトを作成して動作確認

1
$ lein new try-fireplace

プロジェクトディレクトリに移動しREPLを起動しておく

1
2
$ cd try-fireplace
$ lein repl

別のコンソールでプロジェクトディレクトリに移動し、ソースコードを開く

1
2
$ cd try-fireplace
$ vim vi src/try_fireplace/core.clj

サンプル関数をvim内で呼んでみる

1
2
3
4
5
6
7
8
9
(ns try-fireplace.core)

(defn foo
  "I don't do a whole lot."
    [x]
      (println x "Hello, World!"))

;; 以下の行を追加し、コマンド cpp で実行してみる
(foo "fireplace.vim")

正常にインストールされていれば、下部に標準出力と戻り値が表示される

1
2
fireplace.vim Hello, World!  
nil

vimヘルプタグ生成

以下をvim内で実行し、プラグインのヘルプを生成しておく

1
:Helptags

以下でプラグインのヘルプを見ることができる

1
2
:help fireplace  
:help paredit

参考サイト

[Clojure] componentフレームワークを利用した開発

componentフレームワークは動作中に状態を保持するソフトウェアに関して

  • 起動・停止のインターフェイスを定義する
  • ソフトウェアをコンポーネントに分割し、コンポーネント間の依存関係を宣言的に記述する
  • 動作中のソフトウェア全体の状態を一つのデータ構造(システム)にまとめる

といった機能を持つフレームワークです。componentフレームワークを利用することで、tools.namespaceを利用した開発が容易になります。

ring/compojureベースのWebアプリケーションに、componentフレームワークを適用する例を紹介します。

プロジェクト作成

compojure用テンプレートを利用してプロジェクトを作成します。

1
$ lein new compojure example-component-ring

依存ライブラリ追加

project.clj:dependenciesに以下を追記します。

componentフレームワーク

1
[com.stuartsierra/component "0.2.1"]

tools.namespace

1
[org.clojure/tools.namespace "0.2.5"]

ring-jetty-adapter(ringアプリケーションの直接起動、停止)

1
[ring/ring-jetty-adapter "1.3.0"]

HttpServerコンポーネント定義

example-component-ring.componentネームスペースに、HttpServerコンポーネントを記述した例です。
component.clj

コンポーネントはLifecycleプロトコルを実装したレコードとして定義します。
Lifecycleプロトコルはメソッドとしてstartstopを持っています。これをHTTPサーバの起動、停止を行うように実装しています。この時、サーバオブジェクト(このアプリケーションにおける状態)をレコードのフィールドに格納しています。

create-system関数は、コンポーネントを作成し、1つのシステムにまとめる関数です。内部でsystem-map関数を利用しています。上記では、HttpServerコンポーネントのみ作成していますが、複数のコンポーネントを組み合わせて1つのシステムにまとめることや、コンポーネント間の依存関係を記述することができます。

システムの起動・停止・リロード

システムの起動・停止・リロードを行うための関数を定義します。REPLから利用しやすいように、userネームスペースに定義する例を紹介します。

project.clj追記

起動・停止・リロード用の関数は、開発時のみ読み込まれるファイルに記述します。
project.clj:profiles :devに以下を追記します。

1
:source-paths ["dev"]

project.clj全体は以下のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(defproject example-component-ring "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [compojure "1.1.8"]
                 [com.stuartsierra/component "0.2.1"]
                 [ring/ring-jetty-adapter "1.3.0"]
                 [org.clojure/tools.namespace "0.2.5"]]
  :plugins [[lein-ring "0.8.11"]]
  :ring {:handler example-component-ring.handler/app}
  :profiles
  {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
                        [ring-mock "0.1.5"]]
         :source-paths ["dev"]}})

user.clj作成

dev/user.cljファイルを作成し、システム起動・停止・リロードを行う関数を定義します。 user.clj

プロジェクト内でREPLを起動すると自動的にこれらの関数が読み込まれた状態になります。

システムを起動する

1
2
3
user> (go)
;; Starting HTTP server
#com.stuartsierra.component.SystemMap{:http-server #example_component_ring.component.HttpServer{:port 3000, :server #<Server org.eclipse.jetty.server.Server@120f5c9>}}

システムを停止する

1
2
3
user> (stop)
;; HTTP server stopped
#com.stuartsierra.component.SystemMap{:http-server #example_component_ring.component.HttpServer{:port 3000, :server nil}}

(ソースコード修正時)システムをリロードする

1
2
3
4
user> (reset)
:reloading (example-component-ring.handler example-component-ring.component example-component-ring.test.handler user)
;; Starting HTTP server
#com.stuartsierra.component.SystemMap{:http-server #example_component_ring.component.HttpServer{:port 3000, :server #<Server org.eclipse.jetty.server.Server@1fc6f6a>}}

複数のコンポーネントの連携

componentフレームワークでは、複数のコンポーネントの依存関係を記述し、コンポーネント同士を連携させることができます。
ここでは、Databaseコンポーネントを導入し、HttpServerコンポーネントが利用する(HttpServerコンポーネントがDatabaseコンポーネントに依存する)例を紹介します。

MongoDB導入

Databaseコンポーネントの中身として、MongoDBを利用することにします。ClojureからMongoDBを利用するためのクライアントライブラリとしてMongerを利用します。project.cljに以下を追記します。

1
[com.novemberain/monger "2.0.0"]

Databaseコンポーネント定義

component.cljにDatabaseコンポーネントを定義します。
component.clj

HttpServerと同様に、Lifecycleプロトコルを実装したレコードとして定義しています。
既存コードへの変更点として、HttpServerレコードに、databaseフィールドを追加しています。このフィールドにDatabaseコンポーネントのオブジェクトを、状態として保持します。
また、create-system関数では、using関数を利用して依存関係を定義しています。上記では、HttpServerコンポーネントがDatabaseコンポーネントを利用する、という関係を記述しています。これによりHttpServerインスタンスのdatabaseフィールドには、Databaseインスタンスが代入されます(依存関係の定義に従って代入が自動的に行われる仕組みをDependency Injectionといいます)。

コンポーネントをringハンドラに渡す

上記までの例で、HttpServerコンポーネントにDatabaseコンポーネントを渡すことはできていますが、ringアプリケーションのエントリポイントとなるハンドラに、直接引数として渡すことはできないため、Webアプリケーション内からDatabaseコンポーネントを利用するためには、さらに一工夫が必要になります。
例として、requestマップ内にDatabaseコンポーネントを追加するringミドルウェアを利用する方法を紹介します。
component.clj

wrap-app-componentミドルウェアで、requestにDatabaseコンポーネントを追加しています。これにより、ringハンドラ内の処理で、Databaseコンポーネントを利用できるようになります。
handler.clj

参考リンク

[Clojure] tools.namespaceを利用したREPL上でのソースコードリロード

JVMを再起動することなく、プロジェクト全体のソースコードのリロードを実現するツールtools.namespaceを紹介します。

ClojureにはREPLが付属しており、動作を確かめながらソフトウェアを開発することができます。
修正後のソースコードをREPL上に読み込むには通常、
(use 'namespace :reload または :reload-all)
を利用します。例)

1
user=> (use 'my.app :reload-all)

しかし、:reloadreload-allを利用したリロードでは、

  • ソースコードから削除した定義がJVM上に残ってしまう
  • 依存するファイルのリロード漏れがあると、REPL上で古いコードと新しいコードが混在してしまう

といった不便な点があります。
リロードがうまくいかない場合は、JVMごと再起動するのが常套手段ですが、プロジェクトが大きくなるほど時間がかかります。
tools.namespaceを利用するとJVMを再起動することなく、プロジェクト全体のソースコードのリロードを実現できます。

tools.namespaceの使い方

project.cljdependenciesに以下を追記します。

1
[org.clojure/tools.namespace "0.2.5"]

開発中にソースコードを変更した際は、以下をREPL上で実行することで、ソースコードのリロードが行われます。

1
2
user=> (use '[clojure.tools.namespace.repl :only [refresh]])
user=> (refresh)

リロードを活用するために

tools.namespaceによるリロードをスムーズに行うためには、ソフトウェアが以下のルールに従っている必要があります。

1. グローバルな状態を持たない

動作中に変化するアプリケーションの状態をグローバルVarに格納している場合、(refresh)は状態をクリアしてしまいます。
リロードをスムーズに行うためには、アプリケーションの動作中にグローバルVarの状態を参照しない設計(状態を表すデータ構造はパラメータとして必要な関数に渡す設計)が求められます。

2. ライフサイクルを管理できる

REPL上からアプリケーションの起動、停止を行う関数を用意しておくことが求められます。
起動関数では、アプリケーションの動作に必要なリソースを確保し、内部状態を表すデータ構造に組み立てて、必要な関数に渡します。
終了関数では、全てのリソースを開放し、まっさらな状態に戻します。
これらの関数とtools.namespace利用した開発の流れは以下のようになります。

Step 1. REPL起動
Step 2. アプリケーション起動

1
2
3
4
5
user=> (use 'clojure.tools.namespace.repl)
user=> (refresh)
user=> (def my-app {:state-of-world (ref {})
                    :object-handle (atom nil)})
user=> (start my-app)

Step 3. 動作確認
Step 4. ソースコード修正
Step 5. アプリケーション再起動

1
2
3
4
5
user=> (stop my-app)
user=> (refresh)
user=> (def my-app {:state-of-world (ref {})
                    :object-handle (atom nil)})
user=> (start my-app)

※上記でグローバルVaruser/my-appを利用していますが、アプリケーション内からは直接user/my-appを参照せず、ローカルパラメータとしてのみ参照されるため、リロードによって不都合が発生することはありません。

状態管理のフレームワーク

リロード可能なアプリケーションを実現するために、アプリケーションのライフサイクル管理の統一的なインターフェースを定義し、内部状態を適切に管理するための仕組みとして、Componentフレームワークtools.namespaceと同じ作者によって提供されています。

注意点

  • AOTコンパイル非対応
  • 一部のライブラリ(ring-develなど)は機能が衝突する
  • (reresh)を実行した名前空間はクリアされる(REPL上で定義した内容は削除される)

参考

[ZF2] データベース操作色々

ZF2の提供するTableGatewayを利用した基本的なCRUD手順はチュートリアルで紹介されています。 ここでは、より複雑な例をいくつか紹介します。

検索条件指定

TableGatewayを利用した検索で条件を指定するには、selectメソッドに無名関数を渡します。
サンプルコード

結果を配列で取得

TableGatewayを利用した検索では、結果はEntityオブジェクトに変換された状態で取得できますが、JOINしたカラム等は失われてしまいます。
全てのカラムを取得したい場合は、以下のようにselectオブジェクトを生成して実行します。
サンプルコード

JOIN

JOINする場合のサンプルコードです。
サンプルコード

LEFT JOIN, RIGHT JOIN, INNER JOIN

結合方法を指定する場合は、joinメソッドの第4引数で指定します。
サンプルコード

集計

GROUP BY を使用した集計のサンプルコードです。
サンプルコード

副問い合わせ

selectオブジェクトを組み合わせることで、副問い合わせを行うことができます。 WHERE句に副問い合わせを指定する例です。
サンプルコード

JOIN句に副問い合わせを指定する例です。
サンプルコード

[ZF2] バリデーションメッセージのスタイルを変更する

Zend Framework2でフォームバリデーションを行った際のエラーメッセージには、特定のスタイル(class)が指定されていないため、メッセージを赤字で表示したい、といった場合に困ります。

デフォルトの表示方法は、ビューヘルパーformElementErrorsとして登録されています。
これを自作のクラスで置き換えることで、任意のスタイルでエラーメッセージを表示させることができます。

例として、Album\Helper\FormElementErrorsを作成します。
FormElementErrors.php

上記の例ではエラーメッセージのul要素にclass="err-msg"を指定しています。

作成したビューヘルパーは、Module.phpgetViewHelperConfigメソッドで登録します。
Module.php

登録後、バリデーションメッセージが指定したスタイルで表示されるようになります。

[ZF2] フォームバリデーションメッセージを変更する

Zend Framework2でフォームのバリデーションのエラーメッセージを変更するにはいくつか方法がありますが、InputFilter作成時のオプションで指定する方法を紹介します。

ZF2チュートリアルでは、空欄チェックと、文字列の長さチェックが付加されていますが、これを日本語メッセージにしてみます。

サンプルコード

冗長に見えますが、配列の組み合わせなので、関数化して再利用するのは容易です。
関数化した例をいくつか掲載します。

サンプルコード