翻蚳è
ããïŒã ãšãªã¯ãµãŒãšãã§ããã¯ã¹ã¯ãææ°ã®ãŠã§ãéçºãã©ãã«é²ãã§ãããã®è¯ãäŸã§ãã ãã§ã«ãããã®ããŒã«ã¯ãWebã¢ããªã±ãŒã·ã§ã³ã®ãªã¢ã«ã¿ã€ã ãã¯ãããžãŒãžã®è³ªã®é«ãã¢ã¯ã»ã¹ãæäŸããŸãã 察話æ§ãåäžãããµã€ãããã«ããŠãŒã¶ãŒãã©ãŠã¶ãŒã²ãŒã ããã€ã¯ããµãŒãã¹ã¯ããããã®æè¡ãããŸãæ©èœããåéã§ãã 以äžã¯ãPhoenixãã¬ãŒã ã¯ãŒã¯ã§ã®éçºã®è©³çŽ°ãªåŽé¢ã説æããäžé£ã®11ã®èšäºã®ç¿»èš³ã§ããããã°ãšã³ãžã³ã®ãããªäºçŽ°ãªããšã®ããã«æããŸãã ããããæ¥ãã§ã€ãŸãããªãã§ãã ãããç¹ã«èšäºããšãªã¯ãµãŒã«æ³šæãæããã圌ã®ãã©ãã¯ãŒã«ãªãããã«ä¿ãå Žåãããã¯æ¬åœã«é¢çœãã§ãããã
ãã®ããŒãã§ã¯ãPhoenixã®ãã£ã³ãã«ã䜿çšããŠã³ã¡ã³ããçãäžããŸãã
çŸæç¹ã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã¯ä»¥äžã«åºã¥ããŠããŸãã
- ãšãªã¯ãµãŒ ïŒv1.3.1
- ãã§ããã¯ã¹ ïŒv1.2.0
- EctoïŒv2.0.2
ã©ãã§æ¢ããã®
ååã®ã³ã¡ã³ãã¯å®å
šã«çµãããŸããïŒ ãã¹ãŠã®æ©èœãæºåã§ããã®ã§ãElixirãšPhoenixãããã«æäŸããæ©äŒã掻çšããŠãããã°ãæ¬åœã«ã¯ãŒã«ã«ããŸãããã Phoenixã®ãã£ã³ãã«ã䜿çšããŠãã³ã¡ã³ãã·ã¹ãã ãã©ã€ãã³ã¡ã³ãã·ã¹ãã ã«å€ããŸãã æ£çŽãªãšããããã®éšåã«ã¯éåžžã«éãjavascriptããããŸãã
以åãšåãããã«è¡ããŸããããå®è£
ããåã«æ°ããé¢æ°ãèšèšããŸãã ã©ã€ã解説ã·ã¹ãã ã«ã¯æ¬¡ã®èŠä»¶ã課ãããŸãã
- 衚瀺ãããæ°ããã³ã¡ã³ãã¯ãæ¿èªããããŠãŒã¶ãŒã«ã®ã¿ãããŒããã£ã¹ãããå¿
èŠããããŸãã
- æ¿èªãããããã³ã¡ã³ãããã¹ãŠã®ãŠãŒã¶ãŒã«ãããŒããã£ã¹ãããå¿
èŠããããŸãã
- åé€ãããã³ã¡ã³ãã¯ããã¹ãŠã®ãŠãŒã¶ãŒã«å¯ŸããŠè¡šç€ºãããªããªããŸãã
ãã£ã³ãã«ãè¿œå
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ã«å¯ŸããŠåå¥ã®ã³ã¡ã³ããã£ãã«ãäœæããŸã ã
æãåçŽãªå®è£
ããå§ããŠãã»ãã¥ãªãã£ãè¿œå ããããã«æ»ã£ãŠãæåã«ãã¹ãŠã®äººã«èŠããæ¿èªããããã£ãã«ãçšæããŸãã ãŸãããããŒããã£ã¹ãããã€ãã³ãã決å®ããå¿
èŠããããŸãã
èš±å¯ãŠãŒã¶ãŒã®å ŽåïŒ
- ã³ã¡ã³ããäœæããŸãã
- ã³ã¡ã³ããåé€ããŸãã
- ã³ã¡ã³ããæ¿èªãããŸãã
çã®ããã«ïŒ
- ã³ã¡ã³ããæ¿èªãããŸãã
- ã³ã¡ã³ããåé€ããŸãã
ãŸããããã€ãã®åºæ¬çãªãã®ãèšå®ããå¿
èŠããããŸãã jQueryãã¢ããªã±ãŒã·ã§ã³ã«è¿œå ããŠãDOMãšã®å¯Ÿè©±ã容æã«ããŸãã
ãã©ã³ãã§jQueryãè¿œå ãã
NPMããjQueryãã€ã³ã¹ããŒã«ããããšããå§ããŸãããã
npm install
次ã«ã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
ãéããŠãããã€ãã®å°ããªå€æŽãè¡ããŸãã
ãœã±ããã®èª¬æãèŠãŠãã©ã®ã¡ãã»ãŒãžããªãã¹ã³/ãããŒããã£ã¹ãããå¿
èŠãããããç解ããŸãããã æ°ããäœæãããã³ã¡ã³ãã«ã¯ã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
ã§ããœã±ããæ¥ç¶ã³ãŒãã«ããã€ãã®å€æŽãå ããŸãã
ããã§ãæå¹ãªããŒã¯ã³ã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ã³ãŒããå¿
èŠã«ãªããŸãã ã¿ã¹ã¯ãããã«è©³ããèããŠã¿ãŸãããã
- DOMããpostIdãçŽæ¥ååŸãã
- æ°ããã³ã¡ã³ããã³ãã¬ãŒããçæããé¢æ°ãäœæãã
- ã³ã¡ã³ãã®äœæè
ãååŸããé¢æ°ãäœæãã
- ã³ã¡ã³ãããã¹ããååŸããé¢æ°ãäœæãã
- ã³ã¡ã³ãèå¥åãååŸããé¢æ°ãæžã
- ã³ã¡ã³ãããªã»ããããé¢æ°ãæžã
- ã³ã¡ã³ãäœæãåŠçããé¢æ°ãäœæãã
- ã³ã¡ã³ãã®æ¿èªãåŠçããé¢æ°ãäœæãã
- ã³ã¡ã³ãã®åé€ãåŠçããé¢æ°ãäœæãã
- ã³ã¡ã³ãäœæã€ãã³ããåŠçããé¢æ°ãäœæããŸã
- ã³ã¡ã³ãæ¿èªã€ãã³ããåŠçããé¢æ°ãäœæããŸã
- ã³ã¡ã³ãåé€ã€ãã³ããåŠçããé¢æ°ãäœæããŸã
ãããŠãç§ã¯ããªãã«ãããjavascriptã§ãã£ã±ãã«ãªãããšãèŠåããŸãã:)æéãç¡é§ã«ããã«ããããã®åæ©èœãããã«æžãå§ããŸãããã ãã®ã³ãŒãéšåã®ã³ã¡ã³ãã¯ãããããã®èŠä»¶ããããã説æããŠããŸãã
ãããã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ãè¿œå ããŠãããã°ãã©ãããã©ãŒã ã®èŠãç®ãæŽããŸãã
ã·ãªãŒãºã®ä»ã®èšäº
- ãšã³ããªãŒ
- ãã°ã€ã³
- 圹å²ãè¿œå
- ã³ã³ãããŒã©ãŒã§åœ¹å²ãåŠçããŸã
- ExMachinaãæ¥ç¶ããŸã
- ããŒã¯ããŠã³ã®ãµããŒã
- ã³ã¡ã³ããè¿œå
- ã³ã¡ã³ãã§çµäº
- ãã£ã³ãã«
- ãã£ãã«ãã¹ã
- ãããã«
翻蚳è
ããã®è¬èãªçµè«
ã¿ããªïŒ WunshãšåŒã°ãããããžã§ã¯ãã®ãŠã§ããµã€ãã§ããã·ã¢èªã®Elixirã«é¢ãã 3ããŒã¹ã®èšäºãåŸ
ã£ãŠããŸããããã§ãã¯ãŒã«ãªãã¥ãŒã¹ã¬ã¿ãŒã賌èªããŠãElixirã§æãèå³æ·±ããã¥ãŒã¹ãšç¬å èšäºãåãåãããšãã§ããŸãããŸããããã¥ã¡ã³ãã®ç¿»èš³ã®ä»äºã«åå ããŠãã ãããã質åãããå Žåã¯ãTelegramã®ãã£ãããšãã£ã³ãã«ã«æ¥ç¶ããŠãã ããã