スポンサーリンク

2015年6月6日

続!Phoenixのルーティング(Router)について分かったこと

タイトル: 続!Phoenixのルーティング(Router)について分かったこと

目的: ルーティング(Router)のChannel Routesについて分かったことを記述する。

実施環境:
OS: Windows8.1
Erlang: Eshell V6.4
Elixir: v1.0.4
Phoenix Framework: v0.13.1
Node.js: v0.12.4

目次:

  1. 概要
  2. Channel Routesって何?
  3. Channel Routesを使ってみる
  4. おまけ

1.概要

1.1
注意!!
この記事は、Phoenix - Guides RoutingをGoogle翻訳を使い、
素人翻訳して分かったことを書いています。

原文を丸々翻訳しているわけではないので、
流れや説明がおかしいところ多々ありますが、その点注意して下さい。
英語が分かる方は、公式を見た方が確実です。

また、管理人の英語力は本人がドン引きするほど低いので、
間違った解釈及び理解している部分が多分にあると思います。

それでも構わないという方は見て頂けたら嬉しいです。

また、ご指摘等があれば受け付けておりますゆえ、遠慮なくお願いします。
特に間違えて習得している部分は教えて頂ければ、本当に助かります。

1.2
前回の記事: Phoenixのルーティング(Router)について分かったこと

1.3
あらかじめ、テスト用のプロジェクトは作成しておいて下さい。

管理人は、以下の内容で実施しております。
  • アプリケーション名: hello_phoenix
  • 設定は全てデフォルト
  • 作業は、hello_phoenixディレクトリ内で実施(コマンドプロンプトを含む)

1.4
Channelsに関してちょっと余談です。(書くところ違う気がしますが・・・)

Channels Guideの翻訳をするつもりがなくなりました。
今までの独り言とかに色々書いていてどの口が言うのか・・・この口ですorz
より正確に言えば、必要がなくなったですね。

理由は、v0.11.0の翻訳でしたが既に翻訳をされている方がいます。
そちらと原文を参考にした方が効率が良いため、する気がなくなりました。
その分の労力は他に使いたいと思います。

v0.11.0の日本語訳記事ですが、Channels Guide原文を軽く翻訳した感じ・・・
v0.13.1でも問題なく通用しますので、そちらをご参考に実施して下さい。

作者様に感謝を捧げ、記事を利用させて頂きましょう。

2.Channel Routesって何?

2.1
チャネルの詳しい説明は、Channels Guideに投げるとして・・・ここではざっくりと行きます。

チャネルとは、複数のコンポーネントによる階層構造により構成されています。
そして、ソフトリアルタイムの機能をアプリケーションに追加するのがチャネルです。

んで複数のコンポーネントの内の一つが"Channel Routes"と言うことですね。

何故、こいつだけ別記事を上げているのかと言いますと・・・

まず一つ目、この機能の詳細な内容はRouter Guideに出てきていること。
(Channels Guideでは、そちらに投げてますね・・・)

二つ目、Routerの記事を上げた時に理解できなかったか忘れていのか・・・
記事の内容に書いていなかったためです。

2.2
機能自体は簡単です。

チャネルのルーティングを行う機能です。
コントローラと同じように、router.exにルーティング先は記述します。

ルーティング先の定義例としてはこんな感じです。
----
socket "/ws", HelloPhoenix do
  channel "sample_topic:*", SampleTopicChannel
end
----

チャンネルは、特定のトピックのためのソケットを介し
メッセージの受信および送信メッセージを処理する。

チャネルルートは、適切なチャネルへディスパッチするため、
ソケットとトピック別の要求に一致する必要がある。

私達は、ブロック、ソケットのマウントポイントへのパス、
及び完全に私たちのチャネルモジュール名を修飾するために
アプリケーションの名前を持つルータのソケットを定義する。

ようは、前にRouterでやった内容と似たようなこと定義しないとだめよ!ってことですね。

3.Channel Routestを使ってみる

3.1
チャネルのマクロを使用して、
RoomChannelのチャネルモジュールと
rooms:*のトピックでルーティング先を作ってみましょう。

/web/router.exを開いて以下の定義を追加します。
----
socket "/ws", HelloPhoenix do
  channel "rooms:*", RoomChannel
end
----

Tips
トピックは単なる文字列の識別子です。
トピックは以下のような形になります。
トピック: "topic:subtopic"

今回の場合は、
roomsがtopicで、*がsubtopicですね。
(*はワイルドカードとして動作します)

3.2
以下のコマンドでルーティング先を見ます
コマンド: mix phoenix.routes

以下のような出力結果が得られますね。
----
 web_socket_path  GET      /ws                Phoenix.Transports.WebSocket.upgrade/2
 web_socket_path  POST     /ws                Phoenix.Transports.WebSocket.upgrade/2
long_poller_path  OPTIONS  /ws/poll           Phoenix.Transports.LongPoller.options/2
long_poller_path  GET      /ws/poll           Phoenix.Transports.LongPoller.poll/2
long_poller_path  POST     /ws/poll           Phoenix.Transports.LongPoller.publish/2
----

私の環境だと他のルーティング先が追加されていて見辛いので、
Channelの部分だけ抜粋しています。

私達のソケットの定義は、
4つルートと二つの別々のトランスポートメカニズム(WebSocketとLongPolling)を
含むパスに出て、拡張していることに注意して下さい。
(4つ?5つに変わったんだと思う)

3.3
チャネルがトランスポートの一種類のみによって処理されていることを確認したい場合、
このように、オプションを介して指定することができます。

/web/router.exを開いて以下の行を編集して下さい。
変更前: channel "rooms:*", RoomChannel
変更後: channel "rooms:*", RoomChannel, via: [WebSocket]

3.4
コマンド: mix phoenix.routes

あれ?出力変わってないですね・・・
----
 web_socket_path  GET      /ws                Phoenix.Transports.WebSocket.upgrade/2
 web_socket_path  POST     /ws                Phoenix.Transports.WebSocket.upgrade/2
long_poller_path  OPTIONS  /ws/poll           Phoenix.Transports.LongPoller.options/2
long_poller_path  GET      /ws/poll           Phoenix.Transports.LongPoller.poll/2
long_poller_path  POST     /ws/poll           Phoenix.Transports.LongPoller.publish/2
----

原文読んでみましょう・・・
引用
----
If we do this, $ mix phoenix.routes will still show the same four paths, because they are related to the socket, not the channel. Messages for the "rooms:*" topic, however, would all be handled exclusively over WebSockets.
----

これを行う場合、出力結果はまだ、同じ4つのパスが表示されます。
それらはソケットではなく、チャネルに関連しているから。
しかし、メッセージ rooms:*のトピックは、全てWebSocketの上で排他的に処理されます。

多分、こんなこと言ってると思う。
出力が4つ?5つに変わったんだろうけど、問題ないみたいね。
何れにせよ、動かす段になってみないことには分からないね。これは・・・

次行こう!

3.5
各ソケットは、複数のチャネルの要求を処理することができます。

/web/router.exにルーティング先を追加して下さい。
----
socket "/ws", HelloPhoenix do
  channel "rooms:*", RoomChannel, via: [WebSocket]
  channel "foods:*", FoodChannel
end
----

コマンド: mix phoenix.routes

出力変わりませんね。
ソケットの上でチャネルが複数流れるから、
それぞれのチャネルの定義は出てこないと言うことでしょう。

3.6
複数のソケットを定義も定義できるとのこと。
やってみましょう~そうしましょう~。

/web/router.exにルーティング先を追加して下さい。
----
socket "/ws", HelloPhoenix do
  channel "rooms:*", RoomChannel, via: [WebSocket]
  channel "foods:*", FoodChannel
end

socket "/another_ws", HelloPhoenix do
  channel "news:*", NewsChannel
end
----

コマンド: mix phoenix.routes

出力結果
----
 web_socket_path  GET      /ws                Phoenix.Transports.WebSocket.upgrade/2
 web_socket_path  POST     /ws                Phoenix.Transports.WebSocket.upgrade/2
long_poller_path  OPTIONS  /ws/poll           Phoenix.Transports.LongPoller.options/2
long_poller_path  GET      /ws/poll           Phoenix.Transports.LongPoller.poll/2
long_poller_path  POST     /ws/poll           Phoenix.Transports.LongPoller.publish/2
 web_socket_path  GET      /another_ws        Phoenix.Transports.WebSocket.upgrade/2
 web_socket_path  POST     /another_ws        Phoenix.Transports.WebSocket.upgrade/2
long_poller_path  OPTIONS  /another_ws/poll   Phoenix.Transports.LongPoller.options/2
long_poller_path  GET      /another_ws/poll   Phoenix.Transports.LongPoller.poll/2
long_poller_path  POST     /another_ws/poll   Phoenix.Transports.LongPoller.publish/2
----

another_wsのソケットが増えましたね。
各ソケットは、5パスになる。(WebSocket: 2、LongPoller: 3)

4.おまけ

