WicketのComponentにオブジェクトをインジェクトする

私はWicketというフレームワークが大好きです。
それからJavaEE6の新仕様であるCDIも大好きです。

だったらWicketの中でCDIを使いたくなるのは、ごく自然な流れ。

一応WicketCDIを統合するライブラリもあるにはあるようですが、まともに動いたことがないんですよね。。。
そんなわけで今回はこれを自作してみました。

public class MyApplication extends WebApplication {

    @Override
    public Class<? extends Page> getHomePage() {
        return HomePage.class;
    }

    @Override
    protected void init() {
        try {
            final BeanManager beanManager = InitialContext.doLookup("java:comp/BeanManager");
            getComponentInstantiationListeners().add(new IComponentInstantiationListener() {
                @Override
                public void onInstantiation(final Component pComponent) {
                    inject(beanManager, pComponent);
                }
            });
        } catch (final NamingException e) {
            throw new IllegalStateException(e);
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected void inject(final BeanManager pBeanManager, final Component pComponent) {
        final Class pType = pComponent.getClass();
        final Bean<Object> bean = (Bean<Object>) pBeanManager.resolve(pBeanManager.getBeans(pType));
        final CreationalContext<Object> cc = pBeanManager.createCreationalContext(bean);
        final AnnotatedType<Object> at = pBeanManager.createAnnotatedType(pType);
        final InjectionTarget<Object> it = pBeanManager.createInjectionTarget(at);
        it.inject(pComponent, cc);
    }
}

やってることは単純で、IComponentInstantiationListenerを使ってComponentがインスタンス化されるタイミングをフックして、そこでBeanManagerで明示的にインジェクトしてあげているだけです。

改善点はもちろんあります。JNDIが使えない環境では動かない、とか全てのComponentじゃなくてWebPageにだけDIすれば充分なんじゃない?とか。
でもアイデアの核は上記コードに凝縮されています。

CDIに勝ってる?

実はこの方法、本家CDIより機能的に上回っている部分があります。それはnewで生成したComponentにもDI可能なこと。

これはWicketのIComponentInstantiationListenerのおかげです。WicketのIComponentListenerListenerはCompnentクラスのコンストラクタの中から呼び出されるので、こんなことが可能になります。
以下がその部分のコードです。

    public Component(final String id, final IModel<?> model)
    {
        setId(id);
        getApplication().getComponentInstantiationListeners().onInstantiation(this);
        ...(以下省略)
    }

初めてこのコードを見たときは「なるほど!」と膝を打ってしまいました。
Wicketって、本当におもしろい。

eclipseのWTPでテストクラスをデプロイ対象から外す

eclipseWTPを使ってWebアプリを開発するときの話題。

開発環境とは言えWEB-INF/libの下には必要最低限のjarしか置きたくありませんが、JUnitのテストクラスを置いているプロジェクトの場合、JUnitのJARを置いておかないとデプロイに失敗してしまいます。

でもやっぱり余計なJARは置きたくない。

というわけでデプロイ対象からテストクラスを外す設定です。

テストクラス専用のソースフォルダと出力先フォルダを作る

こんな感じの設定にします。

テストクラスは、テストクラス専用のソースフォルダに格納するようにします。

Deployment Assemblyからテストクラス用のフォースフォルダを外す


以上でOK!

StrutsのメッセージファイルをXMLにする

久々のJavaネタ。Strutsの話です。

Strutsではメッセージをpropertiesファイルに記述しますが、これを変更してXMLで書けるようにする方法を紹介します。

仕様

今回のサンプルでは、メッセージファイルはクラスパス直下に置くことにします。

言語化を考慮して、メッセージファイルの末尾にはロケールを付与するようにします。
(ファイル名の例)

message_ja.xml


XMLの形式はこんな感じで。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<messages>
    <message key="k0">
        <text>メッセージ0</text>
    </message>
    <message key="k1">
        <text>メッセージ1</text>
    </message>
    <message key="k2">
        <text>メッセージ2</text>
    </message>
</messages>

見たまんま。

クラスを作る

MessageResourcesとMessageResourcesFactoryを拡張したクラスを作ります。

まずはMessageResourcesの拡張。長いですが省略なしで。なおXMLのパースにはJAXBを使っています。

/**
 * 
 */
package aaa.bbb;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.bind.JAXB;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.apache.struts.util.MessageResources;
import org.apache.struts.util.MessageResourcesFactory;

/**
 * @author jabaraster
 */
public class XmlMessageResources extends MessageResources {
    private static final long                       serialVersionUID = -1390664385723178825L;

    private final Map<Locale, Map<String, Message>> messages         = new ConcurrentHashMap<Locale, Map<String, Message>>();

    /**
     * @param pFactory
     * @param pConfig
     */
    public XmlMessageResources(final MessageResourcesFactory pFactory, final String pConfig) {
        super(pFactory, pConfig);
    }

    /**
     * @see org.apache.struts.util.MessageResources#getMessage(java.util.Locale, java.lang.String)
     */
    @Override
    public String getMessage(final Locale pLocale, final String pMessageKey) {
        Map<String, Message> m = this.messages.get(pLocale);
        if (m == null) {
            m = load(pLocale, this.config);
            this.messages.put(pLocale, m);
        }
        final Message message = m.get(pMessageKey);
        if (message == null) {
            return "*** no message found for '" + pMessageKey + "' ***";
        }
        return message.getText();
    }

    private static void appendIfNotNull(final StringBuilder pBuffer, final String pToken) {
        if (pToken != null && pToken.trim().length() > 0) {
            pBuffer.append("_" + pToken);
        }
    }

    private static Map<String, Message> load(final Locale pLocale, final String pConfig) {
        try {
            final String path = resolvePath(pLocale, pConfig);
            final URL resourceLocation = XmlMessageResources.class.getResource(path);
            if (resourceLocation == null) {
                throw new IllegalStateException("メッセージリソース " + path + " が見付かりませんでした.");
            }
            final InputStream in = new BufferedInputStream(resourceLocation.openStream());
            final Messages messages = JAXB.unmarshal(in, Messages.class);
            final Map<String, Message> ret = new ConcurrentHashMap<String, XmlMessageResources.Message>();
            for (final Message msg : messages.getList()) {
                ret.put(msg.getKey(), msg);
            }
            return ret;

        } catch (final IOException e) {
            throw new IllegalStateException(e);
        }
    }

    private static String resolvePath(final Locale pLocale, final String pKey) {
        final StringBuilder buffer = new StringBuilder();
        buffer.append("/");
        buffer.append(pKey);
        appendIfNotNull(buffer, pLocale.getLanguage());
        appendIfNotNull(buffer, pLocale.getCountry());
        appendIfNotNull(buffer, pLocale.getVariant());
        buffer.append(".xml");
        return new String(buffer);
    }

    /**
     * @author jabaraster
     */
    @XmlRootElement
    private static class Message {
        private String key;
        private String text;

        /**
         * 
         */
        @SuppressWarnings("unused")
        public Message() {
            //
        }

        /**
         * @return the key
         */
        @XmlAttribute
        public String getKey() {
            return this.key;
        }

        /**
         * @return the text
         */
        public String getText() {
            return this.text;
        }

        /**
         * @param pKey the key to set
         */
        @SuppressWarnings("unused")
        public void setKey(final String pKey) {
            this.key = pKey;
        }

        /**
         * @param pText the text to set
         */
        @SuppressWarnings("unused")
        public void setText(final String pText) {
            this.text = pText;
        }
    }

    /**
     * @author jabaraster
     */
    @XmlRootElement
    private static class Messages {
        private List<Message> list = new ArrayList<XmlMessageResources.Message>();

        /**
         * @return the list
         */
        @XmlElement(name = "message")
        public List<Message> getList() {
            return this.list;
        }

        /**
         * @param pList the list to set
         */
        @SuppressWarnings("unused")
        public void setList(final List<Message> pList) {
            this.list = pList;
        }
    }
}

次にMessageResourceFactoryの拡張。さっき作ったXmlMessageResourcesを返すようにするだけです。

import org.apache.struts.util.MessageResources;
import org.apache.struts.util.MessageResourcesFactory;

/**
 * @author jabaraster
 */
public class XmlMessageResourcesFactory extends MessageResourcesFactory {
    private static final long serialVersionUID = -5520240862924828575L;

    /**
     * @see org.apache.struts.util.MessageResourcesFactory#createResources(java.lang.String)
     */
    @Override
    public MessageResources createResources(final String pConfig) {
        return new XmlMessageResources(this, pConfig);
    }
}

sturts-config.xmlを編集

XmlMessageResourcesFactoryをstruts-config.xmlに設定します。
今回は2つメッセージファイルを設定する例を挙げます。
(修正前)

<message-resources parameter="application"
    factory="org.seasar.struts.util.S2PropertyMessageResourcesFactory"/>
<message-resources parameter="label" key="label"
    factory="org.seasar.struts.util.S2PropertyMessageResourcesFactory"/>

(修正後)

<message-resources parameter="application"
    factory="aaa.bbb.XmlMessageResourcesFactory"/>
<message-resources parameter="label" key="label"
    factory="aaa.bbb.XmlMessageResourcesFactory"/>

メッセージファイルを作る

application_ja.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<messages>
    <message key="k0">
        <text>メッセージ0</text>
    </message>
    <message key="k1">
        <text>メッセージ1</text>
    </message>
    <message key="k2">
        <text>メッセージ2</text>
    </message>
</messages>

label_ja.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<messages>
    <message key="k3">
        <text>メッセージ3</text>
    </message>
    <message key="k4">
        <text>メッセージ4</text>
    </message>
    <message key="k5">
        <text>メッセージ5</text>
    </message>
</messages>

使う

準備は完了です。

JSPでメッセージを表示してみます。

<%@ page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
	<title>XMLでメッセージ</title>
	<meta charset="UTF-8">
</head>
<body style="line-height: 1.5em;">

  <bean:message key="k0"/> <br/>
  <bean:message key="k3" bundle="label"/>
	
</body>
</html>

実際に表示してみます。

ちゃんとメッセージが取得出来ました。

XMLにする意義

propertiesファイルはnative2asciiでUnicodeエスケープしないと使えません。
これはかなり不便です。

Unicodeエスケープ不要なフォーマットとしてはXMLJSONYAMLなど無数に考えられます。
その中にあって、XML

  • 別途ライブラリを追加する必要がない(Java標準で付属している)
  • JAXBという便利なツールがある

という点で有利です。

SAStrutsを使う場合の考慮点

上記サンプルはSAStrutsのHOT deployに対応していません。
SAStrutsを使う場合はご注意を。

YesodでHTML/JavaScript/CSSを共通化する

Webアプリを作ってると、HTML/JavaScript/CSSを共通化したくなることがあります。
Yesodでは、これはWidgetって仕組みを使うようです。

なお今回出てくるコードは全て、scaffoldで生成されたソース群がある環境を前提にしています。

Widgetとは?

WidgetについてはYesod Bookに説明があります。
たぶん「hamlet/julius/luciusの3点セットを作ると再利用可能な物が出来る」というところだと思います。

hamletはHTMLのテンプレート、juliusはJavaScriptのテンプレート、luciusはCSSのテンプレートです。


英語は得意ではないので正しく理解出来ている自信はありませんが、大きくは外していないと思います。

試してみたところ、常に3点セットである必要はなくて、1つ、あるいは2つが欠けていてもいいようです。

Widget例・submitボタンにスタイルと動作を与えてみる

ではWidgetを作ってみます。

削除用のsubmitボタンに、特定のスタイルと動作を与えてみましょう。



class属性"delete"のsubmitボタンはこんな外観になるようにして、さらにこのボタンを押すと確認ダイアログが出るようにします。キャンセルを押すとサブミットをキャンセル、OKを押すとサブミット実行、というわけですね。

これを「submit」という名前のWidgetとして作ってみます。

今回の場合、HTMLは必要ないのでhamletファイルは作らないことにします。

まずはCSSのテンプレート「submit.lucius」を作ります。ファイルの置き場所はtemplatesディレクトリです。なおhamletファイル、juliusファイルもこのディレクトリに置きます。

input[type="submit"].delete {
  font-weight: bold;
  color: rgb(255, 0, 0);
  background-color: rgb(255, 180, 180);
}

ごく普通のCSSです。

次にJavaScriptのテンプレート「submit.julius」を作ります。

$(function() {
  $("input[type=submit].delete").click(function() {
    return confirm("削除していい?")
  });
});

これもごく普通のJavaScriptなんですが、jQueryに依存しています。この依存はWidgetを使う側には意識させたくないです。
そこでWidgetを使うための関数を作って、それを通して使ってもらうようにします。この関数の中でjQueryへの依存を解決してしまうわけですね。

ここではWidgetsというモジュールを作って、そこにWidgetを使うための関数を定義することにします。

module Widgets where

import Import

submitWidget :: GGWidget Jabaraster (GHandler Jabaraster Jabaraster) ()
submitWidget = do
  addScriptRemote "https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"
  $(widgetFile "submit")

ポイントは2つです。

  • addScriptRemoteでjQueryへの依存を解決
  • $(widgetFile "submit")でjuliusファイルとluciusファイルを読み込んでパース

これでWidgetが作れました。

Widgetを使う

作ったWidgetを使うのは簡単で、単にsubmitWidget関数を呼び出せばOKです。
一例を挙げます。

getRootR :: Handler RepHtml
getRootR = do
  ((_, widget), enctype) <- runFormPost someForm
  defaultLayout $ do
    setTitle "title"
    submitWidget -- 作ったWidgetを使う
    $(widgetFile "homepage")

引数を取るWidget*1

引数を取るWidgetも作ってみましょう。例えば特定のid属性値に対して操作するjuliusは以下のように書けます。

$(function() {
  $("##{tagId}").click(function() {
    return confirm("削除していい?")
  });
});

#{tagId}の部分が引数の値で置き換えられます。このWidgetを使う関数は以下のように書けます。

submitWidget' :: Text -> GGWidget Jabaraster (GHandler Jabaraster Jabaraster) ()
submitWidget' tagId = do
  addScriptRemote "https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"
  $(widgetFile "submit2")

引数が多くなる場合は引数オブジェクトみたいなものを導入してもいいでしょう。その場合juliusは以下のように書きます。

$(function() {
  $("##{submitTagId context}").click(function() {
    return confirm("削除していい?")
  });
});

juliusの中でHaskellの関数が呼び出せます。もちろんhamletの中やluciusの中でも呼び出せます。これはめちゃくちゃ強力です(強力故、使い方には気を付けないといけないと思いますが)。

Widgetを使うための関数と引数オブジェクトは以下のようになります。

data SubmitWidgetContext = SubmitWidgetContext {
  submitTagId :: Text
  , submitOther :: Text
}

submitWidget'' :: SubmitWidgetContext -> GGWidget Jabaraster (GHandler Jabaraster Jabaraster) ()
submitWidget'' context = do
  addScriptRemote "https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"
  $(widgetFile "submit3")


今回はここまで。
なんか最近はYesodのエントリばかりで、いっそのことYesodをカテゴリにしてしまおうか、とか考えています。

*1:実は引数を取るWidgetは、引数の数が多くなってきたときに使い勝手が悪くなります。引数の数が多い関数の呼び出しは間違えやすいですから。引数オブジェクトを導入するという手もありますが、面倒くささは否めません。共通化したくてWidgetを導入したがために記述が面倒になる・・・このジレンマには、今のところ解決策を見いだせていません。

MacでHerokuにYesodを(Herokuに公開編)

さていよいよYesodアプリをHerokuに公開します。
ただ、当然ながらHerokuにアカウントが必要なので、作業はそこからスタートです。

相変わらずUbuntu on VirtualBoxで作業します。

HerokuにSign Up

これはHerokuのサイトから簡単に出来ました。

ちなみにHerokuのサイトの色使いが好きです。

Heroku Toolbeltをインストール

このツールをインストールすると、herokuコマンドを通してHerokuのリモートサイトを操作出来るようになります。

このツールをUbuntu on VirtualBoxにインストールしないといけないのですが、詳しい手順をメモし忘れたの紹介出来ません・・・
結構面倒だったので、自分としてもメモを取っておかなかったことを後悔しています。絶対、将来また同じことをやろうとして同じところにはまると思う。


ちなみにMacの場合、Heroku Dev Center内のGetting Started with Herokuの「Step 2: Install the Heroku Toolbelt」からダウンロード出来ます。

Gitをインストール

Herokuは先のHeroku Toolbeltに加えてgitを使います。例えば実行ファイルのアップロードはgitで行います。

sudo apt-get install git-core

Macの場合は

sudo port install git-core

で。
gitは今回初めて触りましたが、便利ですね。Subversionから鞍替えしようと思います。

gitリポジトリとHerokuスタックの作成

Herokuに実行ファイルをアップロードするのはgitで行います。そのため、gitのローカルリポジトリを作っておく必要があります。

Yesodアプリのルートディレクトリで作業します。

まずはgitローカルリポジトリの作成

git init

次にHerokuにスタックを作成。

heroku create --stack cedar

これでgitのリモートにherokuのサイトが登録されます。

Yesodアプリのビルド

cabal install

Yesod0.9.4からは、-fproductionフラグは不要になっているので、このコマンド一発でビルド出来ます。

これでdist/build/first-heroku/に実行ファイルfirst-herokuが出来ます。

Procfileを移動して編集

deploy/ProcfileはHerokuにデプロイするときに必要なファイルで、ルートディレクトリに置いておく必要があります。

mv deploy/Procfile .

また、Yesod0.9.4でビルドすると作成される実行ファイルを起動するには、引数が1つ必要なのですが、scaffoldが生成してくれるProcfileにはこれが反映されていません。なのでProcfileを編集する必要があります。

編集前

web: ./dist/build/first-heroku/first-heroku -p $PORT

編集後

web: ./dist/build/first-heroku/first-heroku production -p $PORT

package.jsonを作成

よく分かりませんがpackage.jsonを作らないとHerokuで動かないらしいです。

echo '{ "name": "first-yesod", "version": "0.0.1", "dependencies": {} }' >> package.json

Herokuにアップロード

Herokuにアップロードするにはgitを使います。

まずはローカルリポジトリに今までに作ったファイルをコミット。

git add .
git commit -m "master"

さていよいよHerokuにアップロード!
ブランチを切った方がいい、と書いてあるサイトも多いしProcfileのコメントにもそう書いてありますが、Ubunto on VirtualBoxがそもそもアップロード専用なので、ここではブランチは切りません。

git push heroku master:master 

試してみる

アップロードしたアプリを試してみましょう!

heroku open

すると・・・

おおおおおおおおおおおおおお!!!!

表示された!!!!

これはすごい!!!!


目に見えておかしいところもあるのですが、Yesodの領域で解決出来る気がします。
最大の難関と思われたビルド&デプロイをクリア出来たのは大きい!

今回のソース、一応githubにアップしておきます。
http://github.com/jabaraster/first-heroku

HaskellでWebアプリを書いて公開出来るうれしさ

もうなんというか、感無量です。
数年越しで勉強しているHaskellでWebアプリ作って公開出来る時代が来るなんて!

あきらめずにやってきてよかったです。

これで「MacでHerokuにYesodを」計画の足がかりが出来ました。
うれしいです。

MacでHerokuにYesodを(Yesod導入・アプリビルド編)

前回cabal-installがインストール出来たので、いよいよYesodのインストール、更にアプリのビルドまで行ってみましょう!

相変わらずUbuntu on VirtualBoxで作業します。

yesodのインストール

cabal-installがインストール出来たので、Yesodはこれを使って楽にインストールしてしまいましょう。
参考ページは本家のGetting Started with Yesod

sudo cabal update
sudo cabal install Cabal cabal-install yesod

アプリの作成

yesodコマンドでアプリの雛形を自動生成します。
雛形とは言いましたが、今回はHerokuにテストで載せる実行ファイルがほしいだけなので何も手を加えないです。

yesod init

アプリ名はfirst-herokuにしました。

もしyesodコマンドがPATHの通っていない場所にある場合はパスのが必要です。私の環境では必要でした。

~/.cabal/bin/yesod init

ビルドに必要な依存ライブラリのインストール

実はYesodで作ったアプリをビルドするには、まだライブラリが足りません。
cabal-installで楽しようと思っても、依存を解決出来ずにインストール出来ません。

仕方がないのでcabal-installに頼らず、HackageDBからダウンロードしてきてインストールします。

インストールしたライブラリは以下。入れ子は依存関係を示しています。

  • language-javascript-0.4.5
    • utf8-light-0.4.0.1
    • happy-1.18.8
    • alex-3.0.1
      • QuickCheck-2.4.2

下の方からインストールするとうまくいきます。

アプリのビルド

いよいよビルドです。

cd first-heroku
sudo cabal install

ここでのcabalコマンドにはsudoを付ける必要があります。
Yesodアプリのビルドにはまだ足りないライブラリがあって、cabal installコマンドはこれらのライブラリをインストールしようとするのですが、このときにルート権限が必要なのです。
2つ目以降のYesodアプリの場合は、sudoは必須ではありません。

ずらずらっとコンソールにログが出て

...
Linking dist/build/first-heroku/first-heroku ...
Installing executable(s) in /home/****/.cabal/bin

おお!!成功!!
実行してみよう・・・ドキドキ・・・

sudo ~/.cabal/bin/yesod devel

とやってコンソールに

Devel application launched, listening on port 3000"

と表示されたら
http://localhost:3000/
にアクセスします。

・・・おおおっっっ!!
ついにやった!!

しかしまだ安心出来ません。

目標はあくまでHerokuに実行ファイルを公開すること。
これは次のエントリにて。

MacでHerokuにYesodを(GHC/cabal-install導入編)

前回Ubuntu on VirtualBox環境を作成したので、次はGHC
更にYesod開発に必須のcabal-installもインストールしてしまいます。

GHCをインストール

ここからはUbuntu on VirtualBoxでの作業。
まず依存ライブラリをインストール。

sudo apt-get install libreadline5 libgmpxx4ldbl libgmp3c2 libgmp3-dev

次にGHCのダウロードページからGHCのバイナリをダウンロードしてきましょう。
バージョンは、今回は7.0.4を採用。
ファイル名はghc-7.0.4-x86_64-unknown-linux.tar.bz2となります。
32bit版とまちがわないように気を付けて下さい。

ダウンロードしたファイルを解凍。

tar xvf ghc-7.0.4-x86_64-unknown-linux.tar.bz2

解凍したディレクトリに降りていってインストール。

cd ghc-7.0.4
>||
./configure
sudo make install

ここでこんな↓エラーが出るかもしれません。

make -r --no-print-directory -f ghc.mk install BINDIST=YES NO_INCLUDE_DEPS=YES
mk/config.mk:28: /home/****//mk/project.mk: No such file or directory
mk/config.mk:210: /home/****//mk/install.mk: No such file or directory
ghc.mk:128: *** Please run ./configure first.  中止.
make: *** [install] エラー 2

project.mkとinstall.mkがホームディレクトリの下にないって言っています。
カレントの下のmkディレクトリにはあるので、無理矢理コピーするとうまくいきました。

mkdir ~/mk
cp ./mk/project.mk ~/mk
cp ./mk/install.mk ~/mk

cabal-installをインストール

cabal-installをインストールしますが、先に依存ライブラリをインストールする必要があります。

依存ライブラリの中にはapt-getでインストールすることが出来るものもありますが、GHC6まで一緒にインストールされてしまうようなので、HackageDBから一つ一つダウンロードしてインストールすることにしました。

インストールしたライブラリは以下の通りです。入れ子は依存関係を示しています。

  • HTTP-4000.2.2
    • network-2.3.0.8
      • parsec-3.1.2
        • text-0.11.1.12
          • deepseq-1.2.0.1
    • mtl-2.0.1.0
      • transformers-0.2.2.0
  • zlib-0.5.3.1
    • sudo apt-get install zlib1g-dev (Haskellのライブラリにあらず)

mtlはparsecのインストールにも必要なので、下からインストールして行くとOKです。

以上のライブラリをインストールしたら、cabal-installをインストールします。
Hackageからcabal-installのインストールファイルをダウンロードします。
バージョンは今回は0.10.2。
ん?0始まりっちゃーなにごと?

なおcabal-installのインストールの途中、Ubuntu on VirtualBoxが不安定になり再起動せざるを得なくなる、という現象が起きました。
これはゲストOSのメモリを増やしたら解消しました。

今回はここまで。
次はYesodのインストールです。

メモ・HackageDBからダウンロードしたライブラリのインストール手順

以下の5手。

tar xvf HackageDBからダウンロードしたファイル
cd 解凍したディレクトリ
sudo runghc Setup.hs configure

Setup.hsはSetup.lhsのこともあります。

sudo runghc Setup.hs build
sudo runghc Setup.hs install