PhoenixとElixirでブログエンジンを作成する/パート9.チャンネル





翻訳者から:「 エリクサーとフェニックスは、最新のウェブ開発がどこに進んでいるかの良い例です。 すでにこれらのツールは、Webアプリケーションのリアルタイムテクノロジーへの質の高いアクセスを提供します。 対話性が向上したサイト、マルチユーザーブラウザーゲーム、マイクロサービスは、これらの技術がうまく機能する分野です。 以下は、Phoenixフレームワークでの開発の詳細な側面を説明する一連の11の記事の翻訳です。ブログエンジンのような些細なことのように思えます。 しかし、急いでつまずかないでください。特に記事がエリクサーに注意を払うか、彼のフォロワーになるように促す場合、それは本当に面白いでしょう。







このパートでは、Phoenixのチャンネルを使用してコメントを盛り上げます。







現時点では、アプリケーションは以下に基づいています。









どこで止めたの



前回のコメントは完全に終わりました! すべての機能が準備できたので、ElixirとPhoenixがすぐに提供する機会を活用して、ブログを本当にクールにしましょう。 Phoenixのチャンネルを使用して、コメントシステムをライブコメントシステムに変えます。 正直なところ、この部分には非常に重いjavascriptがあります。







以前と同じように行きましょう。実装する前に新しい関数を設計します。 ライブ解説システムには次の要件が課されます。







  1. 表示される新しいコメントは、承認されたユーザーにのみブロードキャストする必要があります。
  2. 承認されたら、コメントをすべてのユーザーにブロードキャストする必要があります。
  3. 削除されたコメントは、すべてのユーザーに対して表示されなくなります。


チャンネルを追加



Phoenixでチャンネルを実装する最初のステップは、 web/channels/user_socket.ex



## Channels



下のコメント化された行を次のように変更します。







 channel "comments:*", Pxblog.CommentChannel
      
      





次に、チャネル自体を作成します。 これを行うには、Phoenixジェネレーターを使用します。







 $ mix phoenix.gen.channel Comment * creating web/channels/comment_channel.ex * creating test/channels/comment_channel_test.exs Add the channel to your `web/channels/user_socket.ex` handler, for example: channel "comment:lobby", Pxblog.CommentChannel
      
      





要件に基づいて、 各post_idに対して個別のコメントチャネルを作成します







最も単純な実装から始めて、セキュリティを追加するために戻って、最初にすべての人に見える承認されたチャネルを用意します。 また、ブロードキャストするイベントを決定する必要があります。







許可ユーザーの場合:







  1. コメントを作成しました
  2. コメントを削除しました
  3. コメントが承認されました


皆のために:







  1. コメントが承認されました
  2. コメントを削除しました


まず、いくつかの基本的なものを設定する必要があります。 jQueryをアプリケーションに追加して、DOMとの対話を容易にします。







ブランチでjQueryを追加する



NPMからjQueryをインストールすることから始めましょう。







 npm install --save-dev jquery
      
      





次に、Phoenixサーバーを再起動し、jQueryが正常にインストールされたことを確認します。 web / static / js / app.jsファイル開き 、次のコードを追加します。







 import $ from "jquery" if ($("body")) { console.log("jquery works!") }
      
      





「jquery works!」というメッセージが表示された場合は、ブラウザーの開発者コンソールでこれらの行を削除して、次の手順に進むことができます。







Javascriptチャネルの実装



まず、 web/static/js/app.js



に戻って、ソケットインポートステートメントのコメントを解除します。







次に、 web/static/js/socket.js



を開いて、いくつかの小さな変更を行います。







 // For right now, just hardcode this to whatever post id you're working with const postId = 2; const channel = socket.channel(`comments:${postId}`, {}); channel.join() .receive("ok", resp => { console.log("Joined successfully", resp) }) .receive("error", resp => { console.log("Unable to join", resp) });
      
      