4.1
全てのチャネルを単一のソケット上で処理したい場合・・・
以下のように定義する。
----
socket "/another_ws", HelloPhoenix, via: [WebSocket] do
  channel "news:*", NewsChannel
  channel "pets:*", PetChannel
end
----

これでWebSocket単一で、このチャネルが全て流れる。

4.2
さらに、単一で流しているソケットの中で別のソケットで流したい時は・・・
以下のように定義する。
----
socket "/another_ws", HelloPhoenix, via: [WebSocket] do
  channel "news:*", NewsChannel
  channel "pets:*", PetChannel
  channel "conversations:*", ConversationChannel, via: [LongPoller]
end
----

viaのviaっと。

4.3
Channels Guideに書いてある。
チャットシステムのアプリケーション例がGithub上にある。
また、実際に動作させているものがheroku上で動作している。
参考にすると習得が速い・・・かもしれない。

Heroku Demo: http://phoenixchat.herokuapp.com/

追記(2015/06/06-17:46)
上記のソース一式をクローンして、
ローカルで実行しようとしたんですけど、
何か動かなかったので、私の環境で動かすまでにしたこと書いときます。

まず、普通に動かそうとした。
>mix phoenix.server

ライブラリがないから入れてよって言われた。
ライブラリをダウンロードするため以下のコマンド実行~
>mix deps.get

再度、実行~
>mix phoenix.server

node_modulesがないようなことを言い始めた。
えっ!?mix phoenix.newする時に作ってくれてるんじゃないの?
npm関連を調べてみた。

>npm -v
バージョン表示

入ってるな・・・
じゃあ、node_modulesだけ別のPhoenixアプリケーションからコピーしてきたらどうなるか?

というわけで、適当にPhoenixのアプリケーションを作成した後、
node_modulesのディレクトリごとコピーして対象のアプリケーションにペーストした。

もう一回実行~
>mix phoenix.server

あっ!?動いた!!
よっし、じゃあブラウザでアクセスしてみよう。

うん、heroku上で動作しているサンプルと同じ動きをしている。
おk、これで終わり~

以上!!

以下、参考とさせて頂いたサイト様

管理人の独り言~
Channelsをやりながら、Routingの記事見直してたら・・・
あれ?Channel Routes書いて無くね?っと気づきました。

そして今回の記事を書くこととなったわけですね。
相変わらずの無計画進行~

しかし、高々一項目しかやってないのに・・・
いつも並にページ量あるな・・・
やっぱりRouting Guide長いよ。

2015年6月5日

Phoenixのテンプレート(Templates)について分かったこと

タイトル: Phoenixのテンプレート(Templates)について分かったこと

目的: Phoenixアプリケーションのテンプレート(Templates)について分かったことを記述する。

実施環境:
OS: Windows8.1
Erlang: Eshell V6.4
Elixir: v1.0.4
Phoenix Framework: v0.13.1
Node.js: v0.12.4

目次:

  1. 概要
  2. Templatesの基本的なこと
  3. (web.ex)メインビューを使ってみる?
  4. (Displaying Lists)リストはどうやって表示したらいいの?
  5. (Render templates within templates)テンプレートの中のテンプレート
  6. (Shared Templates Across Views)テンプレートの共有

1.概要

1.1
今日は、Templatesに関してです。

注意!!
この記事は、Phoenix - Guides TemplatesをGoogle翻訳の力を借り、
素人翻訳して分かったことを書いています。

原文を丸々翻訳しているわけではないので、
流れや説明がおかしいところ多々ありますが、その点ご注意して下さい。
英語が分かる方は、公式を見た方が確実です。

また、管理人の英語力は本人がドン引きするほど低いので、
間違った解釈及び理解している部分が多分にあると思います。

それでも構わないという方は見て頂けたら嬉しいです。

また、ご指摘等があれば受け付けておりますゆえ、遠慮なくお願いします。
特に間違えて習得している部分は教えて頂ければ、本当に助かります。

1.2
あらかじめ、テスト用のプロジェクトは作成しておいて下さい。
参考: Phoenixが欲しいって言うからNode.jsをインスコするよ!!


管理人は、以下の内容で実施しております。
  • アプリケーション名: hello_phoenix
  • 設定は全てデフォルト
  • 作業は、hello_phoenixディレクトリ内で実施(コマンドプロンプトを含む)

2.Templatesの基本的なこと

2.1
はい、Phoenixのテンプレートについてです。

HTTPのレスポンスを形成するためにデータを渡す。
Webアプリケーションの大体のレスポンスは、HTMLドキュメントだろう。
しかし、JSONやXMLである可能性もある。

テンプレートファイルの大部分はマークアップだが、
Elixirコードを記述している場合もある。

Phoenixのテンプレートは事前にコンパイルされている。
そのため、非常に高速である。

PhoenixのテンプレートシステムはRubyのERBと似ている。
実際には、Elixir自体の一部である。

View Guidesでやったように、
各テンプレートには、View名にちなんで名付けられたディレクトリが必要。
また、デフォルトでは/web/templates配下に配置する。

テンプレートをレンダリングするため、各Viewモジュールがある。

3.(web.ex)メインビューを使ってみる?

3.1
アプリケーションのメインビュー。

/web/web.exファイルにおける、
Viewブロック内の宣言の全ては全テンプレートに適応されるとのこと。

3.2
さてさて、ではちょっとアプリケーションに追加を行い、試してみましょう。
まずは、新しいルーティング先を追加します。

/web/router.exを開き、以下のように編集して下さい。
----
scope "/", HelloPhoenix do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  get "/test", PageController, :test
end
----

3.3
次は、PageControllerへtestアクションを追加します。
----
def test(conn, _params) do
  render conn, "test.html"
end
----

3.4
続いて、/web/web.exを開き、以下の行を編集します。
変更前: import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
変更後: import Phoenix.Controller, only: [get_flash: 2, action_name: 1, controller_module: 1]

3.5
次に、/web/views/page_view.exを以下のように編集します。
----
defmodule HelloPhoenix.PageView do
  use HelloPhoenix.Web, :view

  def handler_info(conn) do
    "Request Hendled By: #{controller_module conn}.#{action_name conn}"
  end

  def connection_keys(conn) do
    conn
    |> Map.from_struct()
    |> Map.keys()
  end
end
----

3.6
ルーティング先を作り、コントローラにアクションを定義し、
アプリケーションのメインビューへ変更を行いました。

後、足りないのは・・・そう!テンプレートファイルですね!!
別に大した内容を記述してるわけではないみたいですけど・・・

/web/templates/pageディレクトリへ以下のファイルを作成する。
作成ファイル: test.html.eex

内容は以下の通り。
----
<div class="jumbotron">
  <p><%= handler_info @conn %></p>
</div>
----

3.7
では実行してみましょう。
コマンド: mix phoenix.server
アドレス: http://localhost:4000/test

"Request Hendled By: Elixir.HelloPhoenix.PageController.test"
上記の文言が表示されていますね。

3.8
我々は、/web/views内のビューで関数を定義することができる。
その関数は、レンダリングするテンプレートで使用することができるようになる。
例えば、hendler_infoがその例です。(connection_keysはどうしたんだ?)

さて、私の理解が追いついていないので少し振り返ります。

  1. router.exへ新しいルーティング先の設定をした
  2. PageControllerにtestアクションを定義した
  3. web.ex(メインビュー)内の"import Phoenix.Controller..."を編集した
  4. PageViewへhandler_info、connection_keysを定義した
  5. test.html.eexでhandler_infoを呼び出した
  6. 実行結果または動作としては、実行したコントローラとアクションを取得し表示している

っと言ったところでしょう。
細かい部分に関しては疑問が多々ありますが、
この項目には書いていないのか、はたまた自分が読めていないだけなのか・・・
不明な部分を残すところです。

4.(Displaying Lists)リストはどうやって表示したらいいの?

4.1
テンプレート内に私たちが定義した値を表示しました。
では、リストはどのようにして全ての要素を表示させればよいでしょうか?

つまり、リストで渡すものの表示方法どうすんの?
っと言うのがこの項目のようですね。

実例、実例~動くものがあるのは、やっぱりいいですね~

4.2
/web/templates/page/test.html.eexを開いて、
内容を以下のように編集します。
----
<div class="jumbotron">
  <p><%= handler_info @conn %></p>

  <h3>Keys for the conn Struct</h3>

  <%= for key <- connection_keys @conn do %>
    <p><%= key %></p>
  <%= end %>
</div>
----

4.3
connection_keys関数出てきましたね。

では実行してみましょう。
コマンド: mix phoenix.server
アドレス: http://localhost:4000/test

画面表示を確認すると・・・
何か、いっぱい出てきましたね。

動作を見るにconnection_keys関数は、
conn構造体のkeysを抽出して、返してるといったところでしょうか。

そして、テンプレートの方でループで一つずつ各keyを出力させている。
そんなところでしょう。

5.(Render templates within templates)テンプレートの中のテンプレート

念の為言っておくが、男の中の漢みたいなものではない!!
全く関係ないのであしからずw

5.1
直訳すると・・・テンプレート内のテンプレートをレンダリング?

何だかテンプレートに
<p><%= key %></p>
ってあると読みにくいよね的なことを言ってます。

