読者です 読者をやめる 読者になる 読者になる

のぶLab.

流しのソフトウェアエンジニアの雑記帳. Android, Scala, Clojure, Ruby on Railsなど

Luminus, Reagentな環境でSpecljを導入

ClojureRSpecのような読みやすいテストを書くためのフレームワークとしてSpecljというものがあります。

ここではprofileにcljsを選択して作成したLuminus projectでClojure, ClojureScriptのspecを実行できるようにしていきます。

前提

前述したLuminus projectは以下のように作成されたprojectを指します

lein new luminus foo +cljs
cd foo
tree .
.
├── Procfile
├── README.md
├── env
│   ├── dev
│   │   ├── clj
│   │   │   └── foo
│   │   │       ├── dev.clj
│   │   │       └── repl.clj
│   │   └── cljs
│   │       └── foo
│   │           └── dev.cljs
│   └── prod
│       └── cljs
│           └── foo
│               └── prod.cljs
├── project.clj
├── resources
│   ├── docs
│   │   └── docs.md
│   ├── public
│   │   ├── css
│   │   │   └── screen.css
│   │   ├── img
│   │   └── js
│   └── templates
│       └── home.html
├── src
│   └── foo
│       ├── core.clj
│       ├── handler.clj
│       ├── layout.clj
│       ├── middleware.clj
│       ├── routes
│       │   └── home.clj
│       └── session.clj
├── src-cljs
│   └── foo
│       └── core.cljs
└── test
    └── foo
        └── test
            └── handler.clj

この方法で作成すると、React.jsのClojureScriptインターフェイスreagentやClojureScriptの変更を自動でブラウザに送信するfigwheelなど便利なlibraryを導入してくれます。 (これらの詳しい説明についてはネット上にたくさん転がっているので、それらをご覧ください。)

また、この方法で作成したディレクトリはsrc, src-cljsの2種類のソースコードを格納するディレクトリがあって今ひとつな気がするので、 ここではspecの環境構築と併せて以下のようなディレクトリ構成にしていきます。

src
├──clj
└──cljs
spec
├──clj
└──cljs

ClojureのSpec

まずclojure向けのspecを構成していきます 現在src直下にcljファイルの格納されているfooディレクトリがあるので、これを以下のようにclj配下に持っていきます

src
└──clj
   └── foo

ついでにspecファイルを格納するディレクトリも追加しておきます 以下のような構成でディレクトリを作成してください。

spec
├──clj
│ └── foo
└──cljs
   └── foo

次にproject.cljを編集してSpecljの依存関係とtest pathを追加します

...

:plugins [[lein-ring "0.9.1"]
            [lein-environ "1.0.0"]
            [lein-ancient "0.6.0"]
            [ragtime/ragtime.lein "0.3.8"]
            [lein-cljsbuild "1.0.4"]
            [speclj "3.2.0"]]   ; <- これを追加

:source-paths ["src/clj"]  ; <- これを追加
:test-paths ["spec/clj"]  ; <- これを追加