ソケットの説明を見て、どのメッセージがリッスン/ブロードキャストする必要があるかを理解しましょう。 新しく作成されたコメントには「CREATED_COMMENT」、承認されたコメントには「APPROVED_COMMENT」、削除されたコメントには「DELETED_COMMENT」を使用します。 それらを定数としてsocket.js



ファイルに追加します。







 const CREATED_COMMENT = "CREATED_COMMENT" const APPROVED_COMMENT = "APPROVED_COMMENT" const DELETED_COMMENT = "DELETED_COMMENT"
      
      





次に、これらの各アクションのチャネルにイベントハンドラーを追加します。







 channel.on(CREATED_COMMENT, (payload) => { console.log("Created comment", payload) }); channel.on(APPROVED_COMMENT, (payload) => { console.log("Approved comment", payload) }); channel.on(DELETED_COMMENT, (payload) => { console.log("Deleted comment", payload) });
      
      





最後に、送信ボタンを変更して、コメントを送信する代わりに「偽の」イベントを作成します。







 $("input[type=submit]").on("click", (event) => { event.preventDefault() channel.push(CREATED_COMMENT, { author: "test", body: "body" }) })
      
      





チャネルサポートのコードの改良



ブラウザでテストしようとすると、アプリケーションがクラッシュします。 次のようなエラーメッセージが表示されます。







 [error] GenServer #PID<0.1250.0> terminating ** (FunctionClauseError) no function clause matching in Pxblog.CommentChannel.handle_in/3 (pxblog) web/channels/comment_channel.ex:14: Pxblog.CommentChannel.handle_in(“CREATED_COMMENT”, %{“author” => “test”, “body” => “body”}, %Phoenix.Socket{assigns: %{}, channel: Pxblog.CommentChannel, channel_pid: #PID<0.1250.0>, endpoint: Pxblog.Endpoint, handler: Pxblog.UserSocket, id: nil, joined: true, pubsub_server: Pxblog.PubSub, ref: “2”, serializer: Phoenix.Transports.WebSocketSerializer, topic: “comments:2”, transport: Phoenix.Transports.WebSocket, transport_name: :websocket, transport_pid: #PID<0.1247.0>}) (phoenix) lib/phoenix/channel/server.ex:229: Phoenix.Channel.Server.handle_info/2 (stdlib) gen_server.erl:615: :gen_server.try_dispatch/4 (stdlib) gen_server.erl:681: :gen_server.handle_msg/5 (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3 Last message: %Phoenix.Socket.Message{event: “CREATED_COMMENT”, payload: %{“author” => “test”, “body” => “body”}, ref: “2”, topic: “comments:2”} State: %Phoenix.Socket{assigns: %{}, channel: Pxblog.CommentChannel, channel_pid: #PID<0.1250.0>, endpoint: Pxblog.Endpoint, handler: Pxblog.UserSocket, id: nil, joined: true, pubsub_server: Pxblog.PubSub, ref: nil, serializer: Phoenix.Transports.WebSocketSerializer, topic: “comments:2”, transport: Phoenix.Transports.WebSocket, transport_name: :websocket, transport_pid: #PID<0.1247.0>}
      
      





現在、チャンネル内のメッセージを処理する機能はありません。 web/channels/comment_channel.ex



ファイルを開き、無言で見るのではなく、 handle_in



関数がサブスクライバーにメッセージをブロードキャストするようにします。 また、上部の標準結合関数を変更する必要があります。







 def join("comments:" <> _comment_id, payload, socket) do if authorized?(payload) do {:ok, socket} else {:error, %{reason: "unauthorized"}} end end # ... # It is also common to receive messages from the client and # broadcast to everyone in the current topic (comments:lobby). def handle_in("CREATED_COMMENT", payload, socket) do broadcast socket, "CREATED_COMMENT", payload {:noreply, socket} end
      
      





これで、「リッスン」する予定の他の2つのメッセージに同様のコードを追加できます。







 def handle_in("APPROVED_COMMENT", payload, socket) do broadcast socket, "APPROVED_COMMENT", payload {:noreply, socket} end def handle_in("DELETED_COMMENT", payload, socket) do broadcast socket, "DELETED_COMMENT", payload {:noreply, socket} end
      
      





