データベースの継続と2番目の記事で展開します 。
ErlangでのWeb開発に関する一連の記事を公開し始めています。 多くの人々はErlangを試してみたいと思っていますが、入門コースは主に機能言語としてのErlangに関するものであり、実際のプロジェクトとは程遠いという問題に直面しています( Learn You Some Some Erlang for good! 一方、すべてのWeb開発チュートリアルは、読者がすでにErlangをよく知っていることを意味します。
この一連の記事は、Web開発(PHP、Ruby、Java)の経験はあるが、Erlang開発の経験はない開発者向けに設計されています。
タスクはブログを作成することです。 記事のコードはhttps://github.com/denys-potapov/n2o-blog-exampleで、完成したプロジェクトはhttp://46.101.118.21:8001/で見ることができます。 プロジェクトの特徴:
- コメントをリアルタイムで更新します。
- Facebook認証
- データはmnesiaに保存されます。
プロジェクトの中心はn2o花火です。 選択は非常に主観的ですが、生きているErlangフレームワークの中で 、n2oは私にとって最も「Erlangに似ている」ように見えましたが、ChicagoBossは他の言語のMVCフレームワークに似ています。
環境をカスタマイズする
Ubuntuで環境を構成しますが、他のOSでも同様に機能するはずです。 Erlangの最新バージョンwww.erlang-solutions.com/resources/download.htmlをダウンロードしてインストールします。
依存関係マネージャー
Erlangの標準的な依存関係マネージャーはrebarです。 しかし、この記事では、鉄筋の設定と互換性があり、動作が速く、テンプレートの変更を追跡できるn2oの作成者のmadを使用します。
curl -fsSL https://raw.github.com/synrc/mad/master/mad > mad chmod +x mad sudo cp mad /usr/local/bin/
madファイルへの変更を追跡するには、inotify-toolsをインストールする必要があります。
sudo apt-get install inotify-tools
アプリケーションのバックボーンを生成して起動します。
mad app "blog" cd blog mad deps compile plan repl
http:// localhost:8001 /でチャットが開き、Webソケット上でリアルタイムに更新され、さまざまなウィンドウから自分とチャットできます。
Madパラメーターは、依存関係の取得とアプリケーションの起動を担当します。
- deps-依存関係を取得します。
- コンパイル-アプリケーションをコンパイルします。
- 計画-起動計画を作成します。
- repl-コンソールを起動します。
プロジェクト構造
プロジェクトのファイル構造は、Erlangアプリケーションの標準です。
├──アプリ ├──rebar.config └──サンプル ├──ebin │├──... ├──priv │├──静的 ││... │└──テンプレート │└──index.html ├──rebar.config └──src ├──index.erl ├──routes.erl ├──sample.app.src └──sample.erl ├──デプス ├──rebar.config └──sys.config
構造に関する詳細は、 公式文書に記載されています 。
後ほど、ほぼすべてのファイルとフォルダーに精通しますが、今のところ、Erlangアプリケーションは通常、appsフォルダーにあるいくつかのアプリケーションで構成されていることを知る必要があります。 サンプルアプリケーションが1つあります。
- srcはソースコードです。
- ebin-コンパイルされたファイル。
- priv-他のプロジェクトファイル(この場合はテンプレートと静的)。
- index.erl-ヘッダーページ。
最初のコード
不要なファイルを削除します。
rm -r apps/sample/priv/static/
テンプレートには、Django Template LanguageのErlang実装であるErlyDTLを使用します。 したがって、構文はDjangoのようなテンプレートエンジン(Django、Twig、Mustache)に精通している人には明らかです。
アプリ/サンプル/ priv /テンプレート/ base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{% block title %}Erlang blog example{% endblock %}</title> <!-- Bootstrap --> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="//oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="//oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> <style> .container { max-width: 40em; } </style> </head> <body> <div class="container"> {% block content %}{% endblock %} </div> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> </body> </html>
アプリ/サンプル/ priv /テンプレート/ index.html
{% extends "base.html" %} {% block title %}Latest posts{% endblock %} {% block content %} <h1>Latest posts</h1> {{ posts }} {% endblock %}
index.erlを開き、コードを次のように置き換えます。
-module(index). -compile(export_all). -include_lib("n2o/include/wf.hrl"). -include_lib("nitro/include/nitro.hrl"). main() -> #dtl{file="index"}.
ファイルヘッダーでは、モジュールを宣言し、このモジュールからすべての関数をエクスポートすることを示し、2つのヘッダーファイルを含めます。
main / 1関数は、メインページが開かれたときに呼び出されます。 関数は、すぐにHTMLを返すか、DSL Erlangレコードを返すことができます。これについては後で説明します。 現時点では、レンダリングされたインデックステンプレートを返すだけです。 Erlangのドキュメントでは、関数は常にname / multiplicityとして記述されます 。 ここで、multiplicityは引数の数です 。
構文を知る
今こそ、構文の基本を理解する時です;これはwww.tryerlang.orgで最も迅速に行われます。 メインページにすべての投稿を表示します。 データベースは使用しませんが、投稿をコードに直接保存します。
ヘッダーファイル/apps/sample/include/records.hrlで、投稿を保存するためのエントリを記述します。
-record(post, {id, title, text, author}).
投稿を保存するためのモジュール/apps/sample/src/posts.erlを作成します。 モジュールは2つの関数をエクスポートします。get / 0-すべての投稿を返し、 get / 1 -Idによる投稿を返します。
-module(posts). -export([get/0, get/1]). -include("records.hrl"). get() -> [ #post{id=1, title="first post", text="interesting text"}, #post{id=2, title="second post", text="not interesting text"}, #post{id=3, title="third post", text="very interesting text"} ]. get(Id) -> lists:keyfind(Id, #post.id, ?MODULE:get()).
Erlangのレコードは構文上の砂糖であり、コンパイラはレコードをタプルに、フィールドをインデックスに置き換えます。 たとえば、#post.idは0に置き換えられます。
DSL
上で書いたように、関数はHTMLに変換されたErlangレコードを返すことができます。 index.erlを変更して、ページにすべての投稿のリストが表示されるようにします。
-module(index). -compile(export_all). -include_lib("n2o/include/wf.hrl"). -include_lib("nitro/include/nitro.hrl"). -include_lib("records.hrl"). posts() -> [ #panel{body=[ #h2{body = #link{body = P#post.title, url = "/post?id=" ++ wf:to_list(P#post.id)}}, #p{body = P#post.text} ]} || P <- posts:get()]. main() -> #dtl{file="index", bindings=[{posts, posts()}]}.
投稿ページを作成するには、パスを処理するモジュールを/apps/sample/src/routes.erlで指定します。
route(<<"post">>) -> post;
apps / sample / src / post.erlモジュールは、投稿データを含むテンプレートを表示するだけです:
モジュール
-module(post). -compile(export_all). -include_lib("n2o/include/wf.hrl"). -include_lib("records.hrl"). main() -> {Id, _} = string:to_integer(binary_to_list(wf:q(<<"id">>))), Post = posts:get(Id), #dtl{file="post", bindings=[{title, Post#post.title}, {text, Post#post.text}]}.
テンプレート:
{% extends "base.html" %} {% block title %}{{ title }}{% endblock %} {% block content %} <h1>{{ title }}<br /> <small>by {{ author }}</small> <p>{{ text }}</p> <h3>Comments</h3> {{ comments }} {% endblock %}
Webソケット
ここで最も興味深いのは、ブラウザとWebソケットを介したサーバーとの接続です。 投稿にコメントを作成し、リアルタイムで更新します。 これを行うには、ベーステンプレートにn2o初期化ライブラリを追加します。
<script>{{script}}</script> <script src='/n2o/protocols/bert.js'></script> <script src='/n2o/protocols/client.js'></script> <script src='/n2o/protocols/nitrogen.js'></script> <script src='/n2o/validation.js'></script> <script src='/n2o/bullet.js'></script> <script src='/n2o/utf8.js'></script> <script src='/n2o/template.js'></script> <script src='/n2o/n2o.js'></script> <script>protos = [ $bert, $client ]; N2O_start();</script>
また、post.erlモジュールで、イベントハンドラーとコードを追加してコメントを表示します。
main() -> Id = wf:to_integer(wf:q(<<"id">>)), Post = posts:get(Id), #dtl{file="post", bindings=[{title, Post#post.title}, {text, Post#post.text}, {comments, comments()}]}. comments() -> [#textarea{id=comment, class=["form-control"], rows=3}, #button{id=send, class=["btn", "btn-default"], body="Post comment",postback=comment,source=[comment]} ]. event(comment) -> wf:insert_bottom(comments, #blockquote{body = #p{body = wf:html_encode(wf:q(comment))}}).
ボタンが表示されたら、トリガーされるイベント(ポストバック)とサーバーに送信されるパラメーター(ソース)を示します。 イベント(コメント)関数では、リストの下部にコメントを追加するコードをクライアントに送信します。 このコメントは他のクライアントには届きませんが、修正します。
event(init) -> wf:reg({post, post_id()}); event(comment) -> wf:send({post, post_id()}, {client, wf:q(comment)}); event({client, Text}) -> wf:insert_bottom(comments, #blockquote{body = #p{body = wf:html_encode(Text)}}).
ページがロードされるとinitイベントが発生し、プール{post、post_id()}からメッセージを受信するプロセスを登録します。
イベントイベント(コメント)にコメントを表示する代わりに、新しいコメントを含むメッセージをプールに送信します。 そして、コメント出力はイベントハンドラー({client、Text})で行われます。 これで、投稿の下で楽しいチャットができ、アプリケーションのバックボーンとしてmadを生成したコードをほぼ繰り返しました。
次の記事では、投稿とコメントをデータベースに保存し、Facebook経由で承認を追加します。