解決方法が、別のテンプレートを呼び出してレンダリングすればよいと・・・
関数のように呼び出せるとのこと。

レンダリングチェーンの一種だそうです。
分かりやすい例としてはレイアウトのテンプレートで別のテンプレート読んでますよね?
あんな感じでできるそうです。

となれば、機能ごとにテンプレートを分割して、
小さいテンプレートを組み合わせて表示ができるのでは?

んで、通常のテンプレートも他のテンプレートを有してよいっと!
んでんで、どうやってやるの?

5.2
まず、新しくテンプレートを作成します。(/web/templates/pageディレクトリへ)
作成ファイル: key.html.eex

内容は以下の通り!!
----
<p><%= @key %></p>
----

@が付く理由~
この新しいテンプレートは、
リスト内包表記の一部ではないため、
@keyとしなければ参照できない。

通常、テンプレートにデータを渡す方法は、
割り当てマップであり、割り当てマップから値を取得する方法は、
先行する@でキーを参照しなければならないからである。

5.3
/web/templates/page/test.html.eexを開き、以下の行を編集する。
変更前: <p><%= key %></p>
変更後: <%= render "key.html", key: key %>

5.4
はい、では実行して下さい。
コマンド: mix phoenix.server
アドレス: http://localhost:4000/test

表示は変わりませんね。

6.(Shared Templates Across Views)テンプレートの共有

6.1
現在のビューからレンダリングしているテンプレートがある。
これをテンプレートAとしよう・・・

そのテンプレートAから別のテンプレートを呼び出す。
この呼び出すテンプレートはテンプレートBとしよう・・・

テンプレートBを別のビューでレンダリングする。
と言うのがこの項目のようだ。

つまり、今のビューと別のビューでレンダリングされているテンプレートを
呼び出すにはどうやって行うのかやっていく項目。

実例を実施して動かしてみましょう。

6.2
/web/viewsディレクトリへ以下のファイルを作成する。
作成ファイル: shared_view.ex

内容は以下の通り。
----
defmodule HelloPhoenix.SharedView do
  use HelloPhoenix.Web, :view
end
----

6.3
/web/templatesディレクトリへ新しいディレクトリを作成する。
作成ディレクトリ: shared

/web/templates/page/key.html.eexを上記で作成したディレクトリへ移動する。
変更前: /web/templates/page/key.html.eex
変更後: /web/templates/shared/key.html.eex

6.4
/web/templates/page/test.html.eexを開き、以下の行を編集する。
変更前: <%= render "key.html", key: key %>
変更後: <%= render HelloPhoenix.SharedView, "key.html", key: key %>

6.5
そして実行する。
コマンド: mix phoenix.server
アドレス: http://localhost:4000/test

以前と同じ表示結果が画面に出力されていればおーるおーけー!!

Tips
SharedViewではなく、PageViewを使ってレンダリングしていた時、
呼び出し部分を明示的にすると以下のような形になる。
----
<%= for key <- connection_keys @conn do %>
  <%= render HelloPhoenix.PageView, "key.html", key: key %>
<% end %>
----

お疲れ様でした~、以上!!

以下、参考にさせて頂いたサイト様

管理人の独り言~
Elixir強化週間もあと少しを残すところとなった。
毎日更新はまだ続く・・・そろそろ疲れてきたのだが・・・
まだだ!まだやれる!!今週の日曜日まで頑張ればいいんだ!!!!

後は、ChannelとPlugあたりを理解しないとね~。
そして、掲示板もどきを作成~っと。
多分、強化週間中には到達しないと思うけどね。

2015年6月4日

[Elixir+MongoDB]MongoDBに接続するだけの簡単なお仕事!!

タイトル: [Elixir+MongoDB]MongoDBに接続するだけの簡単なお仕事!!

目的: ElixirからMongoDBのドライバを使ってみる。

実施環境:
OS: Windows8.1
Erlang: Eshell V6.4
Elixir: v1.0.4
elixir-mongo: v0.5.1
MongoDB:v3.0.3

目次:

  1. 概要
  2. "mix new"でプロジェクト作成
  3. Elixirのライブラリを取り込んでみよう!!
  4. MongoDBに接続するだけの簡単なお仕事!!

1.概要

1.1
今回の記事は少し趣向を変えます。
と言いますか、ずっと英語を素人翻訳するのがあきてきたので、
次の活力を得るためにElixir+MongoDBをやってみようかと思います。

この方の記事を参考に実施しました。
Qiita - Elixirで試しに何か書いてみる(その1)

しかし、CRUD操作まで辿り着けなかった・・・orz

1.2
環境はあらかじめ構築しておいて下さい。

  • MongoDBのインストール
  • Elixirのインストール

また、MongoDBは起動しておいて下さい。
参考までに・・・

2."mix new"でプロジェクト作成

2.1
コマンドプロンプトを開く。

作業ディレクトリを作成するので、
適切なディレクトリに移動しておくこと。

2.2
以下のコマンド実行する。
コマンド: mix new mongo_test

作業ディレクトリ名はお好きなものをどうぞ!!

ディレクトリが作成されているか確認する。
lsでもいいしGUIで見てもいいです。

これで、mix newを使ってプロジェクトができました。

2.3
作成したディレクトリへ移動し、以下のコマンドを実行する
コマンド: mix test

何もエラーが出なければおk。

3.Elixirのライブラリを取り込んでみよう!!

3.1
作業ディレクトリに移動する。

その中から、"/mongo_test/mix.exs"を開く。

3.2
色々書かれていると思うが、とりあえず目的だけ達する。

何がしたいか・・・
MongoDBのElixirドライバのライブラリが欲しい!!

下の方に"defp deps do"と言う部分がある。
ここに、以下の行を追加する。
追加: [{:mongo, "~> 0.5.1"}]


3.3
以下のコマンドを実行する。
コマンド: mix deps.get

これで、3.2で設定したライブラリがダウンロードできる。
依存関係とかも考慮しないといけないらしいが、
今回使うライブラリは一つだけなので気にしない。

hexの利用を聞かれると思うがyesで・・・
local.hexと各プロジェクトで混在できるらしい。

参考までにコマンドの実行結果
mongo_test>mix deps.get
Could not find hex, which is needed to build dependency :mongo
Shall I install hex? [Yn] y
* creating c:/Users/Takes_000/.mix/archives/hex.ez
Running dependency resolution
Dependency resolution completed successfully
  mongo: v0.5.1
  bson: v0.4.3
* Getting mongo (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/mongo-0.5.1.tar)
Fetched package
Unpacked package tarball (c:/Users/Takes_000/.hex/packages/mongo-0.5.1.tar)
* Getting bson (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/bson-0.4.3.tar)
Fetched package
Unpacked package tarball (c:/Users/Takes_000/.hex/packages/bson-0.4.3.tar)

4.MongoDBに接続するだけの簡単なお仕事!!

4.1
Elixirのソースコードは以下の通り作成した。


すまないが、これだけなんだ・・・

4.2
mixで実行するため以下のコマンドを使う。
コマンド: mix run ./lib/mongo_test.ex

4.3
MongoDBに接続し、DBとコレクションが作成されたか確認してみる。
(接続時、DB、コレクションがない場合は作成されて接続される)

以下、一連のMongoDB操作の流れ
mongo_test>mongo
MongoDB shell version: 3.0.3
connecting to: test
> db
test
> show databases
local        0.078GB
phoenix_bbs  0.078GB
> use phoenix_bbs
switched to db phoenix_bbs
> show collections
comments
system.indexes
>exit

問題なく作成されている。
CRUD操作は何れにせよやらなくてはいけないので、
また分かったら続きの記事を作成する。

追記(2015/06/04-21:20)
CRUD操作ができました。

修正したソースコードは以下の通りになります。


実行結果
insert result
[%{_id: ObjectId(55703854f328cfab505c6d99), comment: "hogehoge", name: "hoge",
   title: "hoge"},
 %{_id: ObjectId(55703854f328cfab505c6d9a), comment: "hugehuge", name: "huge",
   title: "huge"},
 %{_id: ObjectId(55703854f328cfab505c6d9b), comment: "darui", name: "darui",
   title: "blogger"},
 %{_id: ObjectId(55703854f328cfab505c6d9c), comment: "foobar", name: "foo",
   title: "bar"}]

update result
[%{_id: ObjectId(55703854f328cfab505c6d99), comment: "hogehoge", name: "hoge",
   title: "hoge"},
 %{_id: ObjectId(55703854f328cfab505c6d9a), comment: "hugehuge", name: "huge",
   title: "huge"},
 %{_id: ObjectId(55703854f328cfab505c6d9b), comment: "darui", name: "darui",
   title: "blogger"},
 %{_id: ObjectId(55703854f328cfab505c6d9c), comment: "fizzbazz", name: "fizz",
   title: "bazz"}]

delete result
[%{_id: ObjectId(55703854f328cfab505c6d99), comment: "hogehoge", name: "hoge",
   title: "hoge"},
 %{_id: ObjectId(55703854f328cfab505c6d9a), comment: "hugehuge", name: "huge",
   title: "huge"},
 %{_id: ObjectId(55703854f328cfab505c6d9b), comment: "darui", name: "darui",
   title: "blogger"}]