また、テンプレートにいくつかの変更を加える必要があります。 あなたは私たちがどのポストで働いているか、そして現在のユーザーが誰であるかを知る必要があります。 したがって、 web/templates/post/show.html.eex



ファイルの先頭に次のコードを追加します。







 <input type="hidden" id="post-id" value="<%= @post.id %>">
      
      





次に、 web/templates/comment/comment.html.eex



を開き、開始div



を変更しweb/templates/comment/comment.html.eex









 <div id="comment-<%= @comment.id %>" class="comment" data-comment-id="<%= @comment.id %>">
      
      





コメントに関連するすべてがJavascriptを介して処理されるようになったので、承認/拒否ボタン用に以前に作成したコードを削除する必要があります。 ブロック全体を次のように変更します。







 <%= if @conn.assigns[:author_or_admin] do %> <%= unless @comment.approved do %> <button class="btn btn-xs btn-primary approve">Approve</button> <% end %> <button class="btn btn-xs btn-danger delete">Delete</button> <% end %>
      
      





また、作成者とコメントのテキストが表示されるdiv



タグ内で、 strong



タグを変更して、それぞれ.comment-author



クラスと.comment-body



クラスを持つようにします。







 <div class="col-xs-4"> <strong class="comment-author"><%= @comment.author %></strong> </div>
      
      





...







 <div class="col-xs-12 comment-body"> <%= @comment.body %> </div>
      
      





最後に、著者とコメントテキストに適切にアクセスできることを確認する必要があるため、 web/templates/comment/form.html.eex



web/templates/comment/form.html.eex



、コメント入力フィールドと送信ボタンが次のようになっていることを確認します。







 <div class="form-group"> <%= label f, :body, class: "control-label" %> <%= textarea f, :body, class: "form-control" %> <%= error_tag f, :body %> </div> <div class="form-group"> <%= submit "Submit", class: "btn btn-primary create-comment" %> </div>
      
      





ここで、各ブロードキャスト機能を適切に実装する必要があるため、「javascriptフィールド」に戻って続行します。







Phoenix.Tokenを介してユーザーIDを実装する



ユーザーが本人であることを確認する方法、およびコメントデータを変更するアクセス権があるかどうかを確認する方法が必要です。 これを行うには、Phoenixに組み込まれているPhoenix.Tokenモジュールを使用します。







アプリケーションテンプレートにユーザートークンを配置することから始めましょう。 おそらくどこにでも表示したいので、これは非常に便利です。 web/templates/layout/app.html.eex



、残りのメタタグに次を追加します。







 <%= if user = current_user(@conn) do %> <%= tag :meta, name: "channel_token", content: Phoenix.Token.sign(@conn, "user", user.id) %> <% end %>
      
      





ここで、ユーザーIDを指す署名付きトークンが必要であると言います(もちろん、ユーザーがログインしている場合)。 これにより、非表示の入力フィールドを信頼したり、その他の奇妙な方法を使用したりすることなく、Javascriptを介してユーザーのuser_idを確認するための優れた方法が提供されます。







次に、 web/static/js/socket.js



で、ソケット接続コードにいくつかの変更を加えます。







 // Grab the user's token from the meta tag const userToken = $("meta[name='channel_token']").attr("content") // And make sure we're connecting with the user's token to persist the user id to the session const socket = new Socket("/socket", {params: {token: userToken}}) // And then connect to our socket socket.connect()
      
      





ここで、有効なトークンをPhoenixコードに渡します。 今回はweb/channels/user_socket.ex



が必要です。このweb/channels/user_socket.ex



では、 connect



関数を変更してユーザートークンを確認します。







 def connect(%{"token" => token}, socket) do case Phoenix.Token.verify(socket, "user", token, max_age: 1209600) do {:ok, user_id} -> {:ok, assign(socket, :user, user_id)} {:error, reason} -> {:ok, socket} end end
      
      