:dev {:dependencies [[ring-mock "0.1.5"]
                        [ring/ring-devel "1.3.2"]
                        [pjstadig/humane-test-output "0.7.0"]
                        [leiningen "2.5.1"]
                        [figwheel "0.2.5"]
                        [weasel "0.6.0"]
                        [speclj "3.2.0"]  ; <- これを追加
                        [com.cemerick/piggieback "0.1.6-SNAPSHOT"]]
...

一旦ライブラリをインストールしておきましょう$ lein deps

続いてspec/clj/foo/core_spec.cljというファイルを作成して、以下のようなダミーのテストを記述します

(ns foo.core-spec
  (:require [speclj.core :refer :all]
            [foo.core :refer :all]))

(describe "Truth"

  (it "is true"
    (should true))

  (it "is not false"
    (should-not false)))

(run-specs)

これで以下のコマンドを実行するとSpecljによるテストが実行されるはずです

$lein spec spec/clj

ClojureScriptのSpec

続いてClojureScriptのSpecです ClojureScriptのspecにはphantomjsが必要ですのでインストールしておいてください。

またここでは PhantomJS 1.9.8での動作を想定しています

(※ PhantomJS 2.0ではうまく動作しないようです。)

luminusによってClojureScriptはsrc-cljsに格納されているのでこれをsrc/cljsに移動します。 (src-cljsのままでいい場合はこの作業は不要です)

src
├──clj
│ └── foo
└──cljs
   └── foo

ディレクトリを移動したらproject.cljでsrc-cljsとなっている部分は全てsrc/cljsと変更しておいてください。

次にproject.cljにテスト向けのbuild情報を記述します。

devのprofile情報に対して以下のように変更してください

:cljsbuild {:test-commands {"spec" ["phantomjs" "bin/speclj" "resources/public/js/app_test.js"]}  ; <- これを追加
                     :builds {:app {:source-paths ["env/dev/cljs"]}
                              :test {:source-paths ["src/cljs" "spec/cljs"]    ; <- これを追加
                                     :compiler {:output-to     "resources/public/js/app_test.js"   ; <- これを追加
                                                :output-dir    "resources/public/js/test"   ; <- これを追加
                                                :source-map    "resources/public/js/test.js.map"   ; <- これを追加
                                                :externs ["react/externs/react.js"]   ; <- これを追加
                                                :optimizations :whitespace   ; <- これを追加
                                                :pretty-print  false}}}}   ; <- これを追加

project rootにbin/specljというファイルを作成して以下のように記述します (※ Speclj公式のtutorialに対してReactの使用も考慮に入れた変更を加えています)

#! /usr/bin/env phantomjs

var fs = require("fs");
var webpage = require('webpage').create();
var system = require('system');
var url = phantom.args[0];

webpage.onConsoleMessage = function (x) {
  system.stdout.write(x)
};

webpage.injectJs("env/test/js/polyfill.js");
// note the polyfill file, this is added to play nice with React

webpage.injectJs(url);

var result = webpage.evaluate(function () {
    speclj.run.standard.armed = true;
    return speclj.run.standard.run_specs(
        cljs.core.keyword("color"), true
    );
});

phantom.exit(result);

env配下にtest/js/polyfill.jsというファイルを作成し以下のように編集します。 (phantomjsでcomponentを扱うために必要みたい(適当))

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          return fToBind.apply(this instanceof fNOP && oThis
                 ? this
                 : oThis,
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

spec/cljs/foo/core_spec.cljsを作成して以下のようにダミーのspecを記述します

(ns foo.core-spec
  (:require-macros [speclj.core :refer [describe it should should-not run-specs]])
  (:require [speclj.core]
            [foo.core :as my-core]))

(describe "Truth"

  (it "is true"
    (should true))

  (it "is not false"
    (should-not false)))

(run-specs)

以下のようにClojureScriptをcompileして、testタスクを実行したらSpecljによるClojureScript向けのspecが実行されるはずです

$ lein cljsbuild once
$ lein cljsbuild spec

これでclojureでの快適なTDD/BDD環境が整いました!

こんなエラーが出たら

以下のようなエラーが出たらPhantomjsがReactのcomponent操作などに対応できていない可能性があります。

TypeError: 'undefined' is not a function (evaluating 'ReactElementValidator.createElement.bind(
      null,
      type
    )')

おそらくtest/js/polyfill.jsかbin/specljの設定がうまくできていないのでしょう。

polyfill.js or bin/specljを見なおして見てください。

LuminusでJSON APIを作る

ClojureのWebアプリケーションフレームワークLuminusでJSON APIを作る際にRoutingで少し躓いたので手順をメモ

src/APP_NAME/routes/home.clj

これだけではダメ

(defn ping-json []    ; <- 追加
  (json {:response  "PONG!!!"}))    ; <- 追加

(defroutes home-routes
   ...
  (GET "/ping" [] (ping-json)))    ; <- 追加

namespaceにnoire.responseを追加すると/pingにアクセスしたときJSONが返却されるようになる

(ns APP_NAME.routes.home
  (:require [compojure.core :refer :all]
                  ...
                [noir.response :as response]))    ; <- 追加

...

(defn ping-json []    ; <- 追加
  (response/json {:response  "PONG!!!"}))    ; <- 追加

(defroutes home-routes
  (GET "/" [] (home-page))
  (GET "/about" [] (about-page))
  (GET "/ping" [] (ping-json)))    ; <- 追加

Rubyでも音楽をcoding

Clojureで音楽をコーディングする記事をいくつか書いてきました。

実はSonic Piというものを使えばRubyでも音楽をコーディングすることができます。 しかもOvertoneよりも環境構築が簡単。 下記のページからインストーラをDLしてインストールするだけです。

Sonic Pi

プログラミング教材としての側面もあるようで、 公式サイトには子どもたちがコーディングに取り組んでいる様子も紹介されています。

OvertoneはCUIベースのコーディング環境、jarの依存関係管理など周辺知識が求められるので、 とっつきにくい部分もあるのかなと思いますが、 Sonic PIはインストールしたらすぐ使うことができて、わかりやすいGUIエディタなので さくっと音楽コーディングを体験してみたいという方はこちらから入るのも良いのではないかと思います。

Overtoneで音楽をコーディング時の指針メモ

Overtoneで音楽を鳴らして遊んでいるときにコーディングに関して感じたことのメモ

Overtoneのチュートリアルにあるswingerメソッドはat関数を何度も書いていて冗長

(defn swinger [beat]
  (at (metro beat) (o-hat))
  (at (metro (inc beat)) (c-hat))
  (at (metro (+ 1.65 beat)) (c-hat))
  (apply-at (metro (+ 2 beat)) #'swinger (+ 2 beat) []))

Swing · overtone/overtone Wiki · GitHub

音の進行をコレクションで表現し、その要素に対して関数を作用させて演奏させられるとスマート。 例えば"音の長さ", "音"のvectorを要素とするリスト(..., [nome, sound], ...)に 別のチュートリアルにあるlooperメソッドのようなものを作用させるなど

(defn looper [nome sound]    
    (let [beat (nome)]
        (at (nome beat) (sound))
        (apply-by (nome (inc beat)) looper nome sound [])))

Metronome and sequencing · overtone/overtone Wiki · GitHub

また、視点をもう一つ高くして、Aメロ,Bメロ, サビなどのまとまりに作用させる関数を用意するとよりスマートになりそう。

OvertoneをEmacsで演奏してみる

EmacsでOvertoneの演奏環境構築は以下の記事を参照ください

EmacsでOvertone演奏環境構築 - のぶLab.

この記事ではOvertoneのGithub WikiにあるSwingチュートリアルEmacs Live上でやってみます。

また、前回の記事で作成したClojureプロジェクトを引き続き使っていきます。

事前準備

Emacs Liveを立ち上げる

$ emacs

演奏内容を記載するファイルを開く

任意のcljファイルに演奏内容を記載していきます ここではとりあえずsrc/hogehoge/core.cljに記載します

C-x C-f src/hogehoge/core.clj

REPLを立ち上げ

以下のコマンドでREPLを立ち上げます 

M-x cider-jack-in

(前回はホスト名やポート番号を指定していたが、この方法だとREPLの立ち上げからやってくれるらしい)

演奏内容の記述

実際に演奏内容を書き連ねていきます

使用するパッケージを記述

namespaseにOvertoneを使用することを明記します

(ns hogehoge.core
  (:use [overtone.live]))

ハイハット

ハイハットの音をシミュレートする関数を書いていきます

(definst c-hat [amp 0.8 t 0.04]
  (let [env (env-gen (perc 0.001 t) 1 1 0 1 FREE)
        noise (white-noise)
        sqr (* (env-gen (perc 0.01 0.04)) (pulse 880 0.2))
        filt (bpf (+ sqr noise) 9000 0.5)]
    (* amp env filt)))


(definst o-hat [amp 0.8 t 0.5]
  (let [env (env-gen (perc 0.001 t) 1 1 0 1 FREE)
        noise (white-noise)
        sqr (* (env-gen (perc 0.01 0.04)) (pulse 880 0.2))
        filt (bpf (+ sqr noise) 9000 0.5)]
    (* amp env filt)))

(ホワイトノイズでハイハットの音を作っているんですね。 こういう音作りも楽しそう。)

関数を書き終わったらカーソルを末尾に置いて、以下のコマンドでREPLに読み込ませることができます (実際どうなっているのかはよくわからない。とりあえず、あとで記述する関数からこれらを呼び出す場合はやっておかないといけない)

C-x C-e

テンポ

(def metro (metronome 120))

Overtoneライブラリのmetronomeという関数を使ってテンポを表現します

演奏内容

Swingの演奏を表現する関数を記述します

(defn swinger [beat]
  (at (metro beat) (o-hat))
  (at (metro (inc beat)) (c-hat))
  (at (metro (+ 1.65 beat)) (c-hat))
  (apply-at (metro (+ 2 beat)) #'swinger (+ 2 beat) []))

演奏する

演奏内容を表現した関数を呼び出します

(swinger (metro))

この関数末尾で以下のコマンドを実行するとSwingyな音が流れてきます (その前にこれまで書いてきた関数にも以下のコマンドを実行を忘れずに)

C-x C-e

演奏をやめる

演奏内容を表現する関数などを書き換えるなどいろいろ手段はあるかと思いますが、以下の関数で演奏をやめることができます

(stop)

以上。 ここまででClojureを書いてなんか音楽っぽいものを鳴らせるようになりました。

EmacsでOvertone演奏環境構築

OvertoneをEmacsで快適に演奏できるEmacs Liveというものがあるようなので、環境構築をしてみました

環境 Mac OS X Yosemite

Overtoneを使えるようにするまでは以下の手順を参考にしてください。

Overtoneを使ってClojureで音楽をcoding - のぶLab.

Emacs v24.4をインストール

現在、Emacs Liveはv24.3以上でないとダメなようなので、最新版をインストールします

$ brew upgrade
$ brew install --cocoa emacs

Emacsエイリアスを設定 .bashrcに以下を追加

alias emacs="/usr/local/Cellar/emacs/24.4/bin/emacs"

Setup

※ これは.emacs.dディレクトリを置き換える方法を取るので、普段使っている環境が壊れる可能性があります。必要があればVagrantなどを使って環境を分けるなど対応してください。

$ cd
$ git clone git@github.com:overtone/emacs-live.git .emacs.d

$ emacsコマンドでEmacsを起動 画面にAAでEmacs Liveと表示されていたらOK

OvertoneプロジェクトのREPLを起動

別ターミナルを開いて以下を実行

$ cd hogehoge
$ lein repl

EmacsとREPLを接続

以下のコマンドを実行

M-x cider [enter]

ホスト名、ポート番号を聴かれるので入力 hostname : localhost port number : [lein replで起動したREPLのポート番号]

Emacs上のREPLからOvertoneを使ってみる

user> (use 'overtone.live)
user> (use 'overtone.inst.piano)
user> (piano)

ピアノの音が鳴ればOK

あとがき

音を鳴らすだけならEmacsなど使う必要はないが、 Live Codingなどをする際にはEmacsなどの高機能な環境だと演奏も楽になるかもしれない。

Overtoneを使ってClojureで音楽をcoding

Overtoneとは

overtone/overtone · GitHub

プログラミング言語Clojureを使って音楽をコーディングするプラットフォームです このOvertoneを使って活動しているアーティストもいるようです (Meta-eXRepl Electricなど)

Overtoneを使ってみる

Overtoneを以下の方法でサクッと試すことができます (Mac OS X Yosemite)

Leiningen(GradleとかMavenClojure版みたいなもの)をインストール

$ brew install leiningen

overtoneプロジェクトの作成

$ lein new hogehoge

projectの依存関係にovertoneを追加

$ cd hogehoge
$ vi project.clj

dependenciesにovertoneを追加

 :dependencies [... [overtone "0.9.1"]]

REPLを起動

$ lein repl

依存するjarがダウンロードされて、REPLが起動する

起動したら以下のようにしてovertoneを起動

user> (use 'overtone.live)

起動したら試しにピアノを鳴らしてみる

user> (use 'overtone.inst.piano)
user> (piano)

これでピアノの音が鳴るはず

もちろん単音だけでなく旋律をコードで表現して演奏することも可能

overtoneから呼び出せる関数は以下のcheat sheetから確認できます

https://github.com/overtone/overtone/raw/master/docs/cheatsheet/overtone-cheat-sheet.pdf

ClojureScriptでNodeJSアプリの作成

ClojureScriptで書かれたNode.js + React + Restify + MongoDBなフルスタックwebアプリ構築メモ

ClojureScriptとは

Clojure同等の記述からJavaScriptへ変換できる言語です

clojure/clojurescript · GitHub

ClojureScript環境構築

ClojureScriptを使う際はleiningenをインストールすると捗るらしい

Clojurescript/Om始めよう #0 - Qiita

ClojureScriptでReactを使う

omというClojureScriptでReactを扱うためのinterfaceがあるので、それを使います

swannodette/om · GitHub

導入方法、使い方は以下を参考に

Clojurescript/Om始めよう #0.5: Figwheelでコードを自動リロード - Qiita

Clojurescript/Om始めよう #1: なぜOmか? - Qiita

ClojureScriptでNodeJS, Restifyを使う

以下を参考に

Getting Started With ClojureScript For Node.js

Restify With ClojureScript

project.cljのbuildで :target :nodejs となっているので、client, serverで分ける必要があるかも(ないかも?)

MongoDBを使う

// TODO あとで書く

scopeでmodelにDBアクセス責務を移譲

RailsではModel.where()を使っていろいろな場所で同条件のクエリを発行することが可能だが、 修正が必要になった際に複数箇所修正が必要になったり、DRYの理念に則っていない。

これらをうまく解消するために、scopeという機能を使ってクエリ発行の責務をmodelに任せる

scopeを定義

class User < ActiveRecord::Base
  scope :activated, -> { where(activated: true)}
end

呼び出し方

User.activated

これでscopeに記述した条件のクエリが発行される。

また呼び出し例からわかる通り、where()で条件を記述するよりも scopeで文脈を示す方が何のデータを取得しているのか理解しやすくなる。

scopeを使うとDRYに則した実装になるだけでなく、可読性の向上も期待できる。

Fat Model解消のアイディア

肥大化したActiveRecordモデルをリファクタリングする7つの方法(翻訳) | TechRacho http://techracho.bpsinc.jp/hachi8833/2013_11_19/14738

※ concernにmodelの機能を移しても本質的には何も変わっていない. どこに何があるかわかりにくく見通しが悪い状態は続く

  • Valueオブジェクト

  • Serviceオブジェクト

  • Formオブジェクト

  • Queryオブジェクト複雑なクエリをここに展開

    • コンポジションでクエリ結合するなどの使い方

    • ViewオブジェクトRailsでの"View"とは異なるものなので"Viewモデル"と呼ぶと良さそう

  • Policyオブジェクトビジネスルールをカプセル化

    • メモリに展開したドメインモデルの操作に特化
  • Decoratorオブジェクト

Railsドメインロジックの実装方法まとめ - assertInstanceOf('Engineer', $a_suenami)

http://a-suenami.hatenablog.com/entry/2014/12/07/200427

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

リファクタリング:Rubyエディション

リファクタリング:Rubyエディション