以上!!

以下、参考にさせて頂いたサイト様

管理人の独り言~
MongoDBに接続できるのにCRUD操作ができない・・・orz

Elixirの記法とかさっぱりやってないのがいけない。
Phoenixばかりではなく、Elixir自体も少しやらないと・・・

追記(2015/06/04-21:25)
CRUD操作できたー!!


2015年6月3日

Phoenixのビュー(Views)について分かったこと

タイトル: Phoenixのビュー(Views)について分かったこと

目的: Phoenixアプリケーションのビュー(Views)について分かったことを記述する。

実施環境:
OS: Windows8.1
Erlang: Eshell V6.4
Elixir: v1.0.4
Phoenix Framework: v0.13.1
Node.js: v0.12.4

目次:

  1. 概要
  2. Viewsの基本的なこと
  3. Viewsについてもっと
  4. レイアウトについて簡単に
  5. ErrorViewについて

1.概要

1.1
今日は、Viewsに関してです。

注意!!
この記事は、Phoenix - Guides ViewsをGoogle翻訳の力を借り、
素人翻訳して分かったことを書いています。

原文を丸々翻訳しているわけではないので、
流れや説明がおかしいところ多々ありますが、その点注意して下さい。
英語が分かる方は、公式を見た方が確実です。

また、管理人の英語力は本人がドン引きするほど低いので、
間違った解釈及び理解している部分が多分にあると思います。

それでも構わないという方は見て頂けたら嬉しいです。

また、ご指摘等があれば受け付けておりますゆえ、遠慮なくお願いします。
特に間違えて習得している部分は教えて頂ければ、本当に助かります。

1.2
あらかじめ、テスト用のプロジェクトは作成しておいて下さい。
参考: Phoenixが欲しいって言うからNode.jsをインスコするよ!!


管理人は、以下の内容で実施しております。
  • アプリケーション名: hello_phoenix
  • 設定は全てデフォルト
  • 作業は、hello_phoenixディレクトリ内で実施(コマンドプロンプトを含む)

2.Viewsの基本的なこと

2.1
ビューの機能簡単に・・・
ビューは、テンプレート(レイアウト含む)をレンダリングする。
また、生データを取得して、それを簡単にテンプレートが処理できるようにする。
Phoenix.Viewモジュールにレンダリング機能が定義されている。

ビューはコントローラから強い命名規則を受ける。
しかしコントローラは、/web/templates/各コントローラのディレクトリに
テンプレートをレンダリングするためビューが必要。

web/web.exのview関数の"use Phoenix.View, root: "web/templates""を変更することで
テンプレートのルートを変更できるようなことが書いてあります。

アプリケーションを作成した時に、デフォルトのビューとして
ErrorView、LayoutView、PageViewが/web/viewsディレクトリに生成される。

2.2
LayoutViewを見てみる。
/web/views/layout_view.exを開く。
----
defmodule HelloPhoenix.LayoutView do
  use HelloPhoenix.Web, :view
end
----

たった三行ですよ。
本当に定義されただけな感じがする。

っと思ったんですけど・・・この一行が・・・
/web/web.exにあるview関数を呼び出しているみたいです。
----
use HelloPhoenix.Web, :view
----

2.3
さて、ちょっと飽きてきたので実行して結果が見れることをやりましょう。
/web/templates/layout/application.html.eexを開き、以下の行を変更します。

変更前: <title>Hello Phoenix!</title>
変更後: <title><%= title %></title>

2.4
次に、/web/views/layout_view.exを開き、
以下の関数を追加しましょう!
----
def title do
  "Awesome New Title!"
end
----

2.5
では、実行してみましょう。
コマンド: mix phoenix.server

あれ・・・どこが変わったの?

あー、ブラウザのタブを見て下さい。
"Awesome New Title!"ってタブが出てるはずです。

しかしなるほど・・・
<%= %>でElixirのコードを実行する方法は前にやりましたね。
コントローラで値を渡した時、似たようなことをやりました。

それが、ビュー側だとこうやって実現できるわけですね。

2.6
少し内容を把握しましょう。
LayoutViewにtitleと言う関数を定義している。
その関数をテンプレートに埋め込み、呼び出している。
そして、実行されたtitle関数が文字列を返して出力して実際の値となっている。

っと言ったところでしょうね。

Tips: HelloPhoenix.LayoutView.titleっと修飾する必要がない。

2.7
さくさく、行きましょう!!
/web/templates/page/index.html.eexを開いて下さい。
以下部分に一行追加します。

追加行: <p><a href="<%= page_path @conn, :index %>">Link back to ourselves</a></p>

変更前
----
<div class="jumbotron">
  <h2>Welcome to Phoenix!</h2>
  <p class="lead">Most frameworks make you choose between speed and a productive environment. <a href="http://phoenixframework.org">Phoenix</a> and <a href="http://elixir-lang.org">Elixir</a> give you both.</p>
</div>
----

変更後
----
<div class="jumbotron">
  <h2>Welcome to Phoenix!</h2>
  <p class="lead">Most frameworks make you choose between speed and a productive environment. <a href="http://phoenixframework.org">Phoenix</a> and <a href="http://elixir-lang.org">Elixir</a> give you both.</p>
  <p><a href="<%= page_path @conn, :index %>">Link back to ourselves</a></p>
</div>
----

2.8
実行してページのHTMLソースを確認してみると・・・こんな出力に変換されています。
<p><a href="/">Link back to ourselves</a></p>

何をやりたかったのかって?
HelloPhoenix.Viewで修飾する必要がないことを示したかった、ただの例です。

つまり・・・ビューを使う時は、
修飾する必要がないってことで、認識してよさそうですね。

3.Viewsについてもっと

3.1
説明がさっぱり分からないので、
百聞は一見に如かずと言いますし、
とりあえず、実例っぽいのをやってみましょう。

/web/view/page_view.exを開き、
以下の関数を追加します。
----
def message do
  "Hello from the view!"
end
----

3.2
次に、/web/templates/pageディレクトリへ
以下の名称のファイルを作成します。
作成ファイル: test.html.eex

以下の内容を記述します。
----
This is the message: <%= message %>
----

3.3
コマンドプロンプトから以下のコマンドを実行します。
コマンド: iex -S mix

対話モードが立ち上がったら、以下の内容を実行する。
iex(1)> Phoenix.View.render(HelloPhoenix.PageView, "test.html", %{})

結果: {:safe, [["" | "This is the message: "] | "Hello from the view!"]}

render関数を使ってPageViewとtest.htmlをレンダリングしているように見えます。
結果の方は・・・test.htmlの内容とmessage関数の結果が出力されていますね。

3.4
/web/templates/page/test.html.eexに
以下の一行を追加して下さい。
----
I came from assigns: <%= @message %>
----

3.5
以下を順番に実行する。
iex(2)> r HelloPhoenix.PageView

結果:
web/views/page_view.ex:1: warning: redefining module HelloPhoenix.PageView
{:reloaded, HelloPhoenix.PageView, [HelloPhoenix.PageView]}

iex(3)> Phoenix.View.render(HelloPhoenix.PageView, "test.html", message: "Assigns has an @.")

結果:
{:safe,
 [[[["" | "I came from assigns: "] | "Assigns has an @."] |
   "\r\nThis is the message: "] | "Hello from the view!"]}

iex(4)> Phoenix.View.render(HelloPhoenix.PageView, "test.html", message: "<script>badThings();</script>")

結果:
{:safe,
 [[[["" | "I came from assigns: "] |
    "&lt;script&gt;badThings();&lt;/script&gt;"] |
   "\r\nThis is the message: "] | "Hello from the view!"]}

iex(5)> Phoenix.View.render_to_iodata(HelloPhoenix.PageView, "test.html", message: "Assigns has an @.")

結果:
[[[["" | "I came from assigns: "] | "Assigns has an @."] |
  "\r\nThis is the message: "] | "Hello from the view!"]

項目自体は、ビューとテンプレートが密接に協力しあっている部分を
説明しようとしているっぽいのだが、実行しても何だかよく分からなかったですね。

その内、分かったらということで先に進みます。

4.レイアウトについて簡単に

4.1
レイアウトについてちょっとやるみたいです。

レイアウトもテンプレートです。
他のテンプレートと同様にビューがあります。
(web/views/layout_view.ex)

テンプレートがレンダリングされると、
LayoutViewはテンプレートのレンダリングされた内容を@innerに割り当てる。

/web/templates/layout/application.html.eexを開いて下さい。
以下の一行を確認してみて下さい。
<%= @inner %>

ここにテンプレートからレンダリングされた文字列が置かれます。

5.ErrorViewについて

5.1
ErrorViewについてですね。

/web/views/error_view.exを開いて、
内容を確認してみましょう。

404(NotFound)と500(内部エラー)の記述がありますね。

5.2
実際に404を見てみたいと思います。
/hello_phoenix/config/dev.exsを開いて以下のように変更する。
(初めて/webの上を参照した気がする・・・)