そのため、Phoenix.Tokenモジュールからverify関数を呼び出し、それにソケット、チェックする値、トークン自体、およびmax_age値(トークンの最大有効期間(2週間など))を渡します。







検証が成功した場合、タプル{:ok、[トークンから取得した値]}を送り返します。この場合はuser_idです。 次に、ソケットに保存されたuser_id値との接続をサポートします(セッションまたはconnに値を保存するのと同様)。







接続を確認できない場合、これも正常です。 権限のないユーザーが検証済みのuser_idなしで更新を受信できるようにしたいので、何も割り当てず、単に{:ok, socket}



返し{:ok, socket}









socket.jsに戻る



念頭に置いてすべてをサポートするには、大量のJavascriptコードが必要になります。 タスクをさらに詳しく考えてみましょう。







  1. DOMからpostIdを直接取得する
  2. 新しいコメントテンプレートを生成する関数を作成する
  3. コメントの作成者を取得する関数を作成する
  4. コメントテキストを取得する関数を作成する
  5. コメント識別子を取得する関数を書く
  6. コメントをリセットする関数を書く
  7. コメント作成を処理する関数を作成する
  8. コメントの承認を処理する関数を作成する
  9. コメントの削除を処理する関数を作成する
  10. コメント作成イベントを処理する関数を作成します
  11. コメント承認イベントを処理する関数を作成します
  12. コメント削除イベントを処理する関数を作成します


そして、私はあなたにそれがjavascriptでいっぱいになることを警告しました:)時間を無駄にせずに、これらの各機能をすぐに書き始めましょう。 このコード部分のコメントは、それぞれの要件をそれぞれ説明しています。







 // Import the socket library import {Socket} from "phoenix" // And import jquery for DOM manipulation import $ from "jquery" // Grab the user's token from the meta tag const userToken = $("meta[name='channel_token']").attr("content") // And make sure we're connecting with the user's token to persist the user id to the session const socket = new Socket("/socket", {params: {token: userToken}}) // And connect out socket.connect() // Our actions to listen for const CREATED_COMMENT = "CREATED_COMMENT" const APPROVED_COMMENT = "APPROVED_COMMENT" const DELETED_COMMENT = "DELETED_COMMENT" // REQ 1: Grab the current post's id from a hidden input on the page const postId = $("#post-id").val() const channel = socket.channel(`comments:${postId}`, {}) channel.join() .receive("ok", resp => { console.log("Joined successfully", resp) }) .receive("error", resp => { console.log("Unable to join", resp) }) // REQ 2: Based on a payload, return to us an HTML template for a comment // Consider this a poor version of JSX const createComment = (payload) => ` <div id="comment-${payload.commentId}" class="comment" data-comment-id="${payload.commentId}"> <div class="row"> <div class="col-xs-4"> <strong class="comment-author">${payload.author}</strong> </div> <div class="col-xs-4"> <em>${payload.insertedAt}</em> </div> <div class="col-xs-4 text-right"> ${ userToken ? '<button class="btn btn-xs btn-primary approve">Approve</button> <button class="btn btn-xs btn-danger delete">Delete</button>' : '' } </div> </div> <div class="row"> <div class="col-xs-12 comment-body"> ${payload.body} </div> </div> </div> ` // REQ 3: Provide the comment's author from the form const getCommentAuthor = () => $("#comment_author").val() // REQ 4: Provide the comment's body from the form const getCommentBody = () => $("#comment_body").val() // REQ 5: Based on something being clicked, find the parent comment id const getTargetCommentId = (target) => $(target).parents(".comment").data("comment-id") // REQ 6: Reset the input fields to blank const resetFields = () => { $("#comment_author").val("") $("#comment_body").val("") } // REQ 7: Push the CREATED_COMMENT event to the socket with the appropriate author/body $(".create-comment").on("click", (event) => { event.preventDefault() channel.push(CREATED_COMMENT, { author: getCommentAuthor(), body: getCommentBody(), postId }) resetFields() }) // REQ 8: Push the APPROVED_COMMENT event to the socket with the appropriate author/body/comment id $(".comments").on("click", ".approve", (event) => { event.preventDefault() const commentId = getTargetCommentId(event.currentTarget) // Pull the approved comment author const author = $(`#comment-${commentId} .comment-author`).text().trim() // Pull the approved comment body const body = $(`#comment-${commentId} .comment-body`).text().trim() channel.push(APPROVED_COMMENT, { author, body, commentId, postId }) }) // REQ 9: Push the DELETED_COMMENT event to the socket but only pass the comment id (that's all we need) $(".comments").on("click", ".delete", (event) => { event.preventDefault() const commentId = getTargetCommentId(event.currentTarget) channel.push(DELETED_COMMENT, { commentId, postId }) }) // REQ 10: Handle receiving the CREATED_COMMENT event channel.on(CREATED_COMMENT, (payload) => { // Don't append the comment if it hasn't been approved if (!userToken && !payload.approved) { return; } // Add it to the DOM using our handy template function $(".comments h2").after( createComment(payload) ) }) // REQ 11: Handle receiving the APPROVED_COMMENT event channel.on(APPROVED_COMMENT, (payload) => { // If we don't already have the right comment, then add it to the DOM if ($(`#comment-${payload.commentId}`).length === 0) { $(".comments h2").after( createComment(payload) ) } // And then remove the "Approve" button since we know it has been approved $(`#comment-${payload.commentId} .approve`).remove() }) // REQ 12: Handle receiving the DELETED_COMMENT event channel.on(DELETED_COMMENT, (payload) => { // Just delete the comment from the DOM $(`#comment-${payload.commentId}`).remove() }) export default socket
      
      