変更前
----
config :hello_phoenix, HelloPhoenix.Endpoint,
  http: [port: 4000],
  debug_errors: true,
  code_reloader: true,
  cache_static_lookup: false,
  watchers: [node: ["node_modules/brunch/bin/brunch", "watch"]]
----

変更後
----
config :hello_phoenix, HelloPhoenix.Endpoint,
  http: [port: 4000],
  debug_errors: false,
  #code_reloader: true,
  catch_errors: true,
  cache_static_lookup: false,
  watchers: [node: ["node_modules/brunch/bin/brunch", "watch"]]
----

5.3
では、リロードを切っているので、起動中の方は一度終了して・・・
再度、実行します。
コマンド: mix phoenix.server

まずは、普通に"http://localhost:4000"へアクセスする。
次に、"http://localhost:4000/such/a/wrong/path"にアクセスする。
(適当にルーティング先がないアドレスでも問題ないです)

404.htmlに記述してあった文言が見れたと思います。

これですね。
----
def render("404.html", _assigns) do
  "Page not found - 404"
end
----

そもそもこのrender関数どこで呼ばれているの?ってことなのですが、
Phoenix.Endpoint.ErrorHandlerモジュールのレンダリング関数だそうです。

5.4
自分のエラーページを作成してみようとのことです。
と言うわけで、実施してみます。

/web/templatesディレクトリへ新規にディレクトリを作成する。
作成ディレクトリ: error

そのディレクトリに以下の名称のファイルを作成する。
作成ファイル: not_found.html.eex

5.5
以下の内容を記述する。

5.6
/web/views/error_view.exを開き、
404のrender関数を以下のように変更する。
----
def render("404.html", _assigns) do
  render("not_found.html", %{})
end
----

先ほど、iexで実行したrender関数が使われていますね。
微妙につながりました。まだ分かりませんが・・・
だから何って程度ですけど・・・

5.7
では、実行してアクセスしてみましょう。
(/config/dev.exsを元に戻してしまった方は再度設定を行う)

画面の表示が変わりましたね!

しかし、レイアウトを通してレンダリングされていないようですね。
先ほどのHTMLの記述を見ると、
レイアウトに記述されているような内容が記述されていますし・・・

説明を読んでみると・・・
推測の通り、レイアウトを介してnot_found.html.eexテンプレートは
レンダリングされていないようです。

理由としてはグローバルにエラーを処理しながら、
エッジケースの問題を実行するのは簡単だからとのこと。


重複を最小限にしたい場合、ヘッダー、フッターの共有テンプレートの実装が可能。
詳しくは、テンプレートのガイドを見てねっと・・・

以上!!

以下、参考にさせて頂いたサイト様

管理人の独り言~
昨日の記事の説明はひどかったですね・・・疲れて、頭が働かなかったです。
それでも、見てくれている人がいるであろうと思います。
なので、今日も記事をアップする。

かと言って、翻訳能力が付加されるわけでも強化されるわけでもないので、
相変わらずのクソ説明かと思いますが、呆れながらも見てくれると嬉しいです。

2015年6月2日

Phoenixのコントローラ(Controllers)について分かったこと(その2)

タイトル: Phoenixのコントローラ(Controllers)について分かったこと(その2)


目的: Phoenixアプリケーションのコントローラ(Controllers)について分かったことを記述する。


実施環境:

OS: Windows8.1
Erlang: Eshell V6.4
Elixir: v1.0.4
Phoenix Framework: v0.13.1
Node.js: v0.12.4

目次:


  1. 概要
  2. Assigning Layoutsの使い方
  3. Overriding Rendering Formatsの使い方
  4. Setting the Content Typeの使い方(未完成)
  5. Setting the HTTP Statusの使い方
  6. Redirectionの使い方

1.概要


1.1

注意!!
この記事は、Phoenix - Guides ControllersをGoogle翻訳を使い、分かったことを書いています。
原文を丸々翻訳しているわけではないので、
流れや説明がおかしいところ多々ありますが、その点注意して下さい。

英語が分かる方は、公式ガイドを見た方が確実です。

また、管理人の英語力は本人がドン引きするほど低いので、
間違った解釈及び理解している部分が多分にあると思います。

それでも構わないという方は見て頂けたら嬉しいです。

また、ご指摘等があれば受け付けておりますゆえ、遠慮なくお願いします。
特に間違えて習得している部分は教えて頂ければ、本当に助かります。

1.2

あらかじめ、テスト用のプロジェクトは作成しておいて下さい。
参考: Phoenixが欲しいって言うからNode.jsをインスコするよ!!

もしくは、その1を実施して下さい。
参考: Phoenixのコントローラ(Controllers)について分かったこと(その1)

※ その1は実施していなくても、その2の実施に問題はありません。

2.Assigning Layoutsの使い方


2.1

Phoenixが生成した、レイアウトのテンプレート。
全てのテンプレートがデフォルトでレンダリングされるレイアウト。
/web/templates/layout/application.html.eex

レイアウトはただのテンプレートなので、
レンダリングしてくれるビューを必要とする。

上記のテンプレートに対応しているのは、
/web/views/layout_view.exで定義されているLayoutViewモジュール。

2.2

レイアウトを切り替えるためには、
Phoenix.Controllerモジュールのput_layout機能を使用する。

第一引数は、レイアウトのベース名を文字列を取る。
第二引数は、booleanを取る。

例のソースコードだと、
第一引数を取ってなかったり、
trueとfalseで何が変わるのとか、
書いてないから引数よく分からない。

2.3

何れにせよ、実際に使ってみよう。

/web/controllers/page_controller.exを開き、
indexアクションを以下のように修正する。
----
def index(conn, params) do
  conn
  |> put_layout(false)
  |> render "index.html"
end
----

実行したけど、表示がいきなりしょぼくなったな・・・

なんか、重要!!とかって書かれてる部分がある。
put_layout使う時は、()で引数囲うこと・・・とか。
つまり、"put_layout false"と言った記述はダメらしい。

2.4

続き、行きましょう・・・

/web/templates/layoutディレクトリに以下の名称のファイルを作成する。
内容は、同ディレクトリのapplication.html.eexをコピーするので、
ファイル自体をコピーしてリネームでもいいですね。

作成ファイル: admin.html.eex

んで、作成したファイルから以下の行を削除する。
<span class="logo"></span>

2.5

/web/controllers/page_controller.exのindexアクションで
先ほど作成したファイルを指定してみる。
----
def index(conn, params) do
  conn
  |> put_layout("admin.html")
  |> render "index.html"
end
----

実行すると、ロゴなしの画面が出てきましたね。

3.Overriding Rendering Formatsの使い方


3.1

あらかじめ、書いておきます。
この項目はよく分からなかった。
一応実行できたので、何となくは分かるのですが、割かし曖昧です。
そのことを念頭に置いて読んで下さい。(飛ばしても結構です)

レンダリングしたい対象がテキストやJSON、HTMLである時の方法について書いてあるっぽい。

んで、この機能は・・・
フォーマットクエリ文字列パラメータを使用してその場でフォーマットを変更できる。
また、使用するには・・・
適切な名前のビューと適切なディレクトリに適切な名前のテンプレートが必要とのこと。

3.2

実際にやってみる。

/web/templates/pageディレクトリへ新規にファイルを作成する。
作成ファイル: index.text.eex

内容は以下の一文(普通にテキストファイルですね)
"OMG, this is actually some text."

3.3

/web/router.exを開き、
pipeline :browserを以下のように修正する。
----
pipeline :browser do
  plug :accepts, ["html", "text"]
  plug :fetch_session
  plug :fetch_flash
  plug :protect_from_forgery
end
----

ここで扱えるフォーマット?を追加している。
修正前: plug :accepts, ["html"]
修正後: plug :accepts, ["html", "text"]

テキストが増えてますね。

3.4

/web/controllers/page_controller.exを開き、
indexアクションを以下のように修正する。
----
def index(conn, _params) do
  render conn, :index
end
----

では実行して表示を確認する。
この段階では、画面内容は変わっていませんね。

"render conn, :index"にご注目。
"index.html"が:indexになっていますね。

これは、:indexで指定していると、自動でindex.html.eesがレンダリングされているみたいですね。
ここ、原文読んでもよく分からなかったのです。

3.5

次は、テンプレートにテキストファイルを指定します。
テキストのテンプレートでもデータを渡すことができることをやるみたいですね。

再び、indexアクションを以下のように修正して下さい。
----
def index(conn, params) do
  render conn, "index.text", message: params["message"]
end
----

テキストファイルを指定してますね。また、messageで値を渡してますね。

ところで、気付いた方いますでしょうかparamsに_がありません。
_なしでparamsの使用がないと警告が出ます。だから、使わない時は_を付けるみたいですね。
例)
web/controllers/page_controller.ex:6: warning: variable params is unused

3.6

次に、
/web/templates/page/index.text.eexを開き、以下のように修正する。

"OMG, this is actually some text." <%= @message %>

3.7

ここまで来たら、実行して下さい。
以下のアドレスにアクセスして下さい。
アドレス: http://localhost:4000/?format=text&message=CrazyTown

フォーマットの指定とmessageの値をURLの中に入れてます。

4.Setting the Content Typeの使い方(未完成)


4.1

HTTPを変更することにより、必要なフォーマットの任意の並べ替えをレンダリングできるらしい・・・
また、ヘッダーを受け入れ、適切なテンプレートを提供する必要があるっと・・・
よく分かりませんねw

物は試しと言うことで実行しましょう。
indexアクションでXMLをレンダリングしたい場合。

すいません、エラーが出て直せません・・・
その内分かったら記述します。

有効なコンテンツのMIMEタイプは別のドキュメントを参照すること
参考: https://github.com/elixir-lang/plug/blob/master/lib/plug/mime.types

思いました・・・Overriding Rendering Formatsでできないんでしょうか?
まぁ、いずれ検証するとして今は項目を消化してしまいましょう。

5.Setting the HTTP Statusの使い方


5.1

何か前に似たようなのやりましたね。send_respでしたっけ?
関数名が違うのでまた違うのでしょうが・・・

応答のHTTPステータスコードを設定する方法。
Plug.Connモジュールをインポートして、put_status関数を使用する。

参考: https://github.com/elixir-lang/plug/blob/master/lib/plug/conn/status.ex#L7-L63

5.2

使ってみよう。

/web/controllers/page_controller.exを開き、
indexアクションを以下のように修正する。
----
def index(conn, _params) do
  conn
  |> put_status(202)
  |> render "index.html"
end
----

実行して、HTTPステータスを確認する。

5.3

次、以下のように修正する。
----
def index(conn, _params) do
  conn
  |> put_status(:not_found)
  |> render "index.html"
end
----

実行して、HTTPステータスを確認する。
HTTPステータスは404not_foundなのに、ページはindex.htmlですね。

5.4

なので、エラーページを出すために以下のように修正する。
----
def index(conn, _params) do
  conn
  |> put_status(:not_found)
  |> render(HelloPhoenix.ErrorView, "404.html")
end
----

実行する。
Phoenixのロゴの下に"Page not found"と表示されましたね。

つまり、HTTPステータスは送れるけど、実際のレンダリングには関係ないよってことらしい。
レンダリングの切り替えは行う必要があるってことですね。

しかし、PageControllerの適切なビューはPageViewのはずなのに、
ErrorViewが出てくるってどうなっているんでしょうか?
疑問点ですね・・・

6.Redirectionの使い方


6.1

あえて、書きます。
リダイレクトのさせ方ですね。

リダイレクト関数があるみたいですね。
アプリケーション内のパスへのリダイレクトと
URLにリダイレクトするのは区別するようです。

6.2

/web/router.exを開き、新しくscope(ルート)を追加する。
----
scope "/", HelloPhoenix do
  get "/redirect_test", PageController, :redirect_test, as: :redirect_test
end
----

6.3

"mix phoenix.routes"コマンドを使い、ルーティングを見てみます。
以下が追加されていますね。

redirect_test_path  GET  /redirect_test     HelloPhoenix.PageController.redirect_test/2

6.4

/web/controllers/page_controller.exを開き、
indexアクションを以下のように修正する。
アプリケーション内のパスを指定してリダイレクトするやり方です。

def index(conn, _params) do
  redirect conn, to: "/redirect_test"
end

また、redirect_testアクションを新しく追加する。
def redirect_test(conn, _params) do
  text conn, "Redirect!"
end

実行します。"localhost:4000"にアクセスして下さい。
Redirect!と表示されますね。

まず、indexアクションが実行され、/redirect_testのパスへリダイレクトしてます。
その後、redirect_testアクションが実行され、textで出力しています。

開発者ツールを使ってHTTPステータスを確認してみると実際に動きが見れます。

6.5

今度は、indexアクションを以下のように修正して下さい。
URLを指定してリダイレクトさせるやり方です。
----
def index(conn, _params) do
  redirect conn, external: "http://elixir-lang.org/"
end
----

はい、実行。
Elixirのサイトへリダイレクトしましたね。

6.6

続いてPathHelpersを使ったやり方です。
----
defmodule HelloPhoenix.PageController do
  use HelloPhoenix.Web, :controller

  def index(conn, _params) do
    redirect conn, to: redirect_test_path(conn, :redirect_test)
  end
end
----

実行。
再び、Redirect!と表示されますね。

6.7

最後です。
URLをPathHelpersで指定する。
----
def index(conn, _params) do
  redirect conn, external: redirect_test_url(conn, :redirect_test)
end
----

ry
Redirect!と表示されます。

URLの指定は必ずしも外部でなくて構わないみたいです。
ちなみに、external:をto:に変更すると失敗します。

以上!!

以下、参考とさせて頂いたサイト様

管理人の独り言~
長々とその1からお疲れ様でした。
目を通して頂いた方はありがとうございます。

まだ、分かってないことはありますが・・・
まぁ、追々と言うことで・・・
また分かったら追記もしくは記事を書きますので・・・

Phoenixのコントローラ(Controllers)について分かったこと(その1)

タイトル: Phoenixのコントローラ(Controllers)について分かったこと(その1)


目的: Phoenixアプリケーションのコントローラ(Controllers)について分かったことを記述する。


実施環境:

OS: Windows8.1
Erlang: Eshell V6.4
Elixir: v1.0.4
Phoenix Framework: v0.13.1
Node.js: v0.12.4

目次:


  1. 概要
  2. Controllerの基本的なこと
  3. Actionの基本的なこと
  4. Flash Messagesの使い方
  5. Renderingに関してあれやこれや
  6. おまけ


1.概要


1.1

Phoenix - Guides Controllersの原文が長いよ!
内容的に二分割しないと長くなりすぎるので二回記事上げる。

注意!!
この記事は、Phoenix - Guides ControllersをGoogle翻訳を使い、分かったことを書いています。
原文を丸々翻訳しているわけではないので、
流れや説明がおかしいところ多々ありますが、その点注意して下さい。

英語が分かる方は、公式ガイドを見た方が確実です。

また、管理人の英語力は本人がドン引きするほど低いので、
間違った解釈及び理解している部分が多分にあると思います。

それでも構わないという方は見て頂けたら嬉しいです。

また、ご指摘等があれば受け付けておりますゆえ、遠慮なくお願いします。
特に間違えて習得している部分は教えて頂ければ、本当に助かります。

1.2

あらかじめ、テスト用のプロジェクトは作成しておいて下さい。

2.Controllerの基本的なこと


2.1

Phoenix Controllersは、中間モジュール。

動作順番
1: HTTP要求
2: ルータ
3: コントローラ
4: アクション
5: ビュー層呼び出しまたは、JSONレスポンスを返す

大概、Plugで何とかなるらしいこと書いてた。
でも、Plugの記事分からない。

2.2

コントローラの置き場所
場所: アプリケーション名/web/controllers/

アプリケーションを新規で作成すると以下が生成される。
デフォルトのコントローラ: アプリケーション名/web/controllers/page_controller.ex

デフォルトのPageControllerを例にプログラム内容を説明する。
以下の記述で、モジュールをインポートしている。
use HelloPhoenix.Web, :controller

Phoenix.Controller.Pipelineモジュールで定義されたプラグのマクロ。
plug :action
(plug :renderとかある・・・後ろの項目でやる)

indexアクションがデフォルトで記述されている。(router.exでルーティング先を定義している)
def index(conn, _params) do
  render conn, "index.html"
end

3.Actionの基本的なこと


3.1

アクション名は好きな名前を付けられる。
アクション名はrouter.exに定義されたルートと一致していること。

しかし、アクション名には可能な限り従うべき規則がある。
  • index: 特定のリソースタイプにおけるアイテムを全てレンダリングする
  • show: IDで個々の項目を描画する
  • new: 新しいアイテムを作成する時にフォームをレンダリングする
  • create: 新しい項目のため、paramsを受信してデータストアに保存する
  • edit: IDで検索し、個々の項目を編集するためのフォームを表示する
  • update: 編集項目のためのparamsを受信して、データストアに保存する
  • delete: 削除するアイテムのIDを受信し、データストアから削除する

3.2

実際にPageControllerのアクション名を変更してみる。

3.3

まず、web/router.exを開きscopeの以下の部分を修正する。

修正前: get "/", HelloPhoenix.PageController, :index
修正後: get "/", HelloPhoenix.PageController, :test

3.4

web/controllers/page_controller.exを開きアクション名をtestに修正する。
----
def test(conn, _params) do
  render conn, "index.html"
end
----

3.5

以下のコマンド実行する。
コマンド: mix phoenix.server

3.6

以下のアドレスへアクセスする。
アドレス: http://localhost:4000

修正前と動作が変わらないことを確認する。
(エラーが出なければそれでいい)

3.7

testに修正した部分を元のindexに直しておいて下さい。

4.Flash Messagesの使い方


4.1

アクション中にユーザと通信する必要がある。
例えば、モデルの更新中にエラーが発生したり・・・
アプリケーションでそれらを受け入れたい。
そのため、Flash Messangesがある。