おそらくJavaScriptで十分です。 これで機能するようになりましたが、保護はありません。 コメントを追加、承認、削除するヘルパー関数を作成して、Elixirコードに追加しましょう。







チャンネルの補助機能



Javascriptコードはすべて問題ないので、おそらくバックエンド側に補助関数を追加する必要があります。 コメントを作成/承認/削除するためのデータベースとのやり取りの主力になる新しいモジュールを作成することから始めましょう。 したがって、 web / channels / comment_helper.exファイルを作成します。







 defmodule Pxblog.CommentHelper do alias Pxblog.Comment alias Pxblog.Post alias Pxblog.User alias Pxblog.Repo import Ecto, only: [build_assoc: 2] def create(%{"postId" => post_id, "body" => body, "author" => author}, _socket) do post = get_post(post_id) changeset = post |> build_assoc(:comments) |> Comment.changeset(%{body: body, author: author}) Repo.insert(changeset) end def approve(%{"postId" => post_id, "commentId" => comment_id}, %{assigns: %{user: user_id}}) do authorize_and_perform(post_id, user_id, fn -> comment = Repo.get!(Comment, comment_id) changeset = Comment.changeset(comment, %{approved: true}) Repo.update(changeset) end) end def delete(%{"postId" => post_id, "commentId" => comment_id}, %{assigns: %{user: user_id}}) do authorize_and_perform(post_id, user_id, fn -> comment = Repo.get!(Comment, comment_id) Repo.delete(comment) end) end defp authorize_and_perform(post_id, user_id, action) do post = get_post(post_id) user = get_user(user_id) if is_authorized_user?(user, post) do action.() else {:error, "User is not authorized"} end end defp get_user(user_id) do Repo.get!(User, user_id) end defp get_post(post_id) do Repo.get!(Post, post_id) |> Repo.preload([:user, :comments]) end defp is_authorized_user?(user, post) do (user && (user.id == post.user_id || Pxblog.RoleChecker.is_admin?(user))) end end
      
      





上から始めましょう。 しばしばComment / Post / User / Repoモジュールを参照するので、コードを簡潔にするために、それらのエイリアスを追加するのが正しいでしょう。 build_assoc



からbuild_assoc



関数をインポートする必要もありますが、アリティバージョン2のみです。