4.2

何はともあれ、とりあえず使ってみる。

web/controllers/page_controller.exを開いてindexアクションを以下のように修正する。
----
def index(conn, _params) do
  conn
  |> put_flash(:info, "Welcome to Phoenix, from flash info!")
  |> put_flash(:error, "Let's pretend we have an error.")
  |> render "index.html"
end
----

"|>"はメソッドチェーンですね。
詳しくはElixirのドキュメント見ると参考になります。

4.3

web/templates/layout/application.html.eexを開いて以下の記述を確認する。
(既に記述がある)
----
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
----

4.4

以下のコマンド実行する。
コマンド: mix phoenix.server

リロードあるから起動しっぱなしでもいけるけど・・・

4.5

以下のアドレスへアクセスする。
アドレス: http://localhost:4000

ページ開いてエラーが出た方、3.7にある通り、testから直しましょう。
私は忘れていました。(router.exで)エラーが出た・・・

4.6

把握しているFlash Messagesを簡単に説明する。

put_flash: KeyとValueのペアとして設定
get_flash: KeyとValueのペアとして取得
persist: 現在の要求サイクルを超え、ValueをFlash Messagesとしてセッションに保存できる。
clear_flash: セッションに格納されているであろうFlash Messagesを削除する。

5.Renderingに関してあれやこれや


5.1

renderでレンダリングすることができる。
レイアウトにテンプレートがレンダリングされる。

それ以外にも、text、json、htmlで出力することができる。

5.2

実際にそれぞれの出力を試してみよう。
web/router.exを開きscopeに以下の記述を追加する。
追加: get "/:id", PageController, :show

5.3

web/controllers/page_controller.exを開き、
一つずつ修正しながら試して下さい。

5.4

text: プレーンテキストを出力
----
def show(conn, %{"id" => id}) do
  text conn, "Showing id #{id}"
end
----

5.5

json: jsonブロックを出力
----
def show(conn, %{"id" => id}) do
  json conn, %{id: id}
end
----

5.6

html: アクションに直接記述することで、テンプレートなしでレンダリングできる。
----
def show(conn, %{"id" => id}) do
  html conn, """
     <html>
       <head>
          <title>Passing an Id</title>
       </head>
       <body>
         <p>You sent in id #{id}</p>
       </body>
     </html>
    """
end
----

5.7

実行してそれぞれの出力結果、表示結果を確認する。
実行方法はここから以下省略する。

5.8

get "/:id", PageController, :showの記述とshowアクションは消しておいて下さい。

5.9

renderプラグのショートカットを使う。

web/controllers/page_controller.exを開き、
renderプラグの追加とindexアクションを以下のように修正する。
----
defmodule HelloPhoenix.PageController do
  use HelloPhoenix.Web, :controller

  plug :action
  plug :render

  def index(conn, _params) do
    conn
  end
end
----

renderを省略できる。

"index.html"の記述がないですが、
内部で自動的にアクション名からテンプレートを選択しているようです。
showアクションで"index.html"を指定していましたが、
renderを省略して記述すると"show.html"のテンプレートがねぇよと実行時エラーが出ます。

5.10

renderプラグを特定のアクションのみで適応して欲しいときの書き方。
一部のアクションではjsonを返したい等と言った時に使うとのこと・・・
例) plug :render when action in [:index, :show]

6.おまけ


6.1

Gathering Data(直訳: データ収集)
Phoenixには独自のデータアクセス層はない。
ただしElixirプロジェクトで、とても良いPostgresのソリューションを提供をしているとのこと。

他のデータアクセスオプションはあるみたい。
ETSとDETSは、OTPに内蔵されているKey値データを格納している。
OTPまたはQLCと呼ばれる独自のクエリ言語とmnesiaと呼ばれるRDBを提供している。

DBは重要であるが、ガイドでこれらのオプションをカバーしません・・・っと・・・まじか!?

(ここら辺よく分からない・・・故におまけ)

6.2

connとparams
conn: ホスト、パス要素、ポート、クエリ文字列などの要求に関する情報を保持する構造体。
(connはPlugに詳細が載っている・・・Plugやるしかないのね・・・)

params: HTTPリクエストのパラメータを保持してるMap

paramsの参考としては、
Phoenix - Guides Adding Pagesでメッセンジャーパラメータを渡した項目。
私は、正直あれで理解はできなかったけど・・・

一応、参考までに書いた記事も・・・

6.3

レンダリングするために、コントローラ以外のものについてまとめ。
PageControllerには、PageViewが必要。
PageViewには、index.html.eexテンプレートが含まれているpageディレクトリが必要。

6.4

@messageや@nameと言った形でテンプレートにて利用したい場合。

単一の渡し方
----
def index(conn, _params) do
  assign(conn, :message, "Welcome Back!")
end
----

複数の渡し方
----
def index(conn, _params) do
  conn
  |> assign(:message, "Welcome Back!")
  |> assign(:name, "Dweezil")
end
----

私の場合以下のようなタグを記述し出力させてみた。
web/templates/page/index.html.eexのテンプレートファイルへ以下のような行を追加した。
<p><%= @message %></p>
<p><%= @name %></p>

メモ1: 渡してないものを使おうとすると実行時エラーになる(空白では表示されない)
メモ2: render plugがないとレンダリングされない(画面真っ白)。

6.5

HTTPのステータスを通知する方法。
----
def index(conn, _params) do
  conn
  |> send_resp(201, "")
end
----

空白のページが表示される。
HTTP Headerを確認するとステータス201になっている。

コンテンツの種類を具体的にしたい時は、以下のようにする。(どうゆうこと?)
空白のページが表示される。
----
def index(conn, _params) do
  conn
  |> put_resp_content_type("text/plain")
  |> send_resp(201, "")
end
----

なんか、send_resp(201, "")を投げるとエラーが出るんだよな・・・なんでだろう・・・
解決した・・・plug :renderをコメントアウトしたら。

以上!!その2へ続く~

以下、参考とさせて頂いたサイト様

管理人の独り言~
長い、長いよ~。

実施手順が、
1: 英語に目を通す
2: Google翻訳
3: 翻訳確認
4: ソースコードあれば実行

っといったサイクルで実施しているから進行がひたすら遅い・・・
大分昔のパソコンで最新の技術動かそうとしてなかなか実行に辿り着かないくらい遅い。

と言うわけで、Controllersその1です。

2015年6月1日

Phoenixのルーティング(Router)について分かったこと

タイトル: Phoenixのルーティング(Router)について分かったこと

目的: Phoenixアプリケーションのルーティング(Router)について分かったことを記述する。

実施環境:
OS: Windows8.1
Erlang: Eshell V6.4
Elixir: v1.0.4
Phoenix Framework: v0.13.1
Node.js: v0.12.4

目次:

  1. 概要
  2. mix phoenix.routesコマンド
  3. Router.exへの記述例
  4. PathHelpersの使い方
  5. おまけ

1.概要

1.1
注意!!
この記事は、Phoenix - Guides Routingを素人翻訳して分かったことを書いています。
原文を丸々翻訳しているわけではないので、
流れや説明がおかしいところ多々ありますが、その点注意して下さい。
英語が分かる方は、公式を見た方が確実です。

また、管理人の英語力は本人がドン引きするほど低いので、
間違った解釈及び理解している部分が多分にあると思います。

それでも構わないという方は見て頂けたら嬉しいです。

また、ご指摘等があれば受け付けておりますゆえ、遠慮なくお願いします。
特に間違えて習得している部分は教えて頂ければ、本当に助かります。

1.2
ルーティング(Router)って何?
ネットワークにおけるルーティングではない。(全く関係ないわけではないが・・・)

RouterはPhoenixアプリケーションの主要なハブ。
HTTPの要求をアクション、チャネル、コントローラなどに一致させる。

1.3
あらかじめ、テスト用のプロジェクトは作成しておいて下さい。

管理人は、以下の内容で実施しております。
  • アプリケーション名: hello_phoenix
  • 設定は全てデフォルト
  • 作業は、hello_phoenixディレクトリ内で実施(コマンドプロンプトを含む)

2.mix phoenix.routesコマンド

2.1
コマンドプロンプトからhello_phoenixディレクトリ内で以下のコマンドを実行する。
コマンド: mix phoenix.routes

出力結果は・・・
page_path  GET  /                  HelloPhoenix.PageController.index/2

と出力される。
つまり・・・なんだってばよこれ?

2.2
ルーティング先を設定しているファイルとなると・・・
hello_phoenix/web/router.exを開いて見てみよう。

scope部分にルーティング先を設定している部分がある。
scope "/", HelloPhoenix do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
end

この部分である。
get "/", PageController, :index

2.3
2.1と2.2の内容からmix phoenix.routesコマンドは、
hello_phoenixアプリケーションのルーティング一覧を出力してくれていることが見て取れる。

本当かな?なら、ルーティング先を追加してみよう!

2.4
先ほどのscope部分に以下の記述を追加する。
追加: get "/users", UserController, :index