次に、すぐに投稿の作成に進みます。 私たちは習慣からソケット関数に渡しますが、必ずしもそれを必要とするわけではありません。 たとえば、この場合、誰でもコメントを追加できます。 サンプル内の引数のpost_id、body、およびauthorの値をマップして、関数内でそれらを操作できるようにします。







 def create(%{"postId" => post_id, "body" => body, "author" => author}, _socket) do post = get_post(post_id) changeset = post |> build_assoc(:comments) |> Comment.changeset(%{body: body, author: author}) Repo.insert(changeset) end
      
      





get_post



関数を使用して投稿を取得しますが、まだ作成していません。 これは、すぐ下のプライベート機能です。 次に、投稿からチェンジを作成して、関連するコメントを作成します。 最後に、Repo.insert関数の結果を返します。 これは絶対にシンプルで標準的なEctoコードなので、ここで驚くことはありません。 他のすべての機能についても同じことが言えます。 次に、 approve



機能を見てみましょう。







 def approve(%{"postId" => post_id, "commentId" => comment_id}, %{assigns: %{user: user_id}}) do authorize_and_perform(post_id, user_id, fn -> comment = Repo.get!(Comment, comment_id) changeset = Comment.changeset(comment, %{approved: true}) Repo.update(changeset) end) end
      
      





ここでも、必要な値がサンプルと比較されます-最初の引数のpost_idとcomment_id、および2番目に渡されたソケットの検証されたuser_id。 次に、補助関数authorize_and_perform



を呼び出し、それに匿名関数を渡します。匿名関数はコメントを受け取り、チェンジャーを介して承認済みフラグをtrueに更新してから、更新をRepoに送信します。 かなり標準的なコードですが、このauthorize_and_perform



関数は不可解に見えるので、そこから脱線しましょう。







 defp authorize_and_perform(post_id, user_id, action) do post = get_post(post_id) user = get_user(user_id) if is_authorized_user?(user, post) do action.() else {:error, "User is not authorized"} end end
      
      





Post_idとuser_idが渡されます。これらの値は両方とも、コメント付きのアクションを適切に許可するために必要なためです。 次に、別のヘルパー関数is_authorized_user?



is_authorized_user?



、ユーザーと投稿を受信すると、trueまたはfalseを返します。 すべてが正常であれば、匿名関数アクションが呼び出されます。 タイトルと括弧の間のドットに注意してください。 それ以外の場合、タプル{:error、 "User is not authorized"}が返されます。美しいエラーメッセージを表示する場合は、さらにインターセプトできます。







関数内で承認が実行され、 fn -> ... end



ブロックを使用して渡されたアクションが実行されます。 これは、多くのロジックが複製される場合の良い例です。







authorize_and_perform



関数で十分です。 delete



機能に移りましょう:







 def delete(%{"postId" => post_id, "commentId" => comment_id}, %{assigns: %{user: user_id}}) do authorize_and_perform(post_id, user_id, fn -> comment = Repo.get!(Comment, comment_id) Repo.delete(comment) end) end
      
      





同じアプローチがあります。 サンプルとの比較を通じて必要な値を取得し、アクションを承認してから、コメントを取得して削除します。 すべてがシンプルです!







最後に、小さなヘルパー関数を見てください。







 defp get_user(user_id) do Repo.get!(User, user_id) end defp get_post(post_id) do Repo.get!(Post, post_id) |> Repo.preload([:user, :comments]) end defp is_authorized_user?(user, post) do (user && (user.id == post.user_id || Pxblog.RoleChecker.is_admin?(user))) end
      
      





ユーザーの受信、投稿の受信、ユーザーの承認の確認( PostController



からPostController



コード)。 ヘルパー関数は終了しました。 CommentChannel



追加します。







コメントチャネルにヘルパー関数を埋め込む



必要なのは、元のコードを補助機能を備えたCREATED / APPROVED / DELETEDメッセージに置き換えることだけです。 web/channels/comment_channel.ex