scope "/", HelloPhoenix do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  get "/users", UserController, :index
end

※実際にコントローラを作成していなくても問題なく動作する。

2.5
再び、先ほどのコマンドを実行しよう。
コマンド: mix phoenix.routes

出力を見てみると・・・
page_path  GET  /                  HelloPhoenix.PageController.index/2
user_path  GET  /users             HelloPhoenix.UserController.index/2

追加された。
ルーティング先をrouter.exに追加し、mix phoenix.routesコマンドすると、
router.exに記述したルーティング先の一覧が見れる。

何に使えるかって?
例えば、一行で複数のルーティング先になる記述があるとしたら?
そういった記述もルーティング先一覧に全て出力してくれます。

確認に便利ですね!

3.Router.exへの記述例

さて、幾つかの記述の仕方を紹介しようと思います。

3.1
2.5でも書いてある、一行で複数として認識される記述でも書きましょう。
router.exのscopeに追加で記述していきます。

先ほどのUserControllerのルーティングを以下のように修正して下さい。
修正: resources "/users", UserController

scope "/", HelloPhoenix do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  get "/users", UserController, :index
end

コマンドを実行しましょう。
コマンド: mix phoenix.routes

出力結果は・・・
page_path  GET  /                  HelloPhoenix.PageController.index/2
user_path  GET     /users             HelloPhoenix.UserController.index/2
user_path  GET     /users/:id/edit    HelloPhoenix.UserController.edit/2
user_path  GET     /users/new         HelloPhoenix.UserController.new/2
user_path  GET     /users/:id         HelloPhoenix.UserController.show/2
user_path  POST    /users             HelloPhoenix.UserController.create/2
user_path  PATCH   /users/:id         HelloPhoenix.UserController.update/2
             PUT     /users/:id         HelloPhoenix.UserController.update/2
user_path  DELETE  /users/:id         HelloPhoenix.UserController.delete/2

いきなり増えましたねw

あの一行だけでこれだけのルーティング先がマッチしてくれるんです。
すごいですよね・・・

ちなみに、/users/:idってどんなパスが来た時動作するのかってところなのですが・・・

例えば、
"http://localhost:4000/users/darui"
"http://localhost:4000/users/999"(Railsだと数値で管理するらしいので)
ってな具合のURL(パス)が来た時ですね。

Railsライクと言うことは数値が入っている方で意識しておいた方が良い気がしますね!

:idは、Phoenixアプリケーションにページを追加してみる。(その2)の:messengerの例を見て下さい。
ようは、ただのラベルですね。

でも、特定のルーティングだけ欲しい時やその逆の時はどうすればいいんでしょうか?

3.2
以下の記述を追加して下さい。
追加: resources "posts", PostController, only: [:index, :show]

コマンドを実行しましょう。
コマンド: mix phoenix.routes

二行増えましたね。
post_path  GET     /posts             HelloPhoenix.PostController.index/2
post_path  GET     /posts/:id         HelloPhoenix.PostController.show/2

only: [アクション名...]で特定のルーティングだけ選択できます。

3.3
以下の記述を追加して下さい。
追加: resources "comments", CommentController, except: [:delete]

コマンドを実行しましょう。
コマンド: mix phoenix.routes

七行増えましたね。
comment_path  GET     /comments           HelloPhoenix.CommentController.index/2
comment_path  GET     /comments/:id/edit  HelloPhoenix.CommentController.edit/2
comment_path  GET     /comments/new       HelloPhoenix.CommentController.new/2
comment_path  GET     /comments/:id       HelloPhoenix.CommentController.show/2
comment_path  POST    /comments           HelloPhoenix.CommentController.create/2
comment_path  PATCH   /comments/:id       HelloPhoenix.CommentController.update/2
                      PUT     /comments/:id       HelloPhoenix.CommentController.update/2

以下の記述をした時と何が違うのでしょう?
get "/users", UserController, :index

そうですね。deleteの行が消えています!!
つまり、特定のルーティングだけ除外できるということです。

4.PathHelpersの使い方

Path Helpersは、Router.Helpersモジュールに定義されている関数。
どんな、URLやパスが出るか試すことができます。

4.1
以下のコマンドを実行して対話モードを起動しましょう。
hello_phoenix>iex -S mix

4.2
以下を実行してみて下さい。
iex> HelloPhoenix.Router.Helpers.page_path(HelloPhoenix.Endpoint, :index)

出力結果・・・
"/"

ルートが返ってくるだけですね。

4.3
また、以下の内容を実行してみて下さい。

iex(1)> import HelloPhoenix.Router.Helpers
nil
iex(2)> alias HelloPhoenix.Endpoint
nil
iex(3)> user_path(Endpoint, :index)
"/users"
iex(4)> user_path(Endpoint, :show, 17)
"/users/17"
iex(5)> user_path(Endpoint, :create)
"/users"
iex(6)> user_path(Endpoint, :edit, 37)
"/users/37/edit"
iex(7)> user_path(Endpoint, :update, 37)
"/users/37"
iex(8)> user_path(Endpoint, :delete, 17)
"/users/17"
iex(9)> user_path(Endpoint, :show, 17, admin: true, active: false)
"/users/17?admin=true&active=false"
iex(10)> user_url(Endpoint, :index)
"http://localhost:4000/users"
iex(11)>

こんな感じで、URLやパスを出力させて試すことができます。

_url機能は、環境ごとに設定された構成パラメータから、完全なURLを構築するために
必要なホスト、ポート、プロキシポートおよびSSLの情報を取得できるもの。

4.4
PathHelpersのおかげでこんなことができます。(紹介だけ)
こんな感じで簡単に埋め込めるとのこと。

<a href="<%= page_path(@conn, :index) %>">To the Welcome Page!</a>

詳しくはViewのガイドを見てくれと・・・つまりこの記事を書いているときにはまだやっていない。
だから紹介だけしかできないw

5.おまけ

おまけなので適当・・・

5.1
特定のパスが含まれている時だけルーティングを変えたい?
としたらこんな感じにするのだ!!

つまりは、scope部分の分割。

以下の記述に対して・・・/adminのパスが含まれる時はルーティング先を変えたい。
resources "/reviews", ReviewController

こんな感じになる。
scope "/", HelloPhoenix do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  resources "/reviews", ReviewController
end

scope "/admin", as: :admin do
  resources "/reviews", HelloPhoenix.Admin.ReviewController
end

5.2
5.1に続く形の内容・・・
/adminに複数行の記述をしたい場合は以下のようになる。

scope "/admin", as: :admin do
  pipe_through :browser
  
  resources "/images", HelloPhoenix.Admin.ImageController
  resources "/reviews", HelloPhoenix.Admin.ReviewController
  resources "/users", HelloPhoenix.Admin.UserController
end

冗長性とミスをなくすため以下のように書き換えることもできる。
scope "/admin", HelloPhoenix.Admin, as: :admin do
  pipe_through :browser
  
  resources "/images",  ImageController
  resources "/reviews", ReviewController
  resources "/users",   UserController
end

5.3
scopeをネストさせたい?
ならば、こんな形で記述することができる。

scope "/api", HelloPhoenix.Api, as: :api do
  pipe_through :api
  
  scope "/v1", V1, as: :v1 do
    resources "/images",  ImageController
    resources "/reviews", ReviewController
    resources "/users",   UserController
  end
end

5.4
それぞれ、mix phoenix.routesで出力結果を見てみよう!!

5.5
Phoenixアプリケーションにページを追加してみる。(その2)で作った
HelloControllerのルーティング先、そのスコープを分割してみた。

これが、
----
scope "/", HelloPhoenix do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  get "/hello", HelloController, :index
  get "/hello/:messenger", HelloController, :show
end
----

こうなった。
----
scope "/", HelloPhoenix do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
end

scope "/hello", HelloPhoenix do
  pipe_through :browser

  get "", HelloController, :index
  get "/:messenger", HelloController, :show
end
----

Pipelines、Nested Resources、Scoped Routes、The Endpoint Plugs...etc
いっぱい分からないことがあるよ~
誰か日本語に翻訳されている方いないかな?(切実)
これについては、俺がやる!!って言えない・・・orz

英語ドキュメントを見ながらプログラムならいいんだよ。デバッグ/実行すれば何となく分かるし・・・
機能を英語で説明は・・・うん、俺には急に難易度が上がる。

追記(2015/06/06-16:23)
Channel Routesについて続きを書きました。
続!Phoenixのルーティング(Router)について分かったこと

以上!!

以下、参考にさせて頂いたサイト様

管理人の独り言~
この記事役に立つのか・・・?
間違ってる可能性を考えると、人様の目に付くところにアップするべきではないのかもしれない・・・
そんな思いがあります。

他にもまだ色々機能があったんですけど、、これだけしか分からなかったorz
本当に自分の亀の歩みのような成長速度には辟易します・・・
もっとドーンと成長できないですかね?www

Railsライクなので、Railsと似たような機能内容ですね。
記法や設定方法が異なるだけの気がします。
なので、Railsのルーティングは参考になります。
参考までに・・・
http://railsguides.jp/routing.html

人気の投稿