ファイルを開きます。







 alias Pxblog.CommentHelper # It is also common to receive messages from the client and # broadcast to everyone in the current topic (comments:lobby). def handle_in("CREATED_COMMENT", payload, socket) do case CommentHelper.create(payload, socket) do {:ok, comment} -> broadcast socket, "CREATED_COMMENT", Map.merge(payload, %{insertedAt: comment.inserted_at, commentId: comment.id, approved: comment.approved}) {:noreply, socket} {:error, _} -> {:noreply, socket} end end def handle_in("APPROVED_COMMENT", payload, socket) do case CommentHelper.approve(payload, socket) do {:ok, comment} -> broadcast socket, "APPROVED_COMMENT", Map.merge(payload, %{insertedAt: comment.inserted_at, commentId: comment.id}) {:noreply, socket} {:error, _} -> {:noreply, socket} end end def handle_in("DELETED_COMMENT", payload, socket) do case CommentHelper.delete(payload, socket) do {:ok, _} -> broadcast socket, "DELETED_COMMENT", payload {:noreply, socket} {:error, _} -> {:noreply, socket} end end
      
      





原則は3つの呼び出しすべてで類似しているcreate



のみをcreate









 # It is also common to receive messages from the client and # broadcast to everyone in the current topic (comments:lobby). def handle_in("CREATED_COMMENT", payload, socket) do case CommentHelper.create(payload, socket) do {:ok, comment} -> broadcast socket, "CREATED_COMMENT", Map.merge(payload, %{insertedAt: comment.inserted_at, commentId: comment.id, approved: comment.approved}) {:noreply, socket} {:error, _} -> {:noreply, socket} end end
      
      





関数のシグネチャは変更されていないため、そのままにしておきます。 最初に行うことは、 CommentHelper.create



関数のcaseステートメントを追加し、 CommentHelper.create



ペイロードとソケットを渡すことです(実行したパターンマッチングを思い出してください)。 :ok



が作成されたコメントに付属している場合、JavascriptにはないデータベースからのデータとともにCREATED_COMMENT



メッセージをソケットに変換します。 エラーが発生した場合は、何もブロードキャストする必要はなく、ソケットを返して気楽に移動するだけです。













おわりに



ブログには、驚くべきリアルタイムのコメントシステムが追加されました。 また、Phoenixでのチャネルとトークンの調査にも深く取り組み、それらを組み合わせてセキュリティレベルを向上させる方法を学びました。 この点で、この例はもちろん完全ではありません。すべてのコメントをブロードキャストしますが、すべてをdiv



追加するわけではありません。 誰かが追加されたすべてのコメントを、承認されていなくても見ることができるように。 さらに、これは、認証されたチャネルと認証されていないチャネルを別々に作成し、必要なチャネルのみにメッセージをブロードキャストすることで改善できます。 コメントのフィルタリングはスパムの回避にのみ役立つと考えると、この場合のセキュリティの向上はそれほど重要ではありません。 しかし、このテクニックは将来役に立つかもしれません。 また、テストについても忘れていました。 これを行わないようにすることをお勧めしますが、この部分は非常に長くなることが判明したので、次のテストを終了しましょう。 これにより、コードカバレッジが高くなり、不要なコードが削除されます。







デザインはまだ悪いので、あなたはそれに対処する必要があります。 これを行うには、Zurb Foundation 6を追加して、ブログプラットフォームの見た目を整えます。







シリーズの他の記事



  1. エントリー
  2. ログイン
  3. 役割を追加
  4. コントローラーで役割を処理します
  5. ExMachinaを接続します
  6. マークダウンのサポート
  7. コメントを追加
  8. コメントで終了
  9. チャンネル
  10. チャネルテスト
  11. おわりに


翻訳者からの謙虚な結論



みんな! Wunshと呼ばれるプロジェクトのウェブサイトで、ロシア語のElixirに関する 3ダースの記事が待っています。そこで、クールなニュースレターを購読して、Elixirで最も興味深いニュースと独占記事を受け取ることができます。また、ドキュメントの翻訳の仕事に参加してくださいご質問がある場合は、Telegramのチャットチャンネル接続してください








All